t10_sam.c revision a9fd9a9e12bea66c9ea9b31f4b6f0ef584933f59
/*
* 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 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <aio.h>
#include <stdio.h>
#include <stddef.h>
#include <strings.h>
#include <pthread.h>
#include <fcntl.h>
#include <assert.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <sys/ucontext.h>
#include <libzfs.h>
#include <assert.h>
#include <umem.h>
#include <time.h>
#include "target.h"
#include "queue.h"
#include "t10.h"
#include "t10_spc.h"
#include "utility.h"
#include "mgmt_scf.h"
/*
* []------------------------------------------------------------------[]
* | This file contains methods which isolate a transport from device |
* | emulation. The first part of the file contains method which are |
* | called by the transport to start commands or deliver data. The |
* | transport does not know anything about what emulation is being |
* | done. The emulation layer receieves cdb's and nows nothing about |
* | the transport. This is how it should be. There are a few special |
* | cases to deal with transports which have a notion of immediate |
* | data, but we're isolating that from the emulation layer. |
* []------------------------------------------------------------------[]
*/
/*
* Forward declarations
*/
static void *lu_runner(void *v);
static void *t10_aio_done(void *v);
static void clear_transport(transport_t t);
#ifdef FULL_DEBUG
static char *state_to_str(t10_cmd_state_t s);
#endif
static char *event_to_str(t10_cmd_event_t e);
/* ---- These are AVL comparison routines ---- */
static sam_device_table_t sam_emul_table[];
/*
* Local variables
*/
static avl_tree_t lu_list;
static pthread_mutex_t lu_list_mutex;
static int lu_id;
static pthread_mutex_t t10_mutex;
static int t10_num;
/*
* Constants
*/
/*
* []----
* | t10_init -- called once at the beginning of time to initialize globals
* []----
*/
void
{
mgmtq = q;
}
/*ARGSUSED*/
static void *
t10_aio_done(void *v)
{
t10_aio_t *a;
do {
"SAM- sema_wait returned error\n");
continue;
}
"SAM- aiowait returned EINVAL\n");
/*
* It's possible for aiowait to return
* prematurely. So, post another semaphore,
* nanosleep for a usec, and try the wait again.
*/
continue;
} else
break;
} else {
}
(*a->a_aio_cmplt)(a->a_id);
} else
} else {
"SAM aiowait returned results, but is NULL\n");
}
/*CONSTANTCONDITION*/
} while (1);
return (NULL);
}
/*
* []------------------------------------------------------------------[]
* | Methods called by transports to interface with SAM-3 |
* []------------------------------------------------------------------[]
*/
/*
* []----
* | t10_handle_create -- Create the I_T nexus
* |
* | NOTES:
* | max_out can be set to 0 if the transport wishes to wait for all of
* | the data before receiving a DATAOUT message. Fibre Channel will most
* | likely set this to 0, whereas iSCSI will set max_out to the value
* | of MaxRecvDataSegment.
* | (*datain_cb)() is called, on the LU thread, when the emulation
* | module needs data *and* t10_send_cmd was called with opt_data_len, but
* | no opt_data.
* []----
*/
{
if (t == NULL)
return (NULL);
(void) pthread_mutex_lock(&t10_mutex);
t->s_targ_num = t10_num++;
(void) pthread_mutex_unlock(&t10_mutex);
t->s_trans_vers = trans_vers;
t->s_to_transport = tq;
t->s_dataout_cb = datain_cb;
/*
* Once we actually support two or more transports it would be
* possible for a collision between the underlying transports
* target port group values since one wouldn't necessarily know
* anything about the other. We'll use the upper bits of the
* target port group value to separate them.
* If we were to support many transports and with one then running
* out of bit space we'd need to change the allocation method. Since
* these values aren't stored anywhere and just used by initiators
* to determine relative path numbering there's no issue with changing
* this later if need be.
*/
switch (trans_vers) {
case T10_TRANS_ISCSI:
break;
case T10_TRANS_FC:
break;
}
return ((t10_targ_handle_t)t);
}
void
{
t10_lu_impl_t *l;
int lu_per_targ = 0;
(void) pthread_mutex_lock(&t->s_mutex);
if (avl_numnodes(&t->s_open_lu) != 0) {
s.t_q = queue_alloc();
while (l != NULL) {
s.t_lu = l;
msg_shutdown, (void *)&s);
lu_per_targ++;
}
"SAM%x Sent %d shutdown requests for %s\n",
}
(void) pthread_mutex_unlock(&t->s_mutex);
}
void
{
t10_lu_impl_t *l;
t10_cmd_t *c;
int fast_free = 0;
(void) pthread_mutex_lock(&t->s_mutex);
if (avl_numnodes(&t->s_open_lu) != 0) {
avl_remove(&t->s_open_lu, l);
(void) pthread_mutex_lock(&l->l_cmd_mutex);
if (avl_numnodes(&l->l_cmds) != 0) {
while (c != NULL) {
c2free = c;
/*
* Only remove those commands which
* are waiting for a response from
* the initiator which will not
* arrive since we're shutting down
* the connection or have already
* been canceled by the transport.
* Other commands will be freed as
* they are processed by the
* transport layer or AIO.
*/
T10_Cmd_S5_Wait) ||
fast_free++;
}
}
"SAM%x FastFree %d ... "
"Waiting for %d cmds to drain\n",
t->s_targ_num, fast_free,
avl_numnodes(&l->l_cmds));
if (avl_numnodes(&l->l_cmds) != 0) {
l->l_wait_for_drain = True;
while (l->l_wait_for_drain == True) {
(void) pthread_cond_wait(
&l->l_cmd_cond,
&l->l_cmd_mutex);
}
"SAM%x Commands drained\n",
t->s_targ_num);
}
}
(void) pthread_mutex_unlock(&l->l_cmd_mutex);
avl_destroy(&l->l_cmds);
free(l);
}
}
avl_destroy(&t->s_open_lu);
(void) pthread_mutex_unlock(&t->s_mutex);
free(t->s_targ_base);
free(t);
}
/*
* []----
* | t10_cmd_create -- creates a command pointer
* |
* | If an error occurs, a sense condition buffer will be created that can
* | be sent back to the initiator. The only time this should occur is during
* | LU setup and we've run out of resources like not having enough file
* | descriptors to open the backing store. If the cmdp is NULL, then there's
* | not even enough memory to create a command buffer and the transport
* | should shutdown it's connection a cleanly as possible.
* []----
*/
{
if (t == NULL)
goto error;
goto error;
goto error;
goto error;
return (True);
}
/*
* If we haven't set up the argument pointer, then free the memory
* that had been allocated to the command.
*/
return (False);
}
/*
* []----
* | t10_send_cmd -- send the given command to appropriate LUN emulation
* |
* | NOTE: emul_id is only provided for DATA_OUT commands (write ops)
* | which have multiple phases to complete the request. The emulation
* | module will provide this value when it requests more data to be
* | sent.
* []----
*/
/*ARGSUSED*/
{
return (False);
return (True);
}
/*ARGSUSED*/
{
return (False);
return (True);
}
/*
* t10_cmd_state_machine -- State machine for T10 commands
*
* S1: Free - State on instantiation, or after successful
* completion of command
* S2: In - The command is currently being processed
* by the lu_runner() thread. Memory associated
* with the command must not be freed. Can't
* transition directly to Free state from threads
* other than lu_runner().
* S3: Trans - Command has been handed off to transport layer
* S4: AIO - Command has been sent to AIO subsystem for
* further processing.
* S5: Wait - Waiting for response from Initiator.
* S6: Freeing - Someone other than the lu_runner() has requested
* that the command be freed and the state was
* either S2 or S4.
*
* The state transition table is as follows:
*
* +-----+---+---+---+---+---+
* |S1 |S2 |S3 |S4 |S5 |S6 |
* ---+-----+---+---+---+---+---+
* S1|T5 |T1 | - | - | - | - |
* ---+-----+---+---+---+---+---+
* S2|T5 | - |T2 |T3 | - |T6 |
* ---+-----+---+---+---+---+---+
* S3|T5/6 |T4 | - | - |T7 | |
* ---+-----+---+---+---+---+---+
* S4| |T4 | | | |T6 |
* ---+-----+---+---+---+---+---+
* S5|T6 | - |T4 | - | - | - |
* ---+-----+---+---+---+---+---+
* S6|T2-4 | | | | | |
* ---+-----+---+---+---+---+---+
*
* Events definitions:
* -T1: Command has been placed on LU queue for exection.
* -T2: Emulation completed to a point where the transport must
* take over and send data or CDB response out.
* -T3: Emulation requires data from storage subsystem via asynchronous
* I/O.
* -T4: One of the following events has caused the transition:
* - Response from initiator to R2T request.
* - Transport has data available to complete dataout request from T10.
* -T5: Command complete. Free resources.
* -T6: Cancel command.
* -T7: Transport has sent command to Initiator.
*/
static t10_cmd_state_t
{
/* ---- Callers must already hold the mutex ---- */
switch (c->c_state) {
case T10_Cmd_S1_Free:
switch (e) {
case T10_Cmd_T1:
c->c_state = T10_Cmd_S2_In;
0, msg_cmd_send, (void *)c);
break;
case T10_Cmd_T5:
c->c_state = T10_Cmd_S1_Free;
cmd_common_free(c);
return (T10_Cmd_S1_Free);
default:
"Illegal event %s on %llx\n", event_to_str(e),
c->c_trans_id);
assert(0);
}
break;
case T10_Cmd_S2_In:
switch (e) {
case T10_Cmd_T2:
c->c_state = T10_Cmd_S3_Trans;
c->c_msg, (void *)c);
break;
case T10_Cmd_T3:
c->c_state = T10_Cmd_S4_AIO;
break;
case T10_Cmd_T5:
c->c_state = T10_Cmd_S1_Free;
cmd_common_free(c);
return (T10_Cmd_S1_Free);
case T10_Cmd_T6:
c->c_state = T10_Cmd_S6_Freeing;
break;
default:
"SAM: Illegal event %s on %llx\n",
event_to_str(e), c->c_trans_id);
assert(0);
}
break;
case T10_Cmd_S3_Trans:
switch (e) {
case T10_Cmd_T4:
c->c_state = T10_Cmd_S2_In;
msg_cmd_data_out, (void *)c);
break;
case T10_Cmd_T5:
/*FALLTHRU*/
case T10_Cmd_T6:
c->c_state = T10_Cmd_S1_Free;
cmd_common_free(c);
return (T10_Cmd_S1_Free);
case T10_Cmd_T7:
c->c_state = T10_Cmd_S5_Wait;
break;
default:
"Illegal event %s -- %llx\n", event_to_str(e),
c->c_trans_id);
assert(0);
}
break;
case T10_Cmd_S4_AIO:
switch (e) {
case T10_Cmd_T4:
c->c_state = T10_Cmd_S2_In;
break;
case T10_Cmd_T6:
c->c_state = T10_Cmd_S6_Freeing;
break;
default:
"Illegal event %s -- %llx\n", event_to_str(e),
c->c_trans_id);
assert(0);
}
break;
case T10_Cmd_S5_Wait:
switch (e) {
case T10_Cmd_T4:
c->c_state = T10_Cmd_S3_Trans;
break;
case T10_Cmd_T6:
c->c_state = T10_Cmd_S1_Free;
cmd_common_free(c);
return (T10_Cmd_S1_Free);
default:
"Illegal event %s -- %llx\n", event_to_str(e),
c->c_trans_id);
assert(0);
}
break;
case T10_Cmd_S6_Freeing:
switch (e) {
case T10_Cmd_T2:
case T10_Cmd_T3:
case T10_Cmd_T4:
c->c_state = T10_Cmd_S1_Free;
cmd_common_free(c);
return (T10_Cmd_S1_Free);
default:
"Illegal event %s -- %llx\n", event_to_str(e),
c->c_trans_id);
assert(0);
}
break;
default:
assert(0);
}
return (c->c_state);
}
void
{
/*
* Since the transport may or may not have called into the T10 layer
* to allocate a command it's possible that this will be NULL. Instead
* of requiring every caller of this function to first check if the
* command pointer is null we'll do the check here.
*/
if (c == NULL)
return;
/*
* If t10_cmd_create() fails for some reason other than lack
* of memory the extended status will be set for the transport
* to send out. There will not be any LU associated with this
* command, but the transport will still try to free it.
*/
if (!lu) {
assert(e == T10_Cmd_T5);
cmd_common_free(c);
return;
}
(void) t10_cmd_state_machine(c, e);
}
/*
* []----
* | t10_task_mgmt -- handle SAM-3 task management needs
* []----
*/
/*ARGSUSED*/
{
switch (op) {
case InventoryChange:
do {
/*CSTYLED*/
0, msg_targ_inventory_change, (void *)lu);
}
return (True);
case ResetTarget:
do {
/*CSTYLED*/
return (True);
} else
return (False);
case ResetLun:
NULL) {
return (True);
} else
return (False);
break;
case CapacityChange:
NULL) {
return (True);
} else
return (False);
break;
default:
return (False);
}
}
/*
* []----
* | t10_targ_stat -- Return stats on each LU associated with target.
* []----
*/
void
{
char lb[32];
char *p;
/*
* It's possible for the management interfaces to request stats
* even though a connection is not up and running.
*/
if (t == NULL)
return;
while (itl) {
case lu_online:
p = TGT_STATUS_ONLINE;
break;
case lu_offline:
p = TGT_STATUS_OFFLINE;
break;
case lu_errored:
p = TGT_STATUS_ERRORED;
break;
}
}
}
/*
* []----
* | t10_thick_provision -- fill the backing store with real blocks
* |
* | The backing store is initially created as a hole-y file. The only
* | thing wrong with leaving the files hole-y is that if a system
* | administrator over provisions the storage at some point a client
* | will attempt to write to a block and receive an error unless the
* | administrator adds more backing store before that event. Now, depending
* | on the client a write error isn't fatal. However, for file systems
* | like UFS and ZFS, they can not currently deal with getting a write
* | error when it's their metadata and panic. That's not good. The concept
* | of "Thin Provisioning" is relatively new so we'll normally preallocate
* | the space, but have the option of doing the "Thin Provisioning".
* []----
*/
{
diskaddr_t offset = 0;
char path[MAXPATHLEN];
char *local_name;
tgt_node_t *n1;
/*
* To guarantee that everything has been setup correctly
* we'll just use the standard interfaces. Otherwise we'd need
* to duplicate the code and therefore offer the chance of
* in another. Obvious right?
*/
lun);
return (False);
}
lun);
goto error;
}
/*
* Attempt to see if there is enough space currently for the LU.
* The initialization might still fail with out of space because someone
* else is consuming space while the initialization is occuring.
* Nothing we can do about that.
*/
lun);
goto error;
lun);
goto error;
}
False) {
/*
* The lu_runner will use this buffer to copy data.
*/
goto error;
/*CSTYLED*/
msg_thick_provo, (void *)cmd);
while ((m = queue_message_get(q)) != NULL) {
switch (m->msg_type) {
case msg_thick_provo:
/*
* An error occurred during
* initialization which mean we
* need to remove this target.
*/
"STE%x received data "
"error at 0x%llx\n", lun,
offset);
goto error;
}
break;
case msg_shutdown:
"---- Thick provo got shutdown\n");
continue; /* don't use break */
default:
assert(0);
}
break;
}
}
} else {
lun);
}
/*
* A forced shutdown is still considered a successful completion.
* Write errors and malloc failures constitute a failure.
*/
/* ---- Completed successfully ---- */
/*
* Now that the initialization is complete, update the params
* file to indicate the status is online. Once done, send a
* message to the LU thread indicating same.
*/
lun);
XML_ELEMENT_STATUS)) == NULL) {
"STE%x couldn't find <status>\n", lun);
goto error;
}
TGT_STATUS_ONLINE) == False) {
"STE%x Could update <status> to online\n", lun);
goto error;
}
if (local_name == NULL)
goto error;
lun);
msg_lu_online, 0);
}
}
if (t != NULL) {
}
}
return (rval);
}
/*
* []------------------------------------------------------------------[]
* | Methods called by emulation modules to interface with SAM-3 |
* []------------------------------------------------------------------[]
*/
/*
* trans_cmd_dup -- Duplicate a T10 command buffer
*
* During read operations with transports that restrict transfer sizes the
* emulation code has two options.
* (1) It could transfer a chunk of data and wait until the
* transport has sent that out. Notification coming through
* the callback mechanism. If the command structure is not
* duplicated it would need to wait since the command structure
* contains the data pointer and offset values which the transport
* needs.
* (2) Use this routine to duplicate the command structure such
* that the emulation layer can send all of the data in chunks
* without waiting.
* For obvious performance reasons it's best to send all of the chunks
* without waiting.
*
* It's expected that the emulation layer will not call this routine for the
* last outgoing packet since the command structure will not be of futher
* use.
*/
{
t10_cmd_t *c;
return (False);
free(c);
return (False);
}
c->c_state = T10_Cmd_S2_In;
return (c);
}
/*
* []----
* | trans_send_datain -- send data to transport
* |
* | NOTES:
* | (1) offset is only valid when a transport has set max_out to a non-zero
* | value.
* | (2) The emulation code must free the memory, if it was allocated, when
* | the transport is finished with it. The callback routine is used
* | to provide the emulation code the notification. The callback will
* | not be run on the same thread as the emulation code so appropriate
* | locking may be required by the emulation code.
* | (3) If the boolean 'last' is True it means that the transport can
* | assume the data out is finished with a CMD_SUCCESS and no futher
* | communication from the emulation layer will occur.
* []----
*/
{
#ifdef FULL_DEBUG
"SAM%x LUN%d DataIn 0x%x, offset 0x%x, Last %s\n",
#endif
c->c_emul_complete = callback;
c->c_data_len = data_len;
c->c_msg = msg_cmd_data_in;
return (True);
}
/*
* []----
* | trans_rqst_dataout -- Request data from transport for command
* |
* | If the transport has indicated that data is immediately available,
* | which is common for iSCSI, then we'll copy that data into the buffer
* | and call the emulation modules datain function directly.
* []----
*/
{
/*
* Transport supports immediate data on writes. Currently
* on the iSCSI protocol has this feature.
* XXX Should all of this be done in the transport?
*/
if (cmd->c_data_len) {
#ifdef FULL_DEBUG
"SAM%x LUN%d DataOut rqst w/ immed, data_len 0x%x\n",
#endif
/*
* When there's data available, but no buffer it
* means the transport has decided to leave the
* data on the socket and will read it in
* when called.
*/
&max_xfer);
} else {
/*
* The data is already in the command buffer so
* we need to copy it out.
*/
data_len);
/*
* It's expected since the transport allocated
* the space, this routine will free the memory
* instead.
*/
&max_xfer);
}
cmd->c_data_len = 0;
return (True);
}
#ifdef FULL_DEBUG
"SAM%x LUN%d DataOut Rqst data_len 0x%x\n",
#endif
/*
* Short cut. There's no reason to call the transport if the
* emulation code hasn't requested any data. If that's the
* case just call the emulation codes data function.
*/
if (data_len == 0)
else {
}
return (True);
}
/*
* []----
* | trans_send_complete -- notify transport command has finished.
* |
* | This routine is called either for when the emulation has completed
* | a command which doesn't have a data in phase so we can't use the 'last'
* | flag or there's been an error.
* | The sense data is expected to be created by calling spc_create_sense(),
* | the memory for that sense data will be freed when the transport calls
* | t10_destroy_cmd().
* |
* | NOTE [1]: If the t10_status equals STATUS_BUSY the command queue for this
* | ITL will be examined. If there are commands in progress the status will
* | be changed to STATUS_QFULL
* |
* | NOTE [2]: Do not access 'cmd' after calling this function. The transport
* | may receive the command, act on it, and then call
* | t10_cmd_shoot_state(cmd, T10_Cmd_T5) before this function returns
* | thereby allowing 'cmd' to be freed and the space reallocated.
* []----
*/
void
{
#ifdef FULL_DEBUG
struct scsi_extended_sense e;
#endif
/*
* XXX Get the exact chapter and verse from the T10 documents.
* translate a STATUS_BUSY to STATUS_QFULL if there are outstanding
* commands in the queue.
*/
if ((t10_status == STATUS_BUSY) &&
}
cmd->c_data_len = 0;
#ifdef FULL_DEBUG
if (t10_status != STATUS_GOOD) {
"SAM%x LUN%d key_sense=0x%x, "
"ASC=0x%x, ASCQ=0x%x\n",
} else {
"SAM%x LUN%d key_sense=0x%x\n",
}
}
#endif
}
void
{
} else {
}
}
void
{
} else {
}
}
/*
* []----
* | trans_params_area -- return dtype params using a command pointer
* |
* | Lock down the ITL structure from change so that we can cleanly access
* | the params area. This is needed to deal with the transport closing
* | a connection while commands are in flight. When those commands finish
* | cleanup work needs to be done. Yet, the logical unit common area
* | can already be released since it doesn't know there's something to wait
* | for.
* []----
*/
void *
{
void *p = NULL;
return (p);
}
/*
* []------------------------------------------------------------------[]
* | Support routines for Routing and Task Management |
* []------------------------------------------------------------------[]
*/
/*
* []----
* | t10_find_lun -- Locate a per target LUN structure
* |
* | Finds per I_T_L structure. If this is the first time that this structure
* | has been accessed we allocate the structure and add it to the global
* | LUN structure. If that structure has never been accessed before it is
* | created along with a thread to handle the queue.
* []----
*/
/*ARGSUSED*/
static Boolean_t
{
t10_lu_impl_t *l = NULL;
char *str;
char *local_name = NULL;
tgt_node_t *n = NULL;
tgt_node_t *n1;
tgt_node_t *ll;
xmlTextReaderPtr r = NULL;
char path[MAXPATHLEN];
int xml_fd = -1;
/*
* Only l_num is used by the AVL search routines so that's
* the only thing we'll set.
*/
/*
* This should be the normal fast path. At some point it
* might be good to look at optimizing this even more.
* If we know for example that the LUN numbers are sequential
* and there's fewer than 64 an array of pointers would be
* even faster than an AVL tree and not take up to much space.
*/
return (True);
}
/*
* First access for this I_T_L so we need to allocate space for it.
*/
return (False);
}
/*
* Initialize the various local fields. Certain fields will not be
* initialized until we've got the common LUN pointer.
*/
l->l_wait_for_drain = False;
l->l_to_transport = t->s_to_transport;
l->l_targ = t;
l->l_targ_lun = lun;
!= NULL) {
break;
} else if (str) {
}
}
if (local_name == NULL)
goto error;
goto error;
n = NULL;
break;
}
if (n == NULL) {
/* ---- ACCESS DENIED - INVALID LU IDENTIFIER ---- */
goto error;
}
/*
* Set the targ variable back to NULL to indicate that
* we don't have an incore copy of the information.
* If the guid is currently 0, we'll update that value
* If the guid is currently 0, we'll update that value
* and update the ZFS property if targ is not NULL.
* Otherwise will update parameter file.
*/
/*
* To locate the common LUN structure we need to find the GUID
* for this LUN. That's the only parsing this section of code
* will do to the params file.
*/
okay_to_free = True;
(void) pthread_mutex_unlock(&lu_list_mutex);
goto error;
}
} else
(void) pthread_mutex_unlock(&lu_list_mutex);
goto error;
}
(void) pthread_mutex_unlock(&lu_list_mutex);
goto error;
}
(void) pthread_mutex_unlock(&lu_list_mutex);
goto error;
}
goto error;
NULL) {
goto error;
}
if (zfs_prop_set(zfsh,
str) != 0) {
goto error;
}
(void) pthread_mutex_unlock(&lu_list_mutex);
goto error;
}
}
(void) pthread_mutex_unlock(&lu_list_mutex);
goto error;
}
/*
* See if the common LUN for this GUID already exists.
*/
wc = 0;
/*
* The GUID wasn't found, so create a new LUN structure
* and thread.
*/
(void) pthread_mutex_unlock(&lu_list_mutex);
goto error;
}
n = NULL;
t->s_targ_base);
"SAM%x FAILED to initialize LU %d\n",
t->s_targ_num, lun);
(void) pthread_mutex_unlock(&lu_list_mutex);
goto error;
}
sizeof (t10_lu_impl_t),
(void *)common);
"SAM%x LU[%d.%d] Created new LU thread 0x%x\n",
} else {
/*
* If there's a common LU structure already we free
* the guid which was created for the search. If an error
* occurs the guid space will be freed in the error handling
* code. If a new LU is created though we don't free the guid
* since the LU needs the information.
*/
/*
* A similar condition exists with the xml tree. If there's
* already a common LU then this node *may* have been created
* here if it's not a ZVOL. If it is a ZVOL tree then it will
* have the same address as that found in l_root so don't
* free it.
*/
if (okay_to_free == True) {
tgt_node_free(n);
n = NULL;
}
"SAM%x Found existing LU[%d.%d]\n", t->s_targ_num,
}
(void) pthread_mutex_unlock(&lu_list_mutex);
/*
* Now add this I_T_L to the targets list of open LUNs so that
* in the future we can get access through the AVL tree.
* We wait to add the LU to the target list until now so that we don't
* have to delete the node in case an error occurs.
*/
(void) pthread_mutex_lock(&l->l_mutex);
(void) pthread_mutex_unlock(&l->l_mutex);
/*
* The common LU thread is responsible for filling in the command
* functions and table.
*/
return (True);
if (guid)
if (xml_fd != -1)
if (r) {
}
if (n)
tgt_node_free(n);
if (l)
free(l);
if (common)
if (dataset)
return (False);
}
static Boolean_t
{
int dtype;
return (False);
dtype++) {
str) == 0) {
== False)
goto error;
else
break;
}
}
} else
goto error;
return (True);
return (False);
}
/*
* []----
* | lu_runner -- The workhorse for each LU
* |
* | This routine is the guts of the Task Router and Task Set for SAM-3.
* []----
*/
static void *
lu_runner(void *v)
{
msg_t *m;
char *data;
char *path;
void *provo_err;
t10_shutdown_t *s;
t10_aio_t *a;
switch (m->msg_type) {
case msg_cmd_send:
/*
* Clear out the per LU values before
* calling trans_send_complete(). It's
* possible for the transport to handle
* this command and free it before returning.
*/
} else {
}
break;
case msg_cmd_data_out:
/*
* We clear the c_data_len here because if the
* emulation routine processes the data and still
* needs more it will call trans_rqst_datain()
* which will look at c_data_len to see if there
* was immediate data available from the transport.
* In this case we've already processed the data
* and need to request more from the transport.
* c_data is set to NULL because there's an assert
* in trans_rqst_datain() checking that c_data is
* indeed null.
*/
cmd->c_data_len = 0;
break;
case msg_lu_aio_done:
(*a->a_aio_cmplt)(a->a_id);
break;
case msg_lu_add:
break;
case msg_reset_lu:
do {
/*
* The current implementation is that we
* have a shared queue for each LU. That means
* if we reset a LU all I_T nexus' must
* receive a CHECK_CONDITION on their next
* command.
*/
break;
case msg_shutdown:
s = (t10_shutdown_t *)m->msg_data;
(void) pthread_mutex_lock(&lu_list_mutex);
NULL);
lu_remove_cmds, (void *)itl);
/*
* Don't remove reference to l_common area until after
* the emulation routines are finished since they
* are likely to reference l_dtype_params.
*/
"LU_%x No remaining targets for LU(%d)\n",
"LU_%x Failed to close fd, "
errno);
/*CSTYLED*/
(void) pthread_mutex_unlock(
&lu->l_common_mutex);
(void) pthread_mutex_unlock(&lu_list_mutex);
(void *)(uintptr_t)pthread_self());
}
(void) pthread_mutex_unlock(&lu_list_mutex);
break;
/*
* SPC-3 revision 21c, section 4.5.6, Table 28
* When LU inventory changes need to report
* a REPORTED LUNS DATA HAS CHANGED event.
*/
"LU_%x Received InventoryChange for %d\n",
break;
case msg_thick_provo:
/*
* If the file at c_offset is currently
* unallocated we'll read in that buffer
* which will be zeros and then write it
* back out which will force the underlying
* filesystem to allocate the blocks.
* If someone has already issued a write
* to this area we'll then just cause a
*/
cmd->c_data_len);
provo_err = 0;
} else {
"LU_%x pread errno=%d\n",
"LU_%x pwrite errno=%d\n",
}
(void *)0 : (void *)1;
}
/*
* acknowledge this op and wait for next
*/
break;
case msg_lu_capacity_change:
"LU_%x Capacity Change from 0x%llx to 0x%llx\n",
break;
}
break;
case msg_lu_online:
"LU_%x Received online event\n",
lu->l_internal_num);
break;
}
break;
}
}
return (NULL);
}
/*
* []----
* | lu_buserr_handler -- deal with SIGBUS on mmap'd files
* |
* | Normally SIGBUS's are a real bad thing. With this project, which uses
* | mmap'd files that start out as hole-y, can represent more space than
* | the underlying storage has available. This is good and considered a
* | feature for "Thin Provisioning". However, this means that if the
* | administrator isn't on the ball the storage can fill up. Because of the
* | asynchronous nature of writing to a mmap'd file the OS will send a SIGBUS
* | to the thread which caused the problem. The thread will then locate its
* | data structure and in turn signal the initiator that a problem occurred.
* | Since we can't restart we're we left off because the out of space
* | condition is still present another thread is started to handle other
* | commands for the logical unit. The current thread will then exit.
* |
* | NOTE:
* | If for any reason this routine doesn't find what's it's expecting to
* | assert() will be called to create a core. This routine will only recover
* | from the expected case of a SIGBUS, otherwise something real bad has
* | happened and we need to see the core.
* []----
*/
/*ARGSUSED*/
void
{
char *fa;
if (pthread_mutex_trylock(&lu_list_mutex) != 0) {
assert(0);
}
break;
}
(void) pthread_mutex_unlock(&lu_list_mutex);
"SAM%x BUS ERROR and couldn't find logical unit\n",
assert(0);
#ifdef NDEBUG
return;
#endif
}
assert(0);
#ifdef NDEBUG
return;
#endif
}
"SAM%x BUS ERROR occurred outsize of mmap bounds\n",
assert(0);
#ifdef NDEBUG
return;
#endif
}
msg_thick_provo, (void *)1);
} else {
}
/*
* Now restart another thread to pick up where we've left off with
* processing commands for this logical unit.
*/
pthread_exit((void *)0);
}
/*
* []----
* | lu_remove_cmds -- look for and free commands for a given ITL
* []----
*/
static Boolean_t
lu_remove_cmds(msg_t *m, void *v)
{
t10_cmd_t *c;
switch (m->msg_type) {
case msg_cmd_send:
case msg_cmd_data_out:
"SAM%x LUN %d, removed command during lu_remove\n",
return (True);
}
break;
}
return (False);
}
/*
* []----
* | load_params -- load parameters and open LU backing store
* |
* | This routine can be called multiple times and will free and release
* | previous resources.
* []----
*/
static Boolean_t
{
char file[MAXPATHLEN];
char *str;
int version_maj = XML_VERS_LUN_MAJ;
int version_min = XML_VERS_LUN_MIN;
/*
* Clean up from previous call to this function. This occurs if
* the LU has grown since it was last opened.
*/
goto error;
goto error;
/*
* If there's no <status> tag it just means this is an older param
* file and there's no need to treat it as an error. Just mark
* the device as online.
*/
} else
/*
* If offline, we need to check to see if there's an initialization
* thread running for this lun. If not, start one.
*/
False)) {
if (thin_provisioning == False) {
1;
tp->q = queue_alloc();
/* ---- wait for start message ---- */
}
}
}
/*
* The default is to disable the fast write acknowledgement which
* can be overridden in a couple of ways. First, see if the global
* fast-write-ack is enabled, then check the per logical unit flags.
* The per LU bit is settable via a SCSI command.
*/
&lu->l_fast_write_ack);
&lu->l_fast_write_ack);
/*
* Object-based Storage Devices currently use directories to
* represent the partitions and files in those directories to
* represent user objects and collections. Therefore, there's
* not just a single file to be opened, but potentially thousands.
* Therefore, stop here if we've got an OSD dtype.
*/
goto error;
return (True);
} else
goto error;
} else {
goto error;
}
#ifndef _LP64
/*
* Since the address space is so limited on 32bit machines
* disable mmap'ing the file by default.
*/
#endif
} else
goto error;
/*
* st_size will be wrong if the device is a block device
* but that's okay since you can't mmap in a block device.
* A block device will fall back to using AIO operations.
*/
} else {
/*
* Since the default case will be to mmap
* in all files someone has asked that this
* lun not be mmap.
*/
}
return (True);
}
}
}
return (False);
}
/*
* []----
* | cmd_common_free -- frees data stored in the cmd
* |
* | NOTE: The mutex which protects c_state must be held when this routine
* | is called if there's a LU associated with this command.
* []----
*/
static void
{
if (lu) {
}
c->c_state = T10_Cmd_S1_Free;
c->c_data = 0;
c->c_data_len = 0;
if (c->c_emul_complete != NULL) {
(*c->c_emul_complete)(c->c_emul_id);
c->c_emul_complete = NULL;
}
if (c->c_cdb) {
}
if (c->c_cmd_sense) {
free(c->c_cmd_sense);
c->c_cmd_sense = NULL;
}
}
}
/*
* clear_transport -- Remove the transports reference to the T10 command
*
* This should be a function pointer stored in the t10_lu_impl structure.
* The only reason it's not, is I wish to wait until we know a little more
* about the FC transport. There may be some other callbacks required for that
* transport and if so, I'll need to define a new method for passing in
* the callbacks to the t10_create_handle. The easiest way would probably
* have a structure. I'm concerned about supporting different versions, so
* wish to think about it some more before implementing.
*
* This function can be called on either the transport thread or the t10
* thread.
*/
static void
{
iscsi_cmd_t *c = (iscsi_cmd_t *)t;
if (c)
}
/*
* []----
* | fallocate -- allocate blocks for file via file system interface
* |
* | This is a faster approach to allocating the blocks for a file.
* | Instead of reading and then writing each block which will force the
* | file system to allocate the data we simply ask the file system to
* | allocate the space. Unfortunately not all file systems support this
* | feature.
* []----
*/
static Boolean_t
{
#ifdef FALLOCATE_SUPPORTED
#if defined(_LARGEFILE64_SOURCE) && !defined(_LP64)
return (False);
else
return (True);
#else
return (False);
else
return (True);
#endif
#else
return (False);
#endif
}
/*
* []----
* | find_lu_by_num -- AVL comparison which looks at LUN
* []----
*/
static int
{
return (-1);
return (1);
return (0);
}
/*
* []----
* | find_lu_by_guid -- AVL comparison which looks at GUID
* []----
*/
static int
{
int i;
}
for (i = 0; i < l1->l_guid_len; i++) {
}
}
return (0);
}
/*
* []----
* | find_lu_by_targ -- AVL comparison which looks at the target
* |
* | NOTE:
* | The target value is the memory address of the per target structure.
* | Therefore, it's not persistent in any manner, nor can any association
* | be made between the target value and the initiator. It will be unique
* | however which is all that we're looking for.
* []----
*/
static int
{
return (-1);
return (1);
else
return (0);
}
/*
* []----
* | find_cmd_by_addr -- AVL comparison using the simplist of methods
* []----
*/
static int
{
return (-1);
return (1);
else
return (0);
}
/*ARGSUSED*/
static Boolean_t
{
assert(0);
return (False);
}
/*ARGSUSED*/
static void
{
assert(0);
}
#ifdef FULL_DEBUG
static char *
{
switch (s) {
case T10_Cmd_S1_Free: return ("FREE");
case T10_Cmd_S2_In: return ("IN");
case T10_Cmd_S3_Trans: return ("TRANS");
case T10_Cmd_S4_AIO: return ("AIO");
case T10_Cmd_S5_Wait: return ("WAIT");
case T10_Cmd_S6_Freeing: return ("FREEING");
}
return ("Invalid State");
}
#endif
static char *
{
switch (e) {
case T10_Cmd_T1: return ("T1");
case T10_Cmd_T2: return ("T2");
case T10_Cmd_T3: return ("T3");
case T10_Cmd_T4: return ("T4");
case T10_Cmd_T5: return ("T5");
case T10_Cmd_T6: return ("T6");
case T10_Cmd_T7: return ("T7");
}
return ("Invalid Event");
}
/*ARGSUSED*/
static void
{
assert(0);
}
/*ARGSUSED*/
static void
{
assert(0);
}
/*ARGSUSED*/
static void
{
assert(0);
}
static sam_device_table_t sam_emul_table[] = {
/* 0x00: DTYPE_DIRECT */
/* 0x01: DTYPE_SEQUENTIAL */
/* 0x11: DTYPE_OSD */
/* 0x1f: DTYPE_UNKNOWN */
/* End-of-Table marker */
{ 0, 0, 0, 0, 0, NULL }
};