root/usr/src/uts/common/io/comstar/stmf/lun_map.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
 */

#include <sys/conf.h>
#include <sys/file.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/modctl.h>
#include <sys/scsi/scsi.h>
#include <sys/scsi/impl/scsi_reset_notify.h>
#include <sys/disp.h>
#include <sys/byteorder.h>
#include <sys/atomic.h>

#include <sys/stmf.h>
#include <sys/lpif.h>
#include <sys/portif.h>
#include <sys/stmf_ioctl.h>

#include "stmf_impl.h"
#include "lun_map.h"
#include "stmf_state.h"

void stmf_update_sessions_per_ve(stmf_view_entry_t *ve,
                stmf_lu_t *lu, int action);
void stmf_add_lus_to_session_per_vemap(stmf_i_local_port_t *ilport,
                stmf_i_scsi_session_t *iss, stmf_lun_map_t *vemap);
stmf_id_data_t *stmf_lookup_group_for_host(uint8_t *ident, uint16_t ident_size);
static stmf_status_t stmf_add_ent_to_map(stmf_lun_map_t *sm, void *ent,
    uint8_t *lun);
static stmf_status_t stmf_remove_ent_from_map(stmf_lun_map_t *sm, uint8_t *lun);
uint16_t stmf_get_next_free_lun(stmf_lun_map_t *sm, uint8_t *lun);
stmf_status_t stmf_add_tg(uint8_t *tg_name, uint16_t tg_name_size,
                int allow_special, uint32_t *err_detail);
stmf_status_t stmf_add_hg(uint8_t *hg_name, uint16_t hg_name_size,
                int allow_special, uint32_t *err_detail);
stmf_i_local_port_t *stmf_targetident_to_ilport(uint8_t *target_ident,
                uint16_t ident_size);
stmf_i_scsi_session_t *stmf_lookup_session_for_hostident(
                stmf_i_local_port_t *ilport, uint8_t *host_ident,
                uint16_t ident_size);
stmf_i_lu_t *stmf_luident_to_ilu(uint8_t *lu_ident);
stmf_lun_map_t *stmf_get_ve_map_per_ids(stmf_id_data_t *tgid,
                stmf_id_data_t *hgid);
stmf_lun_map_t *stmf_duplicate_ve_map(stmf_lun_map_t *src);
int stmf_merge_ve_map(stmf_lun_map_t *src, stmf_lun_map_t *dst,
                stmf_lun_map_t **pp_ret_map, stmf_merge_flags_t mf);
void stmf_destroy_ve_map(stmf_lun_map_t *dst);
void stmf_free_id(stmf_id_data_t *id);


/*
 * Init the view
 */
void
stmf_view_init()
{
        uint8_t grpname_forall = '*';
        (void) stmf_add_hg(&grpname_forall, 1, 1, NULL);
        (void) stmf_add_tg(&grpname_forall, 1, 1, NULL);
}

/*
 * Clear config database here
 */
void
stmf_view_clear_config()
{
        stmf_id_data_t *idgrp, *idgrp_next, *idmemb, *idmemb_next;
        stmf_ver_tg_t *vtg, *vtg_next;
        stmf_ver_hg_t *vhg, *vhg_next;
        stmf_view_entry_t *ve, *ve_next;
        stmf_i_lu_t     *ilu;
        stmf_id_list_t  *idlist;
        stmf_i_local_port_t *ilport;

        for (vtg = stmf_state.stmf_ver_tg_head; vtg; vtg = vtg_next) {
                for (vhg = vtg->vert_verh_list; vhg; vhg = vhg_next) {
                        if (vhg->verh_ve_map.lm_nentries) {
                                kmem_free(vhg->verh_ve_map.lm_plus,
                                    vhg->verh_ve_map.lm_nentries *
                                    sizeof (void *));
                        }
                        vhg_next = vhg->verh_next;
                        kmem_free(vhg, sizeof (stmf_ver_hg_t));
                }
                vtg_next = vtg->vert_next;
                kmem_free(vtg, sizeof (stmf_ver_tg_t));
        }
        stmf_state.stmf_ver_tg_head = NULL;

        if (stmf_state.stmf_luid_list.id_count) {
                /* clear the views for lus */
                for (idmemb = stmf_state.stmf_luid_list.idl_head;
                    idmemb; idmemb = idmemb_next) {
                        for (ve = (stmf_view_entry_t *)idmemb->id_impl_specific;
                            ve; ve = ve_next) {
                                ve_next = ve->ve_next;
                                ve->ve_hg->id_refcnt--;
                                ve->ve_tg->id_refcnt--;
                                kmem_free(ve, sizeof (stmf_view_entry_t));
                        }
                        if (idmemb->id_pt_to_object) {
                                ilu = (stmf_i_lu_t *)(idmemb->id_pt_to_object);
                                ilu->ilu_luid = NULL;
                        }
                        idmemb_next = idmemb->id_next;
                        stmf_free_id(idmemb);
                }
                stmf_state.stmf_luid_list.id_count = 0;
                stmf_state.stmf_luid_list.idl_head =
                    stmf_state.stmf_luid_list.idl_tail = NULL;
        }

        if (stmf_state.stmf_hg_list.id_count) {
                /* free all the host group */
                for (idgrp = stmf_state.stmf_hg_list.idl_head;
                    idgrp; idgrp = idgrp_next) {
                        idlist = (stmf_id_list_t *)(idgrp->id_impl_specific);
                        if (idlist->id_count) {
                                for (idmemb = idlist->idl_head; idmemb;
                                    idmemb = idmemb_next) {
                                        idmemb_next = idmemb->id_next;
                                        stmf_free_id(idmemb);
                                }
                        }
                        idgrp_next = idgrp->id_next;
                        stmf_free_id(idgrp);
                }
                stmf_state.stmf_hg_list.id_count = 0;
                stmf_state.stmf_hg_list.idl_head =
                    stmf_state.stmf_hg_list.idl_tail = NULL;
        }
        if (stmf_state.stmf_tg_list.id_count) {
                /* free all the target group */
                for (idgrp = stmf_state.stmf_tg_list.idl_head;
                    idgrp; idgrp = idgrp_next) {
                        idlist = (stmf_id_list_t *)(idgrp->id_impl_specific);
                        if (idlist->id_count) {
                                for (idmemb = idlist->idl_head; idmemb;
                                    idmemb = idmemb_next) {
                                        idmemb_next = idmemb->id_next;
                                        stmf_free_id(idmemb);
                                }
                        }
                        idgrp_next = idgrp->id_next;
                        stmf_free_id(idgrp);
                }
                stmf_state.stmf_tg_list.id_count = 0;
                stmf_state.stmf_tg_list.idl_head =
                    stmf_state.stmf_tg_list.idl_tail = NULL;
        }

        for (ilport = stmf_state.stmf_ilportlist; ilport;
            ilport = ilport->ilport_next) {
                ilport->ilport_tg = NULL;
        }
}

/*
 * Create luns map for session based on the view
 * iss_lockp is held
 */
stmf_status_t
stmf_session_create_lun_map(stmf_i_local_port_t *ilport,
    stmf_i_scsi_session_t *iss)
{
        stmf_id_data_t *tg;
        stmf_id_data_t *hg;
        stmf_ver_tg_t   *vertg;
        char *phg_data, *ptg_data;
        stmf_ver_hg_t   *verhg;
        stmf_lun_map_t  *ve_map;

        ASSERT(mutex_owned(&stmf_state.stmf_lock));

        tg = ilport->ilport_tg;
        hg = stmf_lookup_group_for_host(iss->iss_ss->ss_rport_id->ident,
            iss->iss_ss->ss_rport_id->ident_length);
        iss->iss_hg = hg;

        /*
         * get the view entry map,
         * take all host/target group into consideration
         */
        ve_map = stmf_duplicate_ve_map(0);
        for (vertg = stmf_state.stmf_ver_tg_head; vertg != NULL;
            vertg = vertg->vert_next) {
                ptg_data = (char *)vertg->vert_tg_ref->id_data;
                if ((ptg_data[0] != '*') && (!tg ||
                    ((tg->id_data[0] != '*') &&
                    (vertg->vert_tg_ref != tg)))) {
                        continue;
                }
                for (verhg = vertg->vert_verh_list; verhg != NULL;
                    verhg = verhg->verh_next) {
                        phg_data = (char *)verhg->verh_hg_ref->id_data;
                        if ((phg_data[0] != '*') && (!hg ||
                            ((hg->id_data[0] != '*') &&
                            (verhg->verh_hg_ref != hg)))) {
                                continue;
                        }
                        (void) stmf_merge_ve_map(&verhg->verh_ve_map, ve_map,
                            &ve_map, 0);
                }
        }


        if (ve_map->lm_nluns) {
                stmf_add_lus_to_session_per_vemap(ilport, iss, ve_map);
        }
        /* not configured, cannot access any luns for now */

        stmf_destroy_ve_map(ve_map);

        return (STMF_SUCCESS);
}

