iscsit_tgt.c revision e42a0851889d583925aa3bd2d9bd139189031cb0
/*
* 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
* 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/sysmacros.h>
#include <sys/stmf_ioctl.h>
#define ISCSIT_TGT_SM_STRINGS
#include <iscsit.h>
#include <iscsit_isns.h>
typedef struct {
static void
static void
static void
static void
static void
static void
static void
static void
static void
static void
static void
static void
static void
iscsit_tgt_dereg_retry(void *arg);
static void
iscsit_tgt_dereg_task(void *arg);
static void
static iscsit_tgt_t *
static void
iscsit_tgt_unref(void *tgt);
static void
static void
static iscsit_tpgt_t *
static iscsit_tpg_t *
iscsit_tpg_lookup_locked(char *tpg_name);
static iscsit_portal_t *
struct sockaddr_storage *sa);
static idm_status_t
static void
static idm_status_t
static idm_status_t
static iscsit_tpgt_t *
static iscsit_tpgt_t *
static void
static iscsit_tpg_t *
static void
static void
static iscsit_portal_t *
static void
static idm_status_t
static void
/*
* Target state machine
*/
void
{
}
void
{
/*
* Use the target_sm_busy flag to keep the state machine single
* threaded. This also serves as recursion avoidance since this
* flag will always be set if we call iscsit_tgt_sm_event from
* within the state machine code.
*/
if (!tgt->target_sm_busy) {
(int)ctx->te_ctx_event, 0);
}
}
}
static void
{
tgt_event_ctx_t *, ctx);
/* State independent actions */
switch (ctx->te_ctx_event) {
case TE_DELETE:
break;
}
/* State dependent actions */
switch (tgt->target_state) {
case TS_CREATED:
break;
case TS_ONLINING:
break;
case TS_ONLINE:
break;
case TS_STMF_ONLINE:
break;
case TS_DELETING_NEED_OFFLINE:
break;
case TS_OFFLINING:
break;
case TS_OFFLINE:
break;
case TS_STMF_OFFLINE:
break;
case TS_DELETING_STMF_DEREG:
break;
break;
case TS_DELETING:
break;
default:
ASSERT(0);
}
}
static void
{
switch (ctx->te_ctx_event) {
case TE_STMF_ONLINE_REQ:
break;
case TE_DELETE:
break;
case TE_STMF_OFFLINE_REQ:
/*
* We're already offline but update to an equivelant
* state just to note that STMF talked to us.
*/
break;
/* Ignore */
break;
default:
ASSERT(0);
}
}
static void
{
switch (ctx->te_ctx_event) {
case TE_ONLINE_SUCCESS:
break;
case TE_ONLINE_FAIL:
break;
case TE_DELETE:
/* TE_DELETE is handled in tgt_sm_event_dispatch() */
break;
case TE_STMF_ONLINE_REQ:
case TE_STMF_OFFLINE_REQ:
/*
* We can't complete STMF's request since we are busy going
* online.
*/
break;
/* Ignore */
break;
default:
ASSERT(0);
}
}
static void
{
switch (ctx->te_ctx_event) {
if (tgt->target_deleting) {
} else {
}
break;
case TE_DELETE:
/* TE_DELETE is handled in tgt_sm_event_dispatch() */
break;
case TE_STMF_ONLINE_REQ:
case TE_STMF_OFFLINE_REQ:
/*
* We can't complete STMF's request since we are busy going
* online (waiting for acknowlegement from STMF)
*/
break;
/* Ignore */
break;
default:
ASSERT(0);
}
}
static void
{
/* Deregister target with iSNS whenever we leave this state */
switch (ctx->te_ctx_event) {
case TE_DELETE:
(void) iscsit_isns_deregister(tgt);
break;
case TE_STMF_OFFLINE_REQ:
(void) iscsit_isns_deregister(tgt);
break;
case TE_STMF_ONLINE_REQ:
/* Already online */
break;
/* Ignore */
break;
default:
ASSERT(0);
}
}
static void
{
switch (ctx->te_ctx_event) {
case TE_STMF_OFFLINE_REQ:
break;
case TE_DELETE:
/* TE_DELETE is handled in tgt_sm_event_dispatch() */
break;
case TE_STMF_ONLINE_REQ:
/*
* We can't complete STMF's request since we need to be offlined
*/
break;
/* Ignore */
break;
default:
ASSERT(0);
}
}
static void
{
switch (ctx->te_ctx_event) {
case TE_OFFLINE_COMPLETE:
break;
case TE_DELETE:
/* TE_DELETE is handled in tgt_sm_event_dispatch() */
break;
case TE_STMF_ONLINE_REQ:
case TE_STMF_OFFLINE_REQ:
/*
* We can't complete STMF's request since we are busy going
* offline.
*/
break;
/* Ignore */
break;
default:
ASSERT(0);
}
}
static void
{
switch (ctx->te_ctx_event) {
if (tgt->target_deleting) {
} else {
}
break;
case TE_DELETE:
/* TE_DELETE is handled in tgt_sm_event_dispatch() */
break;
case TE_STMF_ONLINE_REQ:
case TE_STMF_OFFLINE_REQ:
/*
* We can't complete STMF's request since we are busy going
* offline.
*/
break;
/* Ignore */
break;
default:
ASSERT(0);
}
}
static void
{
switch (ctx->te_ctx_event) {
case TE_STMF_ONLINE_REQ:
break;
case TE_DELETE:
break;
case TE_STMF_OFFLINE_REQ:
/* Already offline */
break;
/* Ignore */
break;
default:
ASSERT(0);
}
}
static void
{
/* Terminal state, no events */
switch (ctx->te_ctx_event) {
case TE_STMF_ONLINE_REQ:
case TE_STMF_OFFLINE_REQ:
/*
* We can't complete STMF's request since we are being deleted
*/
break;
/* Ignore */
break;
case TE_STMF_DEREG_SUCCESS:
break;
case TE_STMF_DEREG_FAIL:
break;
default:
ASSERT(0);
}
}
static void
{
/* Terminal state, no events */
switch (ctx->te_ctx_event) {
case TE_STMF_ONLINE_REQ:
case TE_STMF_OFFLINE_REQ:
/*
* We can't complete STMF's request since we are being deleted
*/
break;
/* Ignore */
break;
case TE_STMF_DEREG_RETRY:
break;
default:
ASSERT(0);
}
}
static void
{
/* Terminal state, no events */
switch (ctx->te_ctx_event) {
case TE_STMF_ONLINE_REQ:
case TE_STMF_OFFLINE_REQ:
/*
* We can't complete STMF's request since we are being deleted
*/
break;
/* Ignore */
break;
default:
ASSERT(0);
}
}
static void
iscsit_tgt_dereg_retry(void *arg)
{
/*
* Rather than guaranteeing the target state machine code will not
* block for long periods of time (tying up this callout thread)
* we will queue a task on the taskq to send the retry event.
* If it fails we'll setup another timeout and try again later.
*/
/* Dispatch failed, try again later */
}
}
static void
iscsit_tgt_dereg_task(void *arg)
{
}
static void
{
/*
* Validate new state
*/
switch (tgt->target_state) {
case TS_ONLINING:
if (idmrc != IDM_STATUS_SUCCESS) {
} else {
}
/*
* Let STMF know the how the online operation completed.
* STMF will respond with an acknowlege later
*/
break;
case TS_ONLINE:
break;
case TS_STMF_ONLINE:
(void) iscsit_isns_register(tgt);
break;
case TS_DELETING_NEED_OFFLINE:
break;
case TS_OFFLINING:
/* Async callback generates completion event */
break;
case TS_OFFLINE:
break;
case TS_STMF_OFFLINE:
break;
case TS_DELETING_STMF_DEREG:
if (stmfrc == STMF_SUCCESS) {
} else {
}
break;
/* Retry dereg in 1 second */
break;
case TS_DELETING:
break;
default:
ASSERT(0);
}
}
/*
* Target, TPGT, TPG utility functions
*/
{
/*
* 1. >> Lock <<
* 2. Removing deleted objects
* 3. Add deleted targets to global delete list
* 4. "delete" event to target state machine
* 5. >> Unlock <<
* 6. Create new targets, update modified targets
*/
}
}
/* Now walk through the list of configured targets */
/* See if we have an existing target */
return (ITCFG_TGT_CREATE_ERR);
} else {
}
}
/*
* Targets on the iscsit_global.global_deleted_target_list will remove
* and destroy themselves when their associated state machines reach
* the TS_DELETED state and all references are released.
*/
return (ITCFG_SUCCESS);
}
iscsit_tgt_lookup(char *target_name)
{
return (result);
}
{
/*
* Use a dummy target for lookup, filling in all fields used in AVL
* comparison.
*/
}
return (result);
}
{
/*
* Each target is an STMF local port.
*/
sizeof (iscsit_tgt_t) + sizeof (scsi_devid_desc_t) +
return (NULL);
}
/* Use pointer arithmetic to find scsi_devid_desc_t */
/* Store a shortcut to the target name */
/* Finish initializing local port */
/*
* Would like infinite timeout, but this is about as long as can
* be specified to stmf on a 32 bit kernel.
*/
/*
* Additional target modifications from config
*/
return (NULL);
}
/*
* Register the target with STMF but not until we have all the
* TPGT bindings and any other additional config setup. STMF
* may immediately ask us to go online.
*/
return (NULL);
}
return (result);
}
static idm_status_t
{
/* Merge TPGT */
if (tgt->target_props) {
}
KM_SLEEP);
/* This should never happen */
"target %s, the target modification could not be "
}
/*
* If the target is truly modified (not newly created),
* inform iSNS to update the target registration.
*/
if ((tgt->target_generation > 0) &&
}
return (idmrc);
}
void
{
}
}
void
iscsit_tgt_unref(void *tgt_void)
{
}
void
{
}
static void
{
/*
* Destroy all target portal group tags
*/
}
if (tgt->target_props) {
}
/*
* Destroy target
*/
}
void
{
}
void
{
}
int
{
int result;
/*
* Sort by ISID first then TSIH
*/
if (result < 0) {
return (-1);
} else if (result > 0) {
return (1);
}
return (0);
}
{
return (result);
}
static iscsit_tpgt_t *
{
/* Caller holds tgt->target_mutex */
NULL) {
}
return (result);
}
{
/* Caller holds tgt->target_mutex */
if (portal) {
*output_tpgt = tpgt;
return (portal);
}
}
return (NULL);
}
void
{
if (tgt) {
} else {
/* Discovery session */
}
}
void
{
if (tgt) {
} else {
/* Discovery session */
}
}
#define LOCK_FOR_SESS_LOOKUP(lookup_tgt) { \
if ((lookup_tgt) == NULL) { \
} else { \
} \
}
#define UNLOCK_FOR_SESS_LOOKUP(lookup_tgt) { \
if ((lookup_tgt) == NULL) { \
ISCSIT_GLOBAL_UNLOCK(); \
} else { \
} \
}
{
/*
* If tgt is NULL then we are looking for a discovery session
*/
} else {
}
return (NULL);
}
/*
* We'll try to find a session matching ISID + TSIH first. If we
* can't find one then we will return the closest match. If the
* caller needs an exact match it must compare the TSIH after
* the session is returned.
*
* The reason we do this "fuzzy matching" is to allow matching
* sessions with different TSIH values on the same AVL list. This
* makes session reinstatement much easier since the new session can
* live on the list at the same time as the old session is cleaning up.
*/
return (result);
}
/*
* avl_find_nearest() may return a result with a different ISID so
* we should only return a result if the name and ISID match
*/
return (result);
}
return (result);
}
return (NULL);
}
static idm_status_t
{
/*
* 1. >> Lock <<
* 2. Removing all objects and place on a temp list
* 3. Add new objects
* 4. >> Unlock <<
* 5. tpgt_del_list contains deleted objects
*/
(tpgt_del_list != NULL));
if (tpgt_del_list) {
}
}
}
}
} else {
/* Add currently defined TPGTs */
goto tpgt_merge_error;
}
}
}
return (IDM_STATUS_SUCCESS);
/*
* This is a problem in configuration we received so it should never
* happen. That's good because we're probably no longer in a state
* requested by the user -- all the unbind operations have already
* taken place.
*/
return (IDM_STATUS_FAIL);
}
static iscsit_tpgt_t *
{
/* This takes a reference on the TPG */
return (NULL);
return (result);
}
{
return (result);
}
void
{
if (tpgt->tpgt_needs_tpg_offline) {
}
}
void
{
}
void
{
}
int
{
return (-1);
return (1);
return (0);
}
static idm_status_t
{
if (rc != IDM_STATUS_SUCCESS) {
goto tgt_online_fail;
}
}
return (IDM_STATUS_SUCCESS);
/* Offline all the tpgs we successfully onlined up to the failure */
}
return (rc);
}
static void
iscsit_tgt_offline_cb(void *tgt_void)
{
}
static void
{
/* Offline target portal groups */
}
/* Close any active sessions */
/*
* This is not a synchronous operation but after all
* sessions have been cleaned up there will be no
* more session-related holds on the target.
*/
}
/*
* Wait for all the sessions to quiesce.
*/
}
{
/*
* 1. >> Lock <<
* 2. Removing deleted objects and place on a temp list
* 3. Add new objects
* 4. >> Unlock <<
* 5. tpg_del_list contains objects to destroy
*/
/*
* The policy around when to allow a target portal
* group to be deleted is implemented in libiscsit.
* By the time the request gets to the kernel module
* we expect that it conforms to policy so we will
* cleanup all references to TPG and destroy it if it
* is possible to do so.
*
*/
}
}
/* Now walk through the list of configured target portal groups */
/* See if we have an existing target portal group */
} else {
}
}
return (ITCFG_SUCCESS);
}
void
{
/* Now finish destroying the target portal groups */
/* Kill it */
}
}
iscsit_tpg_lookup(char *tpg_name)
{
return (result);
}
static iscsit_tpg_t *
iscsit_tpg_lookup_locked(char *tpg_name)
{
}
return (result);
}
{
return (tpg);
}
static void
{
/* Update portals */
portal = next_portal) {
}
}
cfg_portal != NULL;
(void) iscsit_portal_create(tpg,
} else {
}
}
}
void
{
portal = next_portal) {
}
}
void
{
}
void
{
}
{
/* Now create default portal */
return (NULL);
}
return (tpg);
}
void
{
}
int
{
int result;
/*
* Sort by ISID first then TSIH
*/
if (result < 0) {
return (-1);
} else if (result > 0) {
return (1);
}
return (0);
}
{
if (tpg->tpg_online == 0) {
if (rc != IDM_STATUS_SUCCESS) {
goto tpg_online_fail;
}
}
}
tpg->tpg_online++;
return (IDM_STATUS_SUCCESS);
/* Offline all the portals we successfully onlined up to the failure */
portal != portal_fail;
}
return (rc);
}
void
{
tpg->tpg_online--;
if (tpg->tpg_online == 0) {
}
}
}
{
return (result);
}
static iscsit_portal_t *
struct sockaddr_storage *sa)
{
/* Caller holds tpg->tpg_mutex */
NULL) {
}
return (result);
}
{
/*
* If (sa == NULL) then we are being asked to create the default
* portal -- targets will use this portal when no portals are
* explicitly configured.
*/
} else {
}
/*
* Add this portal to the list
*/
return (portal);
}
void
{
}
void
{
}
void
{
}
int
{
int i;
/*
* Compare ports, then address family, then ip address
*/
return (1);
else
return (-1);
}
/*
* ports are the same
*/
return (1);
else
return (-1);
}
/*
* address families are the same
*/
return (1);
return (-1);
else
return (0);
for (i = 0; i < 4; i++) {
return (1);
return (-1);
}
return (0);
} else
"iscsit_portal_avl_compare: unknown ss_family %d",
return (1);
}
{
struct sockaddr_in *sin;
/* Caller holds parent TPG mutex */
if (portal->portal_online == 0) {
/*
* If there is no existing IDM service instance for this port,
* create one. If the service exists, then the lookup,
* creates a reference on the existing service.
*/
if (port == 0)
return (IDM_STATUS_FAIL);
}
/* Get reference on the service we just created */
}
return (IDM_STATUS_FAIL);
}
/*
* Only call iSNS for first online
*/
}
portal->portal_online++;
return (rc);
}
void
{
portal->portal_online--;
if (portal->portal_online == 0) {
/*
* Only call iSNS for last offline
*/
/* If service is unreferenced, destroy it too */
}
}
{
/*
* Initiator objects are so simple we will just destroy all the current
* objects and build new ones. Nothing should ever reference an
* initator object.. instead just lookup the initiator object and
* grab the properties while holding the global config lock.
*/
}
KM_SLEEP);
}
return (ITCFG_SUCCESS);
}
int
{
int result;
/*
* Sort by ISID first then TSIH
*/
if (result < 0) {
return (-1);
} else if (result > 0) {
return (1);
}
return (0);
}
iscsit_ini_lookup_locked(char *ini_name)
{
/*
* Use a dummy target for lookup, filling in all fields used in AVL
* comparison.
*/
return (result);
}