view usr/src/uts/common/io/comstar/stmf/lun_map.c @ 10691:36de6b202f7e

6870543 stmfAddViewEntry() could use a validate-only mode
author tim szeto <Tim.Szeto@Sun.COM>
date Wed, 30 Sep 2009 10:02:47 -0600
parents 81f4d72ae348
children cd144ed668c5
line wrap: on
line source

/*
 * 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 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#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 "stmf.h"
#include "lpif.h"
#include "portif.h"
#include "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);
stmf_status_t stmf_add_ent_to_map(stmf_lun_map_t *sm, void *ent, uint8_t *lun);
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
 */
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;

	if (iss->iss_sm != NULL)
		cmn_err(CE_PANIC, "create lun map called with non NULL map");
	iss->iss_sm = (stmf_lun_map_t *)kmem_zalloc(sizeof (stmf_lun_map_t),
	    KM_SLEEP);
	mutex_enter(&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 */

	mutex_exit(&stmf_state.stmf_lock);
	stmf_destroy_ve_map(ve_map);

	return (STMF_SUCCESS);
}

/*
 * destroy lun map for session
 */
/* ARGSUSED */
stmf_status_t
stmf_session_destroy_lun_map(stmf_i_local_port_t *ilport,
		stmf_i_scsi_session_t *iss)
{
	stmf_lun_map_t *sm;
	stmf_i_lu_t *ilu;
	uint16_t n;
	stmf_lun_map_ent_t *ent;

	/*
	 * to avoid conflict with updating session's map,
	 * which only grab stmf_lock
	 */
	mutex_enter(&stmf_state.stmf_lock);
	sm = iss->iss_sm;
	iss->iss_sm = NULL;
	iss->iss_hg = NULL;
	mutex_exit(&stmf_state.stmf_lock);
	if (sm->lm_nentries) {
		for (n = 0; n < sm->lm_nentries; n++) {
			if ((ent = (stmf_lun_map_ent_t *)sm->lm_plus[n])
			    != NULL) {
				if (ent->ent_itl_datap) {
					stmf_do_itl_dereg(ent->ent_lu,
					    ent->ent_itl_datap,
					    STMF_ITL_REASON_IT_NEXUS_LOSS);
				}
				ilu = (stmf_i_lu_t *)
				    ent->ent_lu->lu_stmf_private;
				atomic_add_32(&ilu->ilu_ref_cnt, -1);
				kmem_free(sm->lm_plus[n],
				    sizeof (stmf_lun_map_ent_t));
			}
		}
		kmem_free(sm->lm_plus,
		    sizeof (stmf_lun_map_ent_t *) * sm->lm_nentries);
	}

	kmem_free(sm, sizeof (*sm));
	return (STMF_SUCCESS);
}

/*
 * Expects the session lock to be 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;
	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;

	for (i = 0; ((i < sm->lm_nentries) && (ent < nluns)); i++) {
		if (sm->lm_plus[i] == NULL)
			continue;
		/* Fill in the entry */
		xd->buf[8 + (ent << 3) + 1] = (uchar_t)i;
		xd->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
 */
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_add_32(&ilu->ilu_ref_cnt, 1);
	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
 */
/* ARGSUSED */
stmf_status_t
stmf_remove_lu_from_session(stmf_i_local_port_t *ilport,
		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 && lun_map_ent->ent_lu == lu);

	ilu = (stmf_i_lu_t *)lu->lu_stmf_private;

	ret = stmf_remove_ent_from_map(sm, lu_nbr);
	ASSERT(ret == STMF_SUCCESS);
	atomic_add_32(&ilu->ilu_ref_cnt, -1);
	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));
	return (STMF_SUCCESS);
}

/*
 * 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 */
				(void) stmf_remove_lu_from_session(ilport, 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 */
void
stmf_remove_lus_from_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_remove_lu_from_session(ilport, 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_add_32(&idlist->id_count, 1);
}

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_add_32(&idlist->id_count, -1);
}


/*
 * 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_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));
}

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);
}

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;
	}
}


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);
}

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);
}

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);
	}
	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;
			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);
		}
	}

	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;
			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(
					    ilport, iss, vemap);
			}
			if (vemap_alltgt)
				stmf_remove_lus_from_session_per_vemap(ilport,
				    iss, vemap_alltgt);
		}
	}

	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);
}