/*
 * Expects the session lock to be held.
 * iss_lockp is held
 */
stmf_xfer_data_t *
stmf_session_prepare_report_lun_data(stmf_lun_map_t *sm)
{
        stmf_xfer_data_t *xd;
        uint16_t nluns, ent;
        uint32_t alloc_size, data_size;
        uchar_t *buf;
        int i;

        nluns = sm->lm_nluns;

        data_size = 8 + (((uint32_t)nluns) << 3);
        if (nluns == 0) {
                data_size += 8;
        }
        alloc_size = data_size + sizeof (stmf_xfer_data_t) - 4;

        xd = (stmf_xfer_data_t *)kmem_zalloc(alloc_size, KM_NOSLEEP);

        if (xd == NULL)
                return (NULL);

        xd->alloc_size = alloc_size;
        xd->size_left = data_size;

        *((uint32_t *)xd->buf) = BE_32(data_size - 8);
        if (nluns == 0) {
                return (xd);
        }

        ent = 0;

        buf = (uchar_t *)xd + offsetof(stmf_xfer_data_t, buf);
        for (i = 0; ((i < sm->lm_nentries) && (ent < nluns)); i++) {
                if (sm->lm_plus[i] == NULL)
                        continue;
                /* Fill in the entry */
                buf[8 + (ent << 3) + 1] = (uchar_t)i;
                buf[8 + (ent << 3) + 0] = ((uchar_t)(i >> 8));
                ent++;
        }

        ASSERT(ent == nluns);

        return (xd);
}

/*
 * Add a lu to active sessions based on LUN inventory.
 * Only invoked when the lu is onlined
 */
void
stmf_add_lu_to_active_sessions(stmf_lu_t *lu)
{
        stmf_id_data_t *luid;
        stmf_view_entry_t       *ve;
        stmf_i_lu_t *ilu;

        ASSERT(mutex_owned(&stmf_state.stmf_lock));
        ilu = (stmf_i_lu_t *)lu->lu_stmf_private;
        ASSERT(ilu->ilu_state == STMF_STATE_ONLINE);

        luid = ((stmf_i_lu_t *)lu->lu_stmf_private)->ilu_luid;

        if (!luid) {
                /* we did not configure view for this lun, so just return */
                return;
        }

        for (ve = (stmf_view_entry_t *)luid->id_impl_specific;
            ve; ve = ve->ve_next) {
                stmf_update_sessions_per_ve(ve, lu, 1);
        }
}
/*
 * Unmap a lun from all sessions
 */
void
stmf_session_lu_unmapall(stmf_lu_t *lu)
{
        stmf_i_lu_t *ilu;
        stmf_id_data_t *luid;
        stmf_view_entry_t *ve;

        ASSERT(mutex_owned(&stmf_state.stmf_lock));

        ilu = (stmf_i_lu_t *)lu->lu_stmf_private;

        if (ilu->ilu_ref_cnt == 0)
                return;

        luid = ((stmf_i_lu_t *)lu->lu_stmf_private)->ilu_luid;
        if (!luid) {
                /*
                 * we did not configure view for this lun, this should be
                 * an error
                 */
                return;
        }

        for (ve = (stmf_view_entry_t *)luid->id_impl_specific;
            ve; ve = ve->ve_next) {
                stmf_update_sessions_per_ve(ve, lu, 0);
                if (ilu->ilu_ref_cnt == 0)
                        break;
        }
}
/*
 * add lu to a session, stmf_lock is already held
 * iss_lockp/ilport_lock already held
 */
static stmf_status_t
stmf_add_lu_to_session(stmf_i_local_port_t *ilport,
    stmf_i_scsi_session_t *iss,
    stmf_lu_t *lu,
    uint8_t *lu_nbr)
{
        stmf_lun_map_t *sm = iss->iss_sm;
        stmf_status_t ret;
        stmf_i_lu_t *ilu = (stmf_i_lu_t *)lu->lu_stmf_private;
        stmf_lun_map_ent_t *lun_map_ent;
        uint32_t new_flags = 0;
        uint16_t luNbr =
            ((uint16_t)lu_nbr[1] | (((uint16_t)(lu_nbr[0] & 0x3F)) << 8));

        ASSERT(mutex_owned(&stmf_state.stmf_lock));
        ASSERT(!stmf_get_ent_from_map(sm, luNbr));

        if ((sm->lm_nluns == 0) &&
            ((iss->iss_flags & ISS_BEING_CREATED) == 0)) {
                new_flags = ISS_GOT_INITIAL_LUNS;
                atomic_or_32(&ilport->ilport_flags, ILPORT_SS_GOT_INITIAL_LUNS);
                stmf_state.stmf_process_initial_luns = 1;
        }

        lun_map_ent = (stmf_lun_map_ent_t *)
            kmem_zalloc(sizeof (stmf_lun_map_ent_t), KM_SLEEP);
        lun_map_ent->ent_lu = lu;
        ret = stmf_add_ent_to_map(sm, (void *)lun_map_ent, lu_nbr);
        ASSERT(ret == STMF_SUCCESS);
        atomic_inc_32(&ilu->ilu_ref_cnt);
        /*
         * do not set lun inventory flag for standby port
         * as this would be handled from peer
         */
        if (ilport->ilport_standby == 0) {
                new_flags |= ISS_LUN_INVENTORY_CHANGED;
        }
        atomic_or_32(&iss->iss_flags, new_flags);
        return (STMF_SUCCESS);
}

/*
 * remvoe lu from a session, stmf_lock is already held
 * iss_lockp held
 */
static void
stmf_remove_lu_from_session(stmf_i_scsi_session_t *iss,
    stmf_lu_t *lu, uint8_t *lu_nbr)
{
        stmf_status_t ret;
        stmf_i_lu_t *ilu;
        stmf_lun_map_t *sm = iss->iss_sm;
        stmf_lun_map_ent_t *lun_map_ent;
        uint16_t luNbr =
            ((uint16_t)lu_nbr[1] | (((uint16_t)(lu_nbr[0] & 0x3F)) << 8));

        ASSERT(mutex_owned(&stmf_state.stmf_lock));
        lun_map_ent = stmf_get_ent_from_map(sm, luNbr);
        ASSERT(lun_map_ent->ent_lu == lu);
        if (lun_map_ent == NULL) {
                return;
        }

        ilu = (stmf_i_lu_t *)lu->lu_stmf_private;

        ret = stmf_remove_ent_from_map(sm, lu_nbr);
        ASSERT(ret == STMF_SUCCESS);
        atomic_dec_32(&ilu->ilu_ref_cnt);
        iss->iss_flags |= ISS_LUN_INVENTORY_CHANGED;
        if (lun_map_ent->ent_itl_datap) {
                stmf_do_itl_dereg(lu, lun_map_ent->ent_itl_datap,
                    STMF_ITL_REASON_USER_REQUEST);
        }
        kmem_free((void *)lun_map_ent, sizeof (stmf_lun_map_ent_t));
}

/*
 * add or remove lu from all related sessions based on view entry,
 * action is 0 for delete, 1 for add
 */
void
stmf_update_sessions_per_ve(stmf_view_entry_t *ve,
    stmf_lu_t *lu, int action)
{
        stmf_i_lu_t *ilu_tmp;
        stmf_lu_t *lu_to_add;
        stmf_i_local_port_t *ilport;
        stmf_i_scsi_session_t *iss;
        stmf_id_list_t  *hostlist;
        stmf_id_list_t  *targetlist;
        int all_hg = 0, all_tg = 0;

        ASSERT(mutex_owned(&stmf_state.stmf_lock));

        if (!lu) {
                ilu_tmp = (stmf_i_lu_t *)ve->ve_luid->id_pt_to_object;
                if (!ilu_tmp)
                        return;
                lu_to_add = ilu_tmp->ilu_lu;
        } else {
                lu_to_add = lu;
                ilu_tmp = (stmf_i_lu_t *)lu->lu_stmf_private;
        }

        if (ve->ve_hg->id_data[0] == '*')
                all_hg = 1;
        if (ve->ve_tg->id_data[0] == '*')
                all_tg = 1;
        hostlist = (stmf_id_list_t *)ve->ve_hg->id_impl_specific;
        targetlist = (stmf_id_list_t *)ve->ve_tg->id_impl_specific;

        if ((!all_hg && !hostlist->idl_head) ||
            (!all_tg && !targetlist->idl_head))
                /* No sessions to be updated */
                return;

        for (ilport = stmf_state.stmf_ilportlist; ilport != NULL;
            ilport = ilport->ilport_next) {
                if (!all_tg && ilport->ilport_tg != ve->ve_tg)
                        continue;
                /* This ilport belongs to the target group */
                rw_enter(&ilport->ilport_lock, RW_WRITER);
                for (iss = ilport->ilport_ss_list; iss != NULL;
                    iss = iss->iss_next) {
                        if (!all_hg && iss->iss_hg != ve->ve_hg)
                                continue;
                        /* This host belongs to the host group */
                        if (action == 0) { /* to remove */
                                stmf_remove_lu_from_session(iss, lu_to_add,
                                    ve->ve_lun);
                                if (ilu_tmp->ilu_ref_cnt == 0) {
                                        rw_exit(&ilport->ilport_lock);
                                        return;
                                }
                        } else {
                                (void) stmf_add_lu_to_session(ilport, iss,
                                    lu_to_add, ve->ve_lun);
                        }
                }
                rw_exit(&ilport->ilport_lock);
        }
}

/*
 * add luns in view entry map to a session,
 * and stmf_lock is already held
 */
void
stmf_add_lus_to_session_per_vemap(stmf_i_local_port_t *ilport,
    stmf_i_scsi_session_t *iss,
    stmf_lun_map_t *vemap)
{
        stmf_lu_t *lu;
        stmf_i_lu_t *ilu;
        stmf_view_entry_t *ve;
        uint32_t        i;

        ASSERT(mutex_owned(&stmf_state.stmf_lock));
        for (i = 0; i < vemap->lm_nentries; i++) {
                ve = (stmf_view_entry_t *)vemap->lm_plus[i];
                if (!ve)
                        continue;
                ilu = (stmf_i_lu_t *)ve->ve_luid->id_pt_to_object;
                if (ilu && ilu->ilu_state == STMF_STATE_ONLINE) {
                        lu = ilu->ilu_lu;
                        (void) stmf_add_lu_to_session(ilport, iss, lu,
                            ve->ve_lun);
                }
        }
}
/*
 * remove luns in view entry map from a session
 * iss_lockp held
 */
void
stmf_remove_lus_from_session_per_vemap(stmf_i_scsi_session_t *iss,
    stmf_lun_map_t *vemap)
{
        stmf_lu_t *lu;
        stmf_i_lu_t *ilu;
        stmf_view_entry_t *ve;
        uint32_t i;

        ASSERT(mutex_owned(&stmf_state.stmf_lock));

        for (i = 0; i < vemap->lm_nentries; i++) {
                ve = (stmf_view_entry_t *)vemap->lm_plus[i];
                if (!ve)
                        continue;
                ilu = (stmf_i_lu_t *)ve->ve_luid->id_pt_to_object;
                if (ilu && ilu->ilu_state == STMF_STATE_ONLINE) {
                        lu = ilu->ilu_lu;
                        stmf_remove_lu_from_session(iss, lu, ve->ve_lun);
                }
        }
}

stmf_id_data_t *
stmf_alloc_id(uint16_t id_size, uint16_t type, uint8_t *id_data,
    uint32_t additional_size)
{
        stmf_id_data_t *id;
        int struct_size, total_size, real_id_size;

        real_id_size = ((uint32_t)id_size + 7) & (~7);
        struct_size = (sizeof (*id) + 7) & (~7);
        total_size = ((additional_size + 7) & (~7)) + struct_size +
            real_id_size;
        id = (stmf_id_data_t *)kmem_zalloc(total_size, KM_SLEEP);
        id->id_type = type;
        id->id_data_size = id_size;
        id->id_data = ((uint8_t *)id) + struct_size;
        id->id_total_alloc_size = total_size;
        if (additional_size) {
                id->id_impl_specific = ((uint8_t *)id) + struct_size +
                    real_id_size;
        }
        bcopy(id_data, id->id_data, id_size);

        return (id);
}

void
stmf_free_id(stmf_id_data_t *id)
{
        kmem_free(id, id->id_total_alloc_size);
}


stmf_id_data_t *
stmf_lookup_id(stmf_id_list_t *idlist, uint16_t id_size, uint8_t *data)
{
        stmf_id_data_t *id;

        for (id = idlist->idl_head; id != NULL; id = id->id_next) {
                if ((id->id_data_size == id_size) &&
                    (bcmp(id->id_data, data, id_size) == 0)) {
                        return (id);
                }
        }

        return (NULL);
}
/* Return the target group which a target belong to */
stmf_id_data_t *
stmf_lookup_group_for_target(uint8_t *ident, uint16_t ident_size)
{
        stmf_id_data_t *tgid;
        stmf_id_data_t *target;

        ASSERT(mutex_owned(&stmf_state.stmf_lock));

        for (tgid = stmf_state.stmf_tg_list.idl_head; tgid;
            tgid = tgid->id_next) {
                target = stmf_lookup_id(
                    (stmf_id_list_t *)tgid->id_impl_specific,
                    ident_size, ident);
                if (target)
                        return (tgid);
        }
        return (NULL);
}
/* Return the host group which a host belong to */
stmf_id_data_t *
stmf_lookup_group_for_host(uint8_t *ident, uint16_t ident_size)
{
        stmf_id_data_t *hgid;
        stmf_id_data_t *host;

        ASSERT(mutex_owned(&stmf_state.stmf_lock));

        for (hgid = stmf_state.stmf_hg_list.idl_head; hgid;
            hgid = hgid->id_next) {
                host = stmf_lookup_id(
                    (stmf_id_list_t *)hgid->id_impl_specific,
                    ident_size, ident);
                if (host)
                        return (hgid);
        }
        return (NULL);
}

void
stmf_append_id(stmf_id_list_t *idlist, stmf_id_data_t *id)
{
        id->id_next = NULL;

        if ((id->id_prev = idlist->idl_tail) == NULL) {
                idlist->idl_head = idlist->idl_tail = id;
        } else {
                idlist->idl_tail->id_next = id;
                idlist->idl_tail = id;
        }
        atomic_inc_32(&idlist->id_count);
}

void
stmf_remove_id(stmf_id_list_t *idlist, stmf_id_data_t *id)
{
        if (id->id_next) {
                id->id_next->id_prev = id->id_prev;
        } else {
                idlist->idl_tail = id->id_prev;
        }

        if (id->id_prev) {
                id->id_prev->id_next = id->id_next;
        } else {
                idlist->idl_head = id->id_next;
        }
        atomic_dec_32(&idlist->id_count);
}


/*
 * The refcnts of objects in a view entry are updated when then entry
 * is successfully added. ve_map is just another representation of the
 * view enrtries in a LU. Duplicating or merging a ve map does not
 * affect any refcnts.
 * stmf_state.stmf_lock held
 */
stmf_lun_map_t *
stmf_duplicate_ve_map(stmf_lun_map_t *src)
{
        stmf_lun_map_t *dst;
        int i;

        dst = (stmf_lun_map_t *)kmem_zalloc(sizeof (*dst), KM_SLEEP);

        if (src == NULL)
                return (dst);

        if (src->lm_nentries) {
                dst->lm_plus = kmem_zalloc(dst->lm_nentries *
                    sizeof (void *), KM_SLEEP);
                for (i = 0; i < dst->lm_nentries; i++) {
                        dst->lm_plus[i] = src->lm_plus[i];
                }
        }

        return (dst);
}

void
stmf_destroy_ve_map(stmf_lun_map_t *dst)
{
        if (dst->lm_nentries) {
                kmem_free(dst->lm_plus, dst->lm_nentries * sizeof (void *));
        }
        kmem_free(dst, sizeof (*dst));
}

/*
 * stmf_state.stmf_lock held. Operations are stmf global in nature and
 * not session level.
 */
int
stmf_merge_ve_map(stmf_lun_map_t *src, stmf_lun_map_t *dst,
    stmf_lun_map_t **pp_ret_map, stmf_merge_flags_t mf)
{
        int i;
        int nentries;
        int to_create_space = 0;

        if (dst == NULL) {
                *pp_ret_map = stmf_duplicate_ve_map(src);
                return (1);
        }

        if (src == NULL || src->lm_nluns == 0) {
                if (mf & MERGE_FLAG_RETURN_NEW_MAP)
                        *pp_ret_map = stmf_duplicate_ve_map(dst);
                else
                        *pp_ret_map = dst;
                return (1);
        }

        if (mf & MERGE_FLAG_RETURN_NEW_MAP) {
                *pp_ret_map = stmf_duplicate_ve_map(NULL);
                nentries = max(dst->lm_nentries, src->lm_nentries);
                to_create_space = 1;
        } else {
                *pp_ret_map = dst;
                /* If there is not enough space in dst map */
                if (dst->lm_nentries < src->lm_nentries) {
                        nentries = src->lm_nentries;
                        to_create_space = 1;
                }
        }
        if (to_create_space) {
                void **p;
                p = (void **)kmem_zalloc(nentries * sizeof (void *), KM_SLEEP);
                if (dst->lm_nentries) {
                        bcopy(dst->lm_plus, p,
                            dst->lm_nentries * sizeof (void *));
                }
                if (mf & (MERGE_FLAG_RETURN_NEW_MAP == 0))
                        kmem_free(dst->lm_plus,
                            dst->lm_nentries * sizeof (void *));
                (*pp_ret_map)->lm_plus = p;
                (*pp_ret_map)->lm_nentries = nentries;
        }

        for (i = 0; i < src->lm_nentries; i++) {
                if (src->lm_plus[i] == NULL)
                        continue;
                if (dst->lm_plus[i] != NULL) {
                        if (mf & MERGE_FLAG_NO_DUPLICATE) {
                                if (mf & MERGE_FLAG_RETURN_NEW_MAP) {
                                        stmf_destroy_ve_map(*pp_ret_map);
                                        *pp_ret_map = NULL;
                                }
                                return (0);
                        }
                } else {
                        dst->lm_plus[i] = src->lm_plus[i];
                        dst->lm_nluns++;
                }
        }

        return (1);
}

/*
 * add host group, id_impl_specific point to a list of hosts,
 * on return, if error happened, err_detail may be assigned if
 * the pointer is not NULL
 */
stmf_status_t
stmf_add_hg(uint8_t *hg_name, uint16_t hg_name_size,
    int allow_special, uint32_t *err_detail)
{
        stmf_id_data_t *id;

        if (!allow_special) {
                if (hg_name[0] == '*')
                        return (STMF_INVALID_ARG);
        }

        if (stmf_lookup_id(&stmf_state.stmf_hg_list,
            hg_name_size, (uint8_t *)hg_name)) {
                if (err_detail)
                        *err_detail = STMF_IOCERR_HG_EXISTS;
                return (STMF_ALREADY);
        }
        id = stmf_alloc_id(hg_name_size, STMF_ID_TYPE_HOST_GROUP,
            (uint8_t *)hg_name, sizeof (stmf_id_list_t));
        stmf_append_id(&stmf_state.stmf_hg_list, id);

        return (STMF_SUCCESS);
}

/* add target group */
stmf_status_t
stmf_add_tg(uint8_t *tg_name, uint16_t tg_name_size,
    int allow_special, uint32_t *err_detail)
{
        stmf_id_data_t *id;

        if (!allow_special) {
                if (tg_name[0] == '*')
                        return (STMF_INVALID_ARG);
        }


        if (stmf_lookup_id(&stmf_state.stmf_tg_list, tg_name_size,
            (uint8_t *)tg_name)) {
                if (err_detail)
                        *err_detail = STMF_IOCERR_TG_EXISTS;
                return (STMF_ALREADY);
        }
        id = stmf_alloc_id(tg_name_size, STMF_ID_TYPE_TARGET_GROUP,
            (uint8_t *)tg_name, sizeof (stmf_id_list_t));
        stmf_append_id(&stmf_state.stmf_tg_list, id);

        return (STMF_SUCCESS);
}

/*
 * insert view entry into list for a luid, if ve->ve_id is 0xffffffff,
 * pick up a smallest available veid for it, and return the veid in ve->ve_id.
 * The view entries list is sorted based on veid.
 */
stmf_status_t
stmf_add_ve_to_luid(stmf_id_data_t *luid, stmf_view_entry_t *ve)
{
        stmf_view_entry_t *ve_tmp = NULL;
        stmf_view_entry_t *ve_prev = NULL;

        ASSERT(mutex_owned(&stmf_state.stmf_lock));

        ve_tmp = (stmf_view_entry_t *)luid->id_impl_specific;

        if (ve->ve_id != 0xffffffff) {
                for (; ve_tmp; ve_tmp = ve_tmp->ve_next) {
                        if (ve_tmp->ve_id > ve->ve_id) {
                                break;
                        } else if (ve_tmp->ve_id == ve->ve_id) {
                                return (STMF_ALREADY);
                        }
                        ve_prev = ve_tmp;
                }
        } else {
                uint32_t veid = 0;
                /* search the smallest available veid */
                for (; ve_tmp; ve_tmp = ve_tmp->ve_next) {
                        ASSERT(ve_tmp->ve_id >= veid);
                        if (ve_tmp->ve_id != veid)
                                break;
                        veid++;
                        if (veid == 0xffffffff)
                                return (STMF_NOT_SUPPORTED);
                        ve_prev = ve_tmp;
                }
                ve->ve_id = veid;
        }

        /* insert before ve_tmp if it exist */
        ve->ve_next = ve_tmp;
        ve->ve_prev = ve_prev;
        if (ve_tmp) {
                ve_tmp->ve_prev = ve;
        }
        if (ve_prev) {
                ve_prev->ve_next = ve;
        } else {
                luid->id_impl_specific = (void *)ve;
        }
        return (STMF_SUCCESS);
}

/* stmf_lock is already held, err_detail may be assigned if error happens */
stmf_status_t
stmf_add_view_entry(stmf_id_data_t *hg, stmf_id_data_t *tg,
    uint8_t *lu_guid, uint32_t *ve_id, uint8_t *lun,
    stmf_view_entry_t **conflicting, uint32_t *err_detail)
{
        stmf_id_data_t *luid;
        stmf_view_entry_t *ve;
        char *phg, *ptg;
        stmf_lun_map_t *ve_map = NULL;
        stmf_ver_hg_t *verhg = NULL, *verhg_ex = NULL;
        stmf_ver_tg_t *vertg = NULL, *vertg_ex = NULL;
        char luid_new;
        uint16_t lun_num;
        stmf_i_lu_t *ilu;
        stmf_status_t ret;

        ASSERT(mutex_owned(&stmf_state.stmf_lock));

        lun_num = ((uint16_t)lun[1] | (((uint16_t)(lun[0] & 0x3F)) << 8));

        luid = stmf_lookup_id(&stmf_state.stmf_luid_list, 16, lu_guid);
        if (luid == NULL) {
                luid = stmf_alloc_id(16, STMF_ID_TYPE_LU_GUID, lu_guid, 0);
                ilu = stmf_luident_to_ilu(lu_guid);
                if (ilu) {
                        ilu->ilu_luid = luid;
                        luid->id_pt_to_object = (void *)ilu;
                }
                luid_new = 1;
        } else {
                luid_new = 0;
                ilu = (stmf_i_lu_t *)luid->id_pt_to_object;
        }

        /* The view entry won't be added if there is any confilict */
        phg = (char *)hg->id_data; ptg = (char *)tg->id_data;
        for (ve = (stmf_view_entry_t *)luid->id_impl_specific; ve != NULL;
            ve = ve->ve_next) {
                if (((phg[0] == '*') || (ve->ve_hg->id_data[0] == '*') ||
                    (hg == ve->ve_hg)) && ((ptg[0] == '*') ||
                    (ve->ve_tg->id_data[0] == '*') || (tg == ve->ve_tg))) {
                        *conflicting = ve;
                        *err_detail = STMF_IOCERR_VIEW_ENTRY_CONFLICT;
                        ret = STMF_ALREADY;
                        goto add_ve_err_ret;
                }
        }

        ve_map = stmf_duplicate_ve_map(0);
        for (vertg = stmf_state.stmf_ver_tg_head; vertg != NULL;
            vertg = vertg->vert_next) {
                ptg = (char *)vertg->vert_tg_ref->id_data;
                if ((ptg[0] != '*') && (tg->id_data[0] != '*') &&
                    (vertg->vert_tg_ref != tg)) {
                        continue;
                }
                if (vertg->vert_tg_ref == tg)
                        vertg_ex = vertg;
                for (verhg = vertg->vert_verh_list; verhg != NULL;
                    verhg = verhg->verh_next) {
                        phg = (char *)verhg->verh_hg_ref->id_data;
                        if ((phg[0] != '*') && (hg->id_data[0] != '*') &&
                            (verhg->verh_hg_ref != hg)) {
                                continue;
                        }
                        if ((vertg_ex == vertg) && (verhg->verh_hg_ref == hg))
                                verhg_ex = verhg;
                        (void) stmf_merge_ve_map(&verhg->verh_ve_map, ve_map,
                            &ve_map, 0);
                }
        }

        if (lun[2] == 0xFF) {
                /* Pick a LUN number */
                lun_num = stmf_get_next_free_lun(ve_map, lun);
                if (lun_num > 0x3FFF) {
                        stmf_destroy_ve_map(ve_map);
                        ret = STMF_NOT_SUPPORTED;
                        goto add_ve_err_ret;
                }
        } else {
                if ((*conflicting = stmf_get_ent_from_map(ve_map, lun_num))
                    != NULL) {
                        stmf_destroy_ve_map(ve_map);
                        *err_detail = STMF_IOCERR_LU_NUMBER_IN_USE;
                        ret = STMF_LUN_TAKEN;
                        goto add_ve_err_ret;
                }
        }
        stmf_destroy_ve_map(ve_map);

        /* All is well, do the actual addition now */
        ve = (stmf_view_entry_t *)kmem_zalloc(sizeof (*ve), KM_SLEEP);
        ve->ve_id = *ve_id;
        ve->ve_lun[0] = lun[0];
        ve->ve_lun[1] = lun[1];

        if ((ret = stmf_add_ve_to_luid(luid, ve)) != STMF_SUCCESS) {
                kmem_free(ve, sizeof (stmf_view_entry_t));
                goto add_ve_err_ret;
        }
        ve->ve_hg = hg; hg->id_refcnt++;
        ve->ve_tg = tg; tg->id_refcnt++;
        ve->ve_luid = luid; luid->id_refcnt++;

        *ve_id = ve->ve_id;

        if (luid_new) {
                stmf_append_id(&stmf_state.stmf_luid_list, luid);
        }

        if (vertg_ex == NULL) {
                vertg_ex = (stmf_ver_tg_t *)kmem_zalloc(sizeof (stmf_ver_tg_t),
                    KM_SLEEP);
                vertg_ex->vert_next = stmf_state.stmf_ver_tg_head;
                stmf_state.stmf_ver_tg_head = vertg_ex;
                vertg_ex->vert_tg_ref = tg;
                verhg_ex = vertg_ex->vert_verh_list =
                    (stmf_ver_hg_t *)kmem_zalloc(sizeof (stmf_ver_hg_t),
                    KM_SLEEP);
                verhg_ex->verh_hg_ref = hg;
        }
        if (verhg_ex == NULL) {
                verhg_ex = (stmf_ver_hg_t *)kmem_zalloc(sizeof (stmf_ver_hg_t),
                    KM_SLEEP);
                verhg_ex->verh_next = vertg_ex->vert_verh_list;
                vertg_ex->vert_verh_list = verhg_ex;
                verhg_ex->verh_hg_ref = hg;
        }
        ret = stmf_add_ent_to_map(&verhg_ex->verh_ve_map, ve, ve->ve_lun);
        ASSERT(ret == STMF_SUCCESS);

        /* we need to update the affected session */
        if (stmf_state.stmf_service_running) {
                if (ilu && ilu->ilu_state == STMF_STATE_ONLINE)
                        stmf_update_sessions_per_ve(ve, ilu->ilu_lu, 1);
        }

        return (STMF_SUCCESS);
add_ve_err_ret:
        if (luid_new) {
                if (ilu)
                        ilu->ilu_luid = NULL;
                stmf_free_id(luid);
        }
        return (ret);
}

/*
 * protected by stmf_state.stmf_lock when working on global lun map.
 * iss_lockp when working at the session level.
 */
static stmf_status_t
stmf_add_ent_to_map(stmf_lun_map_t *lm, void *ent, uint8_t *lun)
{
        uint16_t n;
        if (((lun[0] & 0xc0) >> 6) != 0)
                return (STMF_FAILURE);

        n = (uint16_t)lun[1] | (((uint16_t)(lun[0] & 0x3F)) << 8);
try_again_to_add:
        if (lm->lm_nentries && (n < lm->lm_nentries)) {
                if (lm->lm_plus[n] == NULL) {
                        lm->lm_plus[n] = ent;
                        lm->lm_nluns++;
                        return (STMF_SUCCESS);
                } else {
                        return (STMF_LUN_TAKEN);
                }
        } else {
                void **pplu;
                uint16_t m = n + 1;
                m = ((m + 7) & ~7) & 0x7FFF;
                pplu = (void **)kmem_zalloc(m * sizeof (void *), KM_SLEEP);
                bcopy(lm->lm_plus, pplu,
                    lm->lm_nentries * sizeof (void *));
                kmem_free(lm->lm_plus, lm->lm_nentries * sizeof (void *));
                lm->lm_plus = pplu;
                lm->lm_nentries = m;
                goto try_again_to_add;
        }
}


/*
 * iss_lockp held when working on a session.
 * stmf_state.stmf_lock is held when working on the global views.
 */
static stmf_status_t
stmf_remove_ent_from_map(stmf_lun_map_t *lm, uint8_t *lun)
{
        uint16_t n, i;
        uint8_t lutype = (lun[0] & 0xc0) >> 6;
        if (lutype != 0)
                return (STMF_FAILURE);

        n = (uint16_t)lun[1] | (((uint16_t)(lun[0] & 0x3F)) << 8);

        if (n >= lm->lm_nentries)
                return (STMF_NOT_FOUND);
        if (lm->lm_plus[n] == NULL)
                return (STMF_NOT_FOUND);

        lm->lm_plus[n] = NULL;
        lm->lm_nluns--;

        for (i = 0; i < lm->lm_nentries; i++) {
                if (lm->lm_plus[lm->lm_nentries - 1 - i] != NULL)
                        break;
        }
        i &= ~15;
        if (i >= 16) {
                void **pplu;
                uint16_t m;
                m = lm->lm_nentries - i;
                pplu = (void **)kmem_zalloc(m * sizeof (void *), KM_SLEEP);
                bcopy(lm->lm_plus, pplu, m * sizeof (void *));
                kmem_free(lm->lm_plus, lm->lm_nentries * sizeof (void *));
                lm->lm_plus = pplu;
                lm->lm_nentries = m;
        }

        return (STMF_SUCCESS);
}

/*
 * stmf_state.stmf_lock held
 */
uint16_t
stmf_get_next_free_lun(stmf_lun_map_t *sm, uint8_t *lun)
{
        uint16_t luNbr;


        if (sm->lm_nluns < 0x4000) {
                for (luNbr = 0; luNbr < sm->lm_nentries; luNbr++) {
                        if (sm->lm_plus[luNbr] == NULL)
                                break;
                }
        } else {
                return (0xFFFF);
        }
        if (lun) {
                bzero(lun, 8);
                lun[1] = luNbr & 0xff;
                lun[0] = (luNbr >> 8) & 0xff;
        }

        return (luNbr);
}

/*
 * stmf_state.stmf_lock is held when working on global view map
 * iss_lockp (RW_WRITER) is held when working on session map.
 */
void *
stmf_get_ent_from_map(stmf_lun_map_t *sm, uint16_t lun_num)
{
        if ((lun_num & 0xC000) == 0) {
                if (sm->lm_nentries > lun_num)
                        return (sm->lm_plus[lun_num & 0x3FFF]);
                else
                        return (NULL);
        }

        return (NULL);
}

int
stmf_add_ve(uint8_t *hgname, uint16_t hgname_size,
    uint8_t *tgname, uint16_t tgname_size,
    uint8_t *lu_guid, uint32_t *ve_id,
    uint8_t *luNbr, uint32_t *err_detail)
{
        stmf_id_data_t *hg;
        stmf_id_data_t *tg;
        stmf_view_entry_t *conflictve;
        stmf_status_t ret;

        ASSERT(mutex_owned(&stmf_state.stmf_lock));

        hg = stmf_lookup_id(&stmf_state.stmf_hg_list, hgname_size,
            (uint8_t *)hgname);
        if (!hg) {
                *err_detail = STMF_IOCERR_INVALID_HG;
                return (ENOENT); /* could not find group */
        }
        tg = stmf_lookup_id(&stmf_state.stmf_tg_list, tgname_size,
            (uint8_t *)tgname);
        if (!tg) {
                *err_detail = STMF_IOCERR_INVALID_TG;
                return (ENOENT); /* could not find group */
        }
        ret = stmf_add_view_entry(hg, tg, lu_guid, ve_id, luNbr,
            &conflictve, err_detail);

        if (ret == STMF_ALREADY) {
                return (EALREADY);
        } else if (ret == STMF_LUN_TAKEN) {
                return (EEXIST);
        } else if (ret == STMF_NOT_SUPPORTED) {
                return (E2BIG);
        } else if (ret != STMF_SUCCESS) {
                return (EINVAL);
        }
        return (0);
}

int
stmf_remove_ve_by_id(uint8_t *guid, uint32_t veid, uint32_t *err_detail)
{
        stmf_id_data_t *luid;
        stmf_view_entry_t       *ve;
        stmf_ver_tg_t *vtg;
        stmf_ver_hg_t *vhg;
        stmf_ver_tg_t *prev_vtg = NULL;
        stmf_ver_hg_t *prev_vhg = NULL;
        int found = 0;
        stmf_i_lu_t *ilu;

        ASSERT(mutex_owned(&stmf_state.stmf_lock));
        luid = stmf_lookup_id(&stmf_state.stmf_luid_list, 16, guid);
        if (luid == NULL) {
                *err_detail = STMF_IOCERR_INVALID_LU_ID;
                return (ENODEV);
        }
        ilu = (stmf_i_lu_t *)luid->id_pt_to_object;

        for (ve = (stmf_view_entry_t *)luid->id_impl_specific;
            ve; ve = ve->ve_next) {
                if (ve->ve_id == veid) {
                        break;
                }
        }
        if (!ve) {
                *err_detail = STMF_IOCERR_INVALID_VE_ID;
                return (ENODEV);
        }
        /* remove the ve */
        if (ve->ve_next)
                ve->ve_next->ve_prev = ve->ve_prev;
        if (ve->ve_prev)
                ve->ve_prev->ve_next = ve->ve_next;
        else {
                luid->id_impl_specific = (void *)ve->ve_next;
                if (!luid->id_impl_specific) {
                        /* don't have any view entries related to this lu */
                        stmf_remove_id(&stmf_state.stmf_luid_list, luid);
                        if (ilu)
                                ilu->ilu_luid = NULL;
                        stmf_free_id(luid);
                }
        }

        /* we need to update ver_hg->verh_ve_map */
        for (vtg = stmf_state.stmf_ver_tg_head; vtg; vtg = vtg->vert_next) {
                if (vtg->vert_tg_ref == ve->ve_tg) {
                        found = 1;
                        break;
                }
                prev_vtg = vtg;
        }
        ASSERT(found);
        found = 0;
        for (vhg = vtg->vert_verh_list; vhg; vhg = vhg->verh_next) {
                if (vhg->verh_hg_ref == ve->ve_hg) {
                        found = 1;
                        break;
                }
                prev_vhg = vhg;
        }
        ASSERT(found);

        (void) stmf_remove_ent_from_map(&vhg->verh_ve_map, ve->ve_lun);

        /* free verhg if it don't have any ve entries related */
        if (!vhg->verh_ve_map.lm_nluns) {
                /* we don't have any view entry related */
                if (prev_vhg)
                        prev_vhg->verh_next = vhg->verh_next;
                else
                        vtg->vert_verh_list = vhg->verh_next;

                /* Free entries in case the map still has memory */
                if (vhg->verh_ve_map.lm_nentries) {
                        kmem_free(vhg->verh_ve_map.lm_plus,
                            vhg->verh_ve_map.lm_nentries *
                            sizeof (void *));
                }
                kmem_free(vhg, sizeof (stmf_ver_hg_t));
                if (!vtg->vert_verh_list) {
                        /* we don't have any ve related */
                        if (prev_vtg)
                                prev_vtg->vert_next = vtg->vert_next;
                        else
                                stmf_state.stmf_ver_tg_head = vtg->vert_next;
                        kmem_free(vtg, sizeof (stmf_ver_tg_t));
                }
        }

        if (stmf_state.stmf_service_running && ilu &&
            ilu->ilu_state == STMF_STATE_ONLINE) {
                stmf_update_sessions_per_ve(ve, ilu->ilu_lu, 0);
        }

        ve->ve_hg->id_refcnt--;
        ve->ve_tg->id_refcnt--;

        kmem_free(ve, sizeof (stmf_view_entry_t));
        return (0);
}

int
stmf_add_group(uint8_t *grpname, uint16_t grpname_size,
    stmf_id_type_t group_type, uint32_t *err_detail)
{
        stmf_status_t status;

        ASSERT(mutex_owned(&stmf_state.stmf_lock));

        if (group_type == STMF_ID_TYPE_HOST_GROUP)
                status = stmf_add_hg(grpname, grpname_size, 0, err_detail);
        else if (group_type == STMF_ID_TYPE_TARGET_GROUP)
                status = stmf_add_tg(grpname, grpname_size, 0, err_detail);
        else {
                return (EINVAL);
        }
        switch (status) {
        case STMF_SUCCESS:
                return (0);
        case STMF_INVALID_ARG:
                return (EINVAL);
        case STMF_ALREADY:
                return (EEXIST);
        default:
                return (EIO);
        }
}

/*
 * Group can only be removed only when it does not have
 * any view entry related
 */
int
stmf_remove_group(uint8_t *grpname, uint16_t grpname_size,
    stmf_id_type_t group_type, uint32_t *err_detail)
{
        stmf_id_data_t *id;
        stmf_id_data_t *idmemb;
        stmf_id_list_t *grp_memblist;
        stmf_i_scsi_session_t *iss;
        stmf_i_local_port_t *ilport;

        if (grpname[0] == '*')
                return (EINVAL);

        ASSERT(mutex_owned(&stmf_state.stmf_lock));

        if (group_type == STMF_ID_TYPE_HOST_GROUP)
                id = stmf_lookup_id(&stmf_state.stmf_hg_list,
                    grpname_size, grpname);
        else if (group_type == STMF_ID_TYPE_TARGET_GROUP)
                id = stmf_lookup_id(&stmf_state.stmf_tg_list,
                    grpname_size, grpname);
        if (!id) {
                *err_detail = (group_type == STMF_ID_TYPE_HOST_GROUP)?
                    STMF_IOCERR_INVALID_HG:STMF_IOCERR_INVALID_TG;
                return (ENODEV); /* no such grp */
        }
        if (id->id_refcnt) {
                /* fail, still have viewentry related to it */
                *err_detail = (group_type == STMF_ID_TYPE_HOST_GROUP)?
                    STMF_IOCERR_HG_IN_USE:STMF_IOCERR_TG_IN_USE;
                return (EBUSY);
        }
        grp_memblist = (stmf_id_list_t *)id->id_impl_specific;
        while ((idmemb = grp_memblist->idl_head) != NULL) {
                stmf_remove_id(grp_memblist, idmemb);
                stmf_free_id(idmemb);
        }

        ASSERT(!grp_memblist->id_count);
        if (id->id_type == STMF_ID_TYPE_TARGET_GROUP) {
                for (ilport = stmf_state.stmf_ilportlist; ilport;
                    ilport = ilport->ilport_next) {
                        if (ilport->ilport_tg == (void *)id) {
                                ilport->ilport_tg = NULL;
                        }
                }
                stmf_remove_id(&stmf_state.stmf_tg_list, id);
        } else {
                for (ilport = stmf_state.stmf_ilportlist; ilport;
                    ilport = ilport->ilport_next) {
                        for (iss = ilport->ilport_ss_list; iss;
                            iss = iss->iss_next) {
                                if (iss->iss_hg == (void *)id)
                                        iss->iss_hg = NULL;
                        }
                }
                stmf_remove_id(&stmf_state.stmf_hg_list, id);
        }
        stmf_free_id(id);
        return (0);

}

int
stmf_add_group_member(uint8_t *grpname, uint16_t grpname_size,
    uint8_t *entry_ident, uint16_t entry_size,
    stmf_id_type_t entry_type, uint32_t *err_detail)
{
        stmf_id_data_t  *id_grp, *id_alltgt;
        stmf_id_data_t  *id_member;
        stmf_id_data_t  *id_grp_tmp;
        stmf_i_scsi_session_t *iss;
        stmf_i_local_port_t *ilport;
        stmf_lun_map_t *vemap, *vemap_alltgt;
        uint8_t grpname_forall = '*';

        ASSERT(mutex_owned(&stmf_state.stmf_lock));
        ASSERT(grpname[0] != '*');

        if (entry_type == STMF_ID_TYPE_HOST) {
                id_grp = stmf_lookup_id(&stmf_state.stmf_hg_list,
                    grpname_size, grpname);
                id_grp_tmp = stmf_lookup_group_for_host(entry_ident,
                    entry_size);
        } else {
                id_grp = stmf_lookup_id(&stmf_state.stmf_tg_list,
                    grpname_size, grpname);
                id_grp_tmp = stmf_lookup_group_for_target(entry_ident,
                    entry_size);
        }
        if (id_grp == NULL) {
                *err_detail = (entry_type == STMF_ID_TYPE_HOST)?
                    STMF_IOCERR_INVALID_HG:STMF_IOCERR_INVALID_TG;
                return (ENODEV); /* not found */
        }

        /* Check whether this member already bound to a group */
        if (id_grp_tmp) {
                if (id_grp_tmp != id_grp) {
                        *err_detail = (entry_type == STMF_ID_TYPE_HOST)?
                            STMF_IOCERR_HG_ENTRY_EXISTS:
                            STMF_IOCERR_TG_ENTRY_EXISTS;
                        return (EEXIST); /* already added into another grp */
                }
                else
                        return (0);
        }

        /* verify target is offline */
        if (entry_type == STMF_ID_TYPE_TARGET) {
                ilport = stmf_targetident_to_ilport(entry_ident, entry_size);
                if (ilport && ilport->ilport_state != STMF_STATE_OFFLINE) {
                        *err_detail = STMF_IOCERR_TG_NEED_TG_OFFLINE;
                        return (EBUSY);
                }
        }

        id_member = stmf_alloc_id(entry_size, entry_type,
            entry_ident, 0);
        stmf_append_id((stmf_id_list_t *)id_grp->id_impl_specific, id_member);

        if (entry_type == STMF_ID_TYPE_TARGET) {
                ilport = stmf_targetident_to_ilport(entry_ident, entry_size);
                if (ilport)
                        ilport->ilport_tg = (void *)id_grp;
                return (0);
        }
        /* For host group member, update the session if needed */
        if (!stmf_state.stmf_service_running)
                return (0);
        /* Need to consider all target group + this host group */
        id_alltgt = stmf_lookup_id(&stmf_state.stmf_tg_list,
            1, &grpname_forall);
        vemap_alltgt = stmf_get_ve_map_per_ids(id_alltgt, id_grp);

        /* check whether there are sessions may be affected */
        for (ilport = stmf_state.stmf_ilportlist; ilport;
            ilport = ilport->ilport_next) {
                if (ilport->ilport_state != STMF_STATE_ONLINE)
                        continue;
                iss = stmf_lookup_session_for_hostident(ilport,
                    entry_ident, entry_size);
                if (iss) {
                        stmf_id_data_t *tgid;
                        iss->iss_hg = (void *)id_grp;
                        tgid = ilport->ilport_tg;
                        rw_enter(iss->iss_lockp, RW_WRITER);
                        if (tgid) {
                                vemap = stmf_get_ve_map_per_ids(tgid, id_grp);
                                if (vemap)
                                        stmf_add_lus_to_session_per_vemap(
                                            ilport, iss, vemap);
                        }
                        if (vemap_alltgt)
                                stmf_add_lus_to_session_per_vemap(ilport,
                                    iss, vemap_alltgt);
                        rw_exit(iss->iss_lockp);
                }
        }

        return (0);
}

int
stmf_remove_group_member(uint8_t *grpname, uint16_t grpname_size,
    uint8_t *entry_ident, uint16_t entry_size,
    stmf_id_type_t entry_type, uint32_t *err_detail)
{
        stmf_id_data_t  *id_grp, *id_alltgt;
        stmf_id_data_t  *id_member;
        stmf_lun_map_t *vemap,  *vemap_alltgt;
        uint8_t grpname_forall = '*';
        stmf_i_local_port_t *ilport;
        stmf_i_scsi_session_t *iss;

        ASSERT(mutex_owned(&stmf_state.stmf_lock));
        ASSERT(grpname[0] != '*');

        if (entry_type == STMF_ID_TYPE_HOST) {
                id_grp = stmf_lookup_id(&stmf_state.stmf_hg_list,
                    grpname_size, grpname);
        } else {
                id_grp = stmf_lookup_id(&stmf_state.stmf_tg_list,
                    grpname_size, grpname);
        }
        if (id_grp == NULL) {
                *err_detail = (entry_type == STMF_ID_TYPE_HOST)?
                    STMF_IOCERR_INVALID_HG:STMF_IOCERR_INVALID_TG;
                return (ENODEV); /* no such group */
        }
        id_member = stmf_lookup_id((stmf_id_list_t *)id_grp->id_impl_specific,
            entry_size, entry_ident);
        if (!id_member) {
                *err_detail = (entry_type == STMF_ID_TYPE_HOST)?
                    STMF_IOCERR_INVALID_HG_ENTRY:STMF_IOCERR_INVALID_TG_ENTRY;
                return (ENODEV); /* no such member */
        }
        /* verify target is offline */
        if (entry_type == STMF_ID_TYPE_TARGET) {
                ilport = stmf_targetident_to_ilport(entry_ident, entry_size);
                if (ilport && ilport->ilport_state != STMF_STATE_OFFLINE) {
                        *err_detail = STMF_IOCERR_TG_NEED_TG_OFFLINE;
                        return (EBUSY);
                }
        }

        stmf_remove_id((stmf_id_list_t *)id_grp->id_impl_specific, id_member);
        stmf_free_id(id_member);

        if (entry_type == STMF_ID_TYPE_TARGET) {
                ilport = stmf_targetident_to_ilport(entry_ident, entry_size);
                if (ilport)
                        ilport->ilport_tg = NULL;
                return (0);
        }
        /* For host group member, update the session */
        if (!stmf_state.stmf_service_running)
                return (0);

        /* Need to consider all target group + this host group */
        id_alltgt = stmf_lookup_id(&stmf_state.stmf_tg_list,
            1, &grpname_forall);
        vemap_alltgt = stmf_get_ve_map_per_ids(id_alltgt, id_grp);

        /* check if there are session related, if so, update it */
        for (ilport = stmf_state.stmf_ilportlist; ilport;
            ilport = ilport->ilport_next) {
                if (ilport->ilport_state != STMF_STATE_ONLINE)
                        continue;
                iss = stmf_lookup_session_for_hostident(ilport,
                    entry_ident, entry_size);
                if (iss) {
                        stmf_id_data_t *tgid;
                        rw_enter(iss->iss_lockp, RW_WRITER);
                        iss->iss_hg = NULL;
                        tgid = ilport->ilport_tg;
                        if (tgid) {
                                vemap = stmf_get_ve_map_per_ids(tgid, id_grp);
                                if (vemap)
                                        stmf_remove_lus_from_session_per_vemap(
                                            iss, vemap);
                        }
                        if (vemap_alltgt)
                                stmf_remove_lus_from_session_per_vemap(iss,
                                    vemap_alltgt);
                        rw_exit(iss->iss_lockp);
                }
        }

        return (0);
}

/* Assert stmf_lock is already held */
stmf_i_local_port_t *
stmf_targetident_to_ilport(uint8_t *target_ident, uint16_t ident_size)
{
        stmf_i_local_port_t *ilport;
        uint8_t *id;

        ASSERT(mutex_owned(&stmf_state.stmf_lock));

        for (ilport = stmf_state.stmf_ilportlist; ilport;
            ilport = ilport->ilport_next) {
                id = (uint8_t *)ilport->ilport_lport->lport_id;
                if ((id[3] == ident_size) &&
                    bcmp(id + 4, target_ident, ident_size) == 0) {
                        return (ilport);
                }
        }
        return (NULL);
}

stmf_i_scsi_session_t *
stmf_lookup_session_for_hostident(stmf_i_local_port_t *ilport,
    uint8_t *host_ident, uint16_t ident_size)
{
        stmf_i_scsi_session_t *iss;
        uint8_t *id;

        ASSERT(mutex_owned(&stmf_state.stmf_lock));

        for (iss = ilport->ilport_ss_list; iss; iss = iss->iss_next) {
                id = (uint8_t *)iss->iss_ss->ss_rport_id;
                if ((id[3] == ident_size) &&
                    bcmp(id + 4, host_ident, ident_size) == 0) {
                        return (iss);
                }
        }
        return (NULL);
}

stmf_i_lu_t *
stmf_luident_to_ilu(uint8_t *lu_ident)
{
        stmf_i_lu_t *ilu;

        ASSERT(mutex_owned(&stmf_state.stmf_lock));

        for (ilu = stmf_state.stmf_ilulist; ilu; ilu = ilu->ilu_next) {
                if (bcmp(&ilu->ilu_lu->lu_id->ident[0], lu_ident, 16) == 0)
                        return (ilu);
        }

        return (NULL);
}

/*
 * Assert stmf_lock is already held,
 * Just get the view map for the specific target group and host group
 * tgid and hgid can not be NULL
 */
stmf_lun_map_t *
stmf_get_ve_map_per_ids(stmf_id_data_t *tgid, stmf_id_data_t *hgid)
{
        int found = 0;
        stmf_ver_tg_t *vertg;
        stmf_ver_hg_t *verhg;

        ASSERT(mutex_owned(&stmf_state.stmf_lock));

        for (vertg = stmf_state.stmf_ver_tg_head;
            vertg; vertg = vertg->vert_next) {
                if (vertg->vert_tg_ref == tgid) {
                        found = 1;
                        break;
                }
        }
        if (!found)
                return (NULL);

        for (verhg = vertg->vert_verh_list; verhg; verhg = verhg->verh_next) {
                if (verhg->verh_hg_ref == hgid) {
                        return (&verhg->verh_ve_map);
                }
        }
        return (NULL);
}

stmf_status_t
stmf_validate_lun_view_entry(stmf_id_data_t *hg, stmf_id_data_t *tg,
    uint8_t *lun, uint32_t *err_detail)
{
        char                    *phg, *ptg;
        stmf_lun_map_t          *ve_map = NULL;
        stmf_ver_hg_t           *verhg = NULL;
        stmf_ver_tg_t           *vertg = NULL;
        uint16_t                lun_num;
        stmf_status_t           ret = STMF_SUCCESS;

        ASSERT(mutex_owned(&stmf_state.stmf_lock));

        ve_map = stmf_duplicate_ve_map(0);
        for (vertg = stmf_state.stmf_ver_tg_head; vertg != NULL;
            vertg = vertg->vert_next) {
                ptg = (char *)vertg->vert_tg_ref->id_data;
                if ((ptg[0] != '*') && (tg->id_data[0] != '*') &&
                    (vertg->vert_tg_ref != tg)) {
                        continue;
                }
                for (verhg = vertg->vert_verh_list; verhg != NULL;
                    verhg = verhg->verh_next) {
                        phg = (char *)verhg->verh_hg_ref->id_data;
                        if ((phg[0] != '*') && (hg->id_data[0] != '*') &&
                            (verhg->verh_hg_ref != hg)) {
                                continue;
                        }
                        (void) stmf_merge_ve_map(&verhg->verh_ve_map, ve_map,
                            &ve_map, 0);
                }
        }

        ret = STMF_SUCCESS;
        /* Return an available lun number */
        if (lun[2] == 0xFF) {
                /* Pick a LUN number */
                lun_num = stmf_get_next_free_lun(ve_map, lun);
                if (lun_num > 0x3FFF)
                        ret = STMF_NOT_SUPPORTED;
        } else {
                lun_num = (uint16_t)lun[1] | (((uint16_t)(lun[0] & 0x3F)) << 8);
                if (stmf_get_ent_from_map(ve_map, lun_num) != NULL) {
                        *err_detail = STMF_IOCERR_LU_NUMBER_IN_USE;
                        ret = STMF_LUN_TAKEN;
                }
        }
        stmf_destroy_ve_map(ve_map);

        return (ret);
}

int
stmf_validate_lun_ve(uint8_t *hgname, uint16_t hgname_size,
    uint8_t *tgname, uint16_t tgname_size,
    uint8_t *luNbr, uint32_t *err_detail)
{
        stmf_id_data_t          *hg;
        stmf_id_data_t          *tg;
        stmf_status_t           ret;

        ASSERT(mutex_owned(&stmf_state.stmf_lock));

        hg = stmf_lookup_id(&stmf_state.stmf_hg_list, hgname_size,
            (uint8_t *)hgname);
        if (!hg) {
                *err_detail = STMF_IOCERR_INVALID_HG;
                return (ENOENT); /* could not find group */
        }
        tg = stmf_lookup_id(&stmf_state.stmf_tg_list, tgname_size,
            (uint8_t *)tgname);
        if (!tg) {
                *err_detail = STMF_IOCERR_INVALID_TG;
                return (ENOENT); /* could not find group */
        }
        ret = stmf_validate_lun_view_entry(hg, tg, luNbr, err_detail);

        if (ret == STMF_LUN_TAKEN) {
                return (EEXIST);
        } else if (ret == STMF_NOT_SUPPORTED) {
                return (E2BIG);
        } else if (ret != STMF_SUCCESS) {
                return (EINVAL);
        }
        return (0);
}