--- a/usr/src/uts/common/io/idm/idm.c Tue Mar 24 19:19:49 2009 -0400
+++ b/usr/src/uts/common/io/idm/idm.c Tue Mar 24 17:50:49 2009 -0600
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -66,9 +66,16 @@
static void idm_task_abort_one(idm_conn_t *ic, idm_task_t *idt,
idm_abort_type_t abort_type);
static void idm_task_aborted(idm_task_t *idt, idm_status_t status);
+static idm_pdu_t *idm_pdu_alloc_common(uint_t hdrlen, uint_t datalen,
+ int sleepflag);
boolean_t idm_conn_logging = 0;
boolean_t idm_svc_logging = 0;
+#ifdef DEBUG
+boolean_t idm_pattern_checking = 1;
+#else
+boolean_t idm_pattern_checking = 0;
+#endif
/*
* Potential tuneable for the maximum number of tasks. Default to
@@ -228,14 +235,34 @@
*
*/
void
+idm_ini_conn_destroy_task(void *ic_void)
+{
+ idm_conn_t *ic = ic_void;
+
+ ic->ic_transport_ops->it_ini_conn_destroy(ic);
+ idm_conn_destroy_common(ic);
+}
+
+void
idm_ini_conn_destroy(idm_conn_t *ic)
{
+ /*
+ * It's reasonable for the initiator to call idm_ini_conn_destroy
+ * from within the context of the CN_CONNECT_DESTROY notification.
+ * That's a problem since we want to destroy the taskq for the
+ * state machine associated with the connection. Remove the
+ * connection from the list right away then handle the remaining
+ * work via the idm_global_taskq.
+ */
mutex_enter(&idm.idm_global_mutex);
list_remove(&idm.idm_ini_conn_list, ic);
mutex_exit(&idm.idm_global_mutex);
- ic->ic_transport_ops->it_ini_conn_destroy(ic);
- idm_conn_destroy_common(ic);
+ if (taskq_dispatch(idm.idm_global_taskq,
+ &idm_ini_conn_destroy_task, ic, TQ_SLEEP) == NULL) {
+ cmn_err(CE_WARN,
+ "idm_ini_conn_destroy: Couldn't dispatch task");
+ }
}
/*
@@ -243,24 +270,31 @@
*
* Establish connection to the remote system identified in idm_conn_t.
* The connection parameters including the remote IP address were established
- * in the call to idm_ini_conn_create.
+ * in the call to idm_ini_conn_create. The IDM state machine will
+ * perform client notifications as necessary to prompt the initiator through
+ * the login process. IDM also keeps a timer running so that if the login
+ * process doesn't complete in a timely manner it will fail.
*
* ic - idm_conn_t structure representing the relevant connection
*
* Returns success if the connection was established, otherwise some kind
* of meaningful error code.
*
- * Upon return the initiator can send a "login" request when it is ready.
+ * Upon return the login has either failed or is loggin in (ffp)
*/
idm_status_t
idm_ini_conn_connect(idm_conn_t *ic)
{
- idm_status_t rc;
+ idm_status_t rc = IDM_STATUS_SUCCESS;
rc = idm_conn_sm_init(ic);
if (rc != IDM_STATUS_SUCCESS) {
return (ic->ic_conn_sm_status);
}
+
+ /* Hold connection until we return */
+ idm_conn_hold(ic);
+
/* Kick state machine */
idm_conn_event(ic, CE_CONNECT_REQ, NULL);
@@ -274,6 +308,7 @@
if (ic->ic_state_flags & CF_ERROR) {
/* ic->ic_conn_sm_status will contains failure status */
+ idm_conn_rele(ic);
return (ic->ic_conn_sm_status);
}
@@ -281,21 +316,9 @@
ASSERT(ic->ic_state_flags & CF_LOGIN_READY);
(void) idm_notify_client(ic, CN_READY_FOR_LOGIN, NULL);
- return (IDM_STATUS_SUCCESS);
-}
+ idm_conn_rele(ic);
-/*
- * idm_ini_conn_sm_fini_task()
- *
- * Dispatch a thread on the global taskq to tear down an initiator connection's
- * state machine. Note: We cannot do this from the disconnect thread as we will
- * end up in a situation wherein the thread is running on a taskq that it then
- * attempts to destroy.
- */
-static void
-idm_ini_conn_sm_fini_task(void *ic_void)
-{
- idm_conn_sm_fini((idm_conn_t *)ic_void);
+ return (rc);
}
/*
@@ -306,30 +329,38 @@
*
* ic - idm_conn_t structure representing the relevant connection
*
- * This is synchronous and it will return when the connection has been
- * properly shutdown.
+ * This is asynchronous and will return before the connection is properly
+ * shutdown
*/
/* ARGSUSED */
void
idm_ini_conn_disconnect(idm_conn_t *ic)
{
- mutex_enter(&ic->ic_state_mutex);
+ idm_conn_event(ic, CE_TRANSPORT_FAIL, NULL);
+}
- if (ic->ic_state_flags == 0) {
- /* already disconnected */
- mutex_exit(&ic->ic_state_mutex);
- return;
+/*
+ * idm_ini_conn_disconnect_wait
+ *
+ * Forces a connection (previously established using idm_ini_conn_connect)
+ * to perform a controlled shutdown. Blocks until the connection is
+ * disconnected.
+ *
+ * ic - idm_conn_t structure representing the relevant connection
+ */
+/* ARGSUSED */
+void
+idm_ini_conn_disconnect_sync(idm_conn_t *ic)
+{
+ mutex_enter(&ic->ic_state_mutex);
+ if ((ic->ic_state != CS_S9_INIT_ERROR) &&
+ (ic->ic_state != CS_S11_COMPLETE)) {
+ idm_conn_event_locked(ic, CE_TRANSPORT_FAIL, NULL, CT_NONE);
+ while ((ic->ic_state != CS_S9_INIT_ERROR) &&
+ (ic->ic_state != CS_S11_COMPLETE))
+ cv_wait(&ic->ic_state_cv, &ic->ic_state_mutex);
}
- ic->ic_state_flags = 0;
- ic->ic_conn_sm_status = 0;
mutex_exit(&ic->ic_state_mutex);
-
- /* invoke the transport-specific conn_destroy */
- (void) ic->ic_transport_ops->it_ini_conn_disconnect(ic);
-
- /* teardown the connection sm */
- (void) taskq_dispatch(idm.idm_global_taskq, &idm_ini_conn_sm_fini_task,
- (void *)ic, TQ_SLEEP);
}
/*
@@ -425,13 +456,6 @@
cv_broadcast(&idm.idm_tgt_svc_cv);
mutex_exit(&idm.idm_global_mutex);
- /* tear down the svc resources */
- idm_refcnt_destroy(&is->is_refcnt);
- cv_destroy(&is->is_count_cv);
- mutex_destroy(&is->is_count_mutex);
- cv_destroy(&is->is_cv);
- mutex_destroy(&is->is_mutex);
-
/* teardown each transport-specific service */
for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) {
it = &idm_transport_list[type];
@@ -442,6 +466,13 @@
it->it_ops->it_tgt_svc_destroy(is);
}
+ /* tear down the svc resources */
+ idm_refcnt_destroy(&is->is_refcnt);
+ cv_destroy(&is->is_count_cv);
+ mutex_destroy(&is->is_count_mutex);
+ cv_destroy(&is->is_cv);
+ mutex_destroy(&is->is_mutex);
+
/* free the svc handle */
kmem_free(is, sizeof (idm_svc_t));
}
@@ -475,15 +506,13 @@
idm_tgt_svc_online(idm_svc_t *is)
{
- idm_transport_type_t type;
+ idm_transport_type_t type, last_type;
idm_transport_t *it;
- int rc;
- int svc_found;
+ int rc = IDM_STATUS_SUCCESS;
mutex_enter(&is->is_mutex);
- /* Walk through each of the transports and online them */
if (is->is_online == 0) {
- svc_found = 0;
+ /* Walk through each of the transports and online them */
for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) {
it = &idm_transport_list[type];
if (it->it_ops == NULL) {
@@ -494,19 +523,39 @@
mutex_exit(&is->is_mutex);
rc = it->it_ops->it_tgt_svc_online(is);
mutex_enter(&is->is_mutex);
- if (rc == IDM_STATUS_SUCCESS) {
- /* We have at least one service running. */
- svc_found = 1;
+ if (rc != IDM_STATUS_SUCCESS) {
+ last_type = type;
+ break;
}
}
+ if (rc != IDM_STATUS_SUCCESS) {
+ /*
+ * The last transport failed to online.
+ * Offline any transport onlined above and
+ * do not online the target.
+ */
+ for (type = 0; type < last_type; type++) {
+ it = &idm_transport_list[type];
+ if (it->it_ops == NULL) {
+ /* transport is not registered */
+ continue;
+ }
+
+ mutex_exit(&is->is_mutex);
+ it->it_ops->it_tgt_svc_offline(is);
+ mutex_enter(&is->is_mutex);
+ }
+ } else {
+ /* Target service now online */
+ is->is_online = 1;
+ }
} else {
- svc_found = 1;
+ /* Target service already online, just bump the count */
+ is->is_online++;
}
- if (svc_found)
- is->is_online++;
mutex_exit(&is->is_mutex);
- return (svc_found ? IDM_STATUS_SUCCESS : IDM_STATUS_FAIL);
+ return (rc);
}
/*
@@ -600,12 +649,11 @@
* Passes the set of key value pairs to the transport for activation.
* This will be invoked as the connection is entering full-feature mode.
*/
-idm_status_t
+void
idm_notice_key_values(idm_conn_t *ic, nvlist_t *negotiated_nvl)
{
ASSERT(ic->ic_transport_ops != NULL);
- return (ic->ic_transport_ops->it_notice_key_values(ic,
- negotiated_nvl));
+ ic->ic_transport_ops->it_notice_key_values(ic, negotiated_nvl);
}
/*
@@ -640,6 +688,13 @@
idb->idb_xfer_len = xfer_len;
idb->idb_buf_cb = idb_buf_cb;
idb->idb_cb_arg = cb_arg;
+ gethrestime(&idb->idb_xfer_start);
+
+ /*
+ * Buffer should not contain the pattern. If the pattern is
+ * present then we've been asked to transmit initialized data
+ */
+ IDM_BUFPAT_CHECK(idb, xfer_len, BP_CHECK_ASSERT);
mutex_enter(&idt->idt_mutex);
switch (idt->idt_state) {
@@ -715,6 +770,7 @@
idb->idb_xfer_len = xfer_len;
idb->idb_buf_cb = idb_buf_cb;
idb->idb_cb_arg = cb_arg;
+ gethrestime(&idb->idb_xfer_start);
/*
* "In" buf list is for "Data In" PDU's, "Out" buf list is for
@@ -766,6 +822,7 @@
idb->idb_in_transport = B_FALSE;
idb->idb_tx_thread = B_FALSE;
idt->idt_tx_to_ini_done++;
+ gethrestime(&idb->idb_xfer_done);
/*
* idm_refcnt_rele may cause TASK_SUSPENDING --> TASK_SUSPENDED or
@@ -827,6 +884,7 @@
ASSERT(mutex_owned(&idt->idt_mutex));
idb->idb_in_transport = B_FALSE;
idt->idt_rx_from_ini_done++;
+ gethrestime(&idb->idb_xfer_done);
/*
* idm_refcnt_rele may cause TASK_SUSPENDING --> TASK_SUSPENDED or
@@ -836,6 +894,14 @@
idm_task_rele(idt);
idb->idb_status = status;
+ if (status == IDM_STATUS_SUCCESS) {
+ /*
+ * Buffer should not contain the pattern. If it does then
+ * we did not get the data from the remote host.
+ */
+ IDM_BUFPAT_CHECK(idb, idb->idb_xfer_len, BP_CHECK_ASSERT);
+ }
+
switch (idt->idt_state) {
case TASK_ACTIVE:
idm_buf_unbind_out_locked(idt, idb);
@@ -919,6 +985,8 @@
buf->idb_bufoffset = 0;
buf->idb_xfer_len = 0;
buf->idb_magic = IDM_BUF_MAGIC;
+ buf->idb_in_transport = B_FALSE;
+ buf->idb_bufbcopy = B_FALSE;
/*
* If bufptr is NULL, we have an implicit request to allocate
@@ -945,8 +1013,13 @@
buf->idb_bufalloc = B_TRUE;
} else {
/*
- * Set the passed bufptr into the buf handle, and
- * register the handle with the transport layer.
+ * For large transfers, Set the passed bufptr into
+ * the buf handle, and register the handle with the
+ * transport layer. As memory registration with the
+ * transport layer is a time/cpu intensive operation,
+ * for small transfers (up to a pre-defined bcopy
+ * threshold), use pre-registered memory buffers
+ * and bcopy data at the appropriate time.
*/
buf->idb_buf = bufptr;
@@ -956,12 +1029,15 @@
kmem_cache_free(idm.idm_buf_cache, buf);
return (NULL);
}
- /* Ensure bufalloc'd flag is unset */
- buf->idb_bufalloc = B_FALSE;
+ /*
+ * The transport layer is now expected to set the idb_bufalloc
+ * correctly to indicate if resources have been allocated.
+ */
}
+ IDM_BUFPAT_SET(buf);
+
return (buf);
-
}
/*
@@ -1013,6 +1089,14 @@
void
idm_buf_bind_out(idm_task_t *idt, idm_buf_t *buf)
{
+ /*
+ * For small transfers, the iSER transport delegates the IDM
+ * layer to bcopy the SCSI Write data for faster IOPS.
+ */
+ if (buf->idb_bufbcopy == B_TRUE) {
+
+ bcopy(buf->idb_bufptr, buf->idb_buf, buf->idb_buflen);
+ }
mutex_enter(&idt->idt_mutex);
idm_buf_bind_out_locked(idt, buf);
mutex_exit(&idt->idt_mutex);
@@ -1029,6 +1113,14 @@
void
idm_buf_unbind_in(idm_task_t *idt, idm_buf_t *buf)
{
+ /*
+ * For small transfers, the iSER transport delegates the IDM
+ * layer to bcopy the SCSI Read data into the read buufer
+ * for faster IOPS.
+ */
+ if (buf->idb_bufbcopy == B_TRUE) {
+ bcopy(buf->idb_buf, buf->idb_bufptr, buf->idb_buflen);
+ }
mutex_enter(&idt->idt_mutex);
idm_buf_unbind_in_locked(idt, buf);
mutex_exit(&idt->idt_mutex);
@@ -1080,6 +1172,66 @@
return (NULL);
}
+void
+idm_bufpat_set(idm_buf_t *idb)
+{
+ idm_bufpat_t *bufpat;
+ int len, i;
+
+ len = idb->idb_buflen;
+ len = (len / sizeof (idm_bufpat_t)) * sizeof (idm_bufpat_t);
+
+ bufpat = idb->idb_buf;
+ for (i = 0; i < len; i += sizeof (idm_bufpat_t)) {
+ bufpat->bufpat_idb = idb;
+ bufpat->bufpat_bufmagic = IDM_BUF_MAGIC;
+ bufpat->bufpat_offset = i;
+ bufpat++;
+ }
+}
+
+boolean_t
+idm_bufpat_check(idm_buf_t *idb, int check_len, idm_bufpat_check_type_t type)
+{
+ idm_bufpat_t *bufpat;
+ int len, i;
+
+ len = (type == BP_CHECK_QUICK) ? sizeof (idm_bufpat_t) : check_len;
+ len = (len / sizeof (idm_bufpat_t)) * sizeof (idm_bufpat_t);
+ ASSERT(len <= idb->idb_buflen);
+ bufpat = idb->idb_buf;
+
+ /*
+ * Don't check the pattern in buffers that came from outside IDM
+ * (these will be buffers from the initiator that we opted not
+ * to double-buffer)
+ */
+ if (!idb->idb_bufalloc)
+ return (B_FALSE);
+
+ /*
+ * Return true if we find the pattern anywhere in the buffer
+ */
+ for (i = 0; i < len; i += sizeof (idm_bufpat_t)) {
+ if (BUFPAT_MATCH(bufpat, idb)) {
+ IDM_CONN_LOG(CE_WARN, "idm_bufpat_check found: "
+ "idb %p bufpat %p "
+ "bufpat_idb=%p bufmagic=%08x offset=%08x",
+ (void *)idb, (void *)bufpat, bufpat->bufpat_idb,
+ bufpat->bufpat_bufmagic, bufpat->bufpat_offset);
+ DTRACE_PROBE2(bufpat__pattern__found,
+ idm_buf_t *, idb, idm_bufpat_t *, bufpat);
+ if (type == BP_CHECK_ASSERT) {
+ ASSERT(0);
+ }
+ return (B_TRUE);
+ }
+ bufpat++;
+ }
+
+ return (B_FALSE);
+}
+
/*
* idm_task_alloc
*
@@ -1123,7 +1275,7 @@
/*
* idm_task_start
*
- * Add the task to an AVL tree to notify IDM about a new task. The caller
+ * Mark the task active and initialize some stats. The caller
* sets up the idm_task_t structure with a prior call to idm_task_alloc().
* The task service does not function as a task/work engine, it is the
* responsibility of the initiator to start the data transfer and free the
@@ -1138,22 +1290,35 @@
idt->idt_state = TASK_ACTIVE;
idt->idt_client_handle = handle;
idt->idt_tx_to_ini_start = idt->idt_tx_to_ini_done =
- idt->idt_rx_from_ini_start = idt->idt_rx_from_ini_done = 0;
+ idt->idt_rx_from_ini_start = idt->idt_rx_from_ini_done =
+ idt->idt_tx_bytes = idt->idt_rx_bytes = 0;
}
/*
* idm_task_done
*
- * This function will remove the task from the AVL tree indicating that the
- * task is no longer active.
+ * This function sets the state to indicate that the task is no longer active.
*/
void
idm_task_done(idm_task_t *idt)
{
ASSERT(idt != NULL);
- ASSERT(idt->idt_refcnt.ir_refcnt == 0);
+
+ mutex_enter(&idt->idt_mutex);
+ idt->idt_state = TASK_IDLE;
+ mutex_exit(&idt->idt_mutex);
- idt->idt_state = TASK_IDLE;
+ /*
+ * Although unlikely it is possible for a reference to come in after
+ * the client has decided the task is over but before we've marked
+ * the task idle. One specific unavoidable scenario is the case where
+ * received PDU with the matching ITT/TTT results in a successful
+ * lookup of this task. We are at the mercy of the remote node in
+ * that case so we need to handle it. Now that the task state
+ * has changed no more references will occur so a simple call to
+ * idm_refcnt_wait_ref should deal with the situation.
+ */
+ idm_refcnt_wait_ref(&idt->idt_refcnt);
idm_refcnt_reset(&idt->idt_refcnt);
}
@@ -1166,11 +1331,14 @@
void
idm_task_free(idm_task_t *idt)
{
- idm_conn_t *ic = idt->idt_ic;
+ idm_conn_t *ic;
ASSERT(idt != NULL);
+ ASSERT(idt->idt_refcnt.ir_refcnt == 0);
ASSERT(idt->idt_state == TASK_IDLE);
+ ic = idt->idt_ic;
+
/*
* It's possible for items to still be in the idt_inbufv list if
* they were added after idm_task_cleanup was called. We rely on
@@ -1190,13 +1358,13 @@
}
/*
- * idm_task_find
- *
- * This function looks up a task by task tag
+ * idm_task_find_common
+ * common code for idm_task_find() and idm_task_find_and_complete()
*/
/*ARGSUSED*/
-idm_task_t *
-idm_task_find(idm_conn_t *ic, uint32_t itt, uint32_t ttt)
+static idm_task_t *
+idm_task_find_common(idm_conn_t *ic, uint32_t itt, uint32_t ttt,
+ boolean_t complete)
{
uint32_t tt, client_handle;
idm_task_t *idt;
@@ -1224,16 +1392,32 @@
if (idt != NULL) {
mutex_enter(&idt->idt_mutex);
if ((idt->idt_state != TASK_ACTIVE) ||
+ (idt->idt_ic != ic) ||
(IDM_CONN_ISTGT(ic) &&
(idt->idt_client_handle != client_handle))) {
/*
- * Task is aborting, we don't want any more references.
+ * Task doesn't match or task is aborting and
+ * we don't want any more references.
*/
+ if ((idt->idt_ic != ic) &&
+ (idt->idt_state == TASK_ACTIVE) &&
+ (IDM_CONN_ISINI(ic) || idt->idt_client_handle ==
+ client_handle)) {
+ IDM_CONN_LOG(CE_WARN,
+ "idm_task_find: wrong connection %p != %p",
+ (void *)ic, (void *)idt->idt_ic);
+ }
mutex_exit(&idt->idt_mutex);
rw_exit(&idm.idm_taskid_table_lock);
return (NULL);
}
idm_task_hold(idt);
+ /*
+ * Set the task state to TASK_COMPLETE so it can no longer
+ * be found or aborted.
+ */
+ if (B_TRUE == complete)
+ idt->idt_state = TASK_COMPLETE;
mutex_exit(&idt->idt_mutex);
}
rw_exit(&idm.idm_taskid_table_lock);
@@ -1242,6 +1426,25 @@
}
/*
+ * This function looks up a task by task tag.
+ */
+idm_task_t *
+idm_task_find(idm_conn_t *ic, uint32_t itt, uint32_t ttt)
+{
+ return (idm_task_find_common(ic, itt, ttt, B_FALSE));
+}
+
+/*
+ * This function looks up a task by task tag. If found, the task state
+ * is atomically set to TASK_COMPLETE so it can longer be found or aborted.
+ */
+idm_task_t *
+idm_task_find_and_complete(idm_conn_t *ic, uint32_t itt, uint32_t ttt)
+{
+ return (idm_task_find_common(ic, itt, ttt, B_TRUE));
+}
+
+/*
* idm_task_find_by_handle
*
* This function looks up a task by the client-private idt_client_handle.
@@ -1323,15 +1526,21 @@
rw_enter(&idm.idm_taskid_table_lock, RW_READER);
for (idx = 0; idx < idm.idm_taskid_max; idx++) {
task = idm.idm_taskid_table[idx];
- if (task && (task->idt_state != TASK_IDLE) &&
+ if (task == NULL)
+ continue;
+ mutex_enter(&task->idt_mutex);
+ if ((task->idt_state != TASK_IDLE) &&
+ (task->idt_state != TASK_COMPLETE) &&
(task->idt_ic == ic)) {
rw_exit(&idm.idm_taskid_table_lock);
idm_task_abort_one(ic, task, abort_type);
rw_enter(&idm.idm_taskid_table_lock, RW_READER);
- }
+ } else
+ mutex_exit(&task->idt_mutex);
}
rw_exit(&idm.idm_taskid_table_lock);
} else {
+ mutex_enter(&idt->idt_mutex);
idm_task_abort_one(ic, idt, abort_type);
}
}
@@ -1360,11 +1569,15 @@
}
}
+/*
+ * Abort the idm task.
+ * Caller must hold the task mutex, which will be released before return
+ */
static void
idm_task_abort_one(idm_conn_t *ic, idm_task_t *idt, idm_abort_type_t abort_type)
{
/* Caller must hold connection mutex */
- mutex_enter(&idt->idt_mutex);
+ ASSERT(mutex_owned(&idt->idt_mutex));
switch (idt->idt_state) {
case TASK_ACTIVE:
switch (abort_type) {
@@ -1689,11 +1902,10 @@
}
/*
- * Allocates a PDU along with memory for header and data.
+ * Common allocation of a PDU along with memory for header and data.
*/
-
-idm_pdu_t *
-idm_pdu_alloc(uint_t hdrlen, uint_t datalen)
+static idm_pdu_t *
+idm_pdu_alloc_common(uint_t hdrlen, uint_t datalen, int sleepflag)
{
idm_pdu_t *result;
@@ -1706,21 +1918,45 @@
* length is assumed to be datalen. isp_hdrlen and isp_datalen
* can be adjusted after the PDU is returned if necessary.
*/
- result = kmem_zalloc(sizeof (idm_pdu_t) + hdrlen + datalen, KM_SLEEP);
- result->isp_flags |= IDM_PDU_ALLOC; /* For idm_pdu_free sanity check */
- result->isp_hdr = (iscsi_hdr_t *)(result + 1); /* Ptr. Arithmetic */
- result->isp_hdrlen = hdrlen;
- result->isp_hdrbuflen = hdrlen;
- result->isp_transport_hdrlen = 0;
- result->isp_data = (uint8_t *)result->isp_hdr + hdrlen;
- result->isp_datalen = datalen;
- result->isp_databuflen = datalen;
- result->isp_magic = IDM_PDU_MAGIC;
+ result = kmem_zalloc(sizeof (idm_pdu_t) + hdrlen + datalen, sleepflag);
+ if (result != NULL) {
+ /* For idm_pdu_free sanity check */
+ result->isp_flags |= IDM_PDU_ALLOC;
+ /* pointer arithmetic */
+ result->isp_hdr = (iscsi_hdr_t *)(result + 1);
+ result->isp_hdrlen = hdrlen;
+ result->isp_hdrbuflen = hdrlen;
+ result->isp_transport_hdrlen = 0;
+ result->isp_data = (uint8_t *)result->isp_hdr + hdrlen;
+ result->isp_datalen = datalen;
+ result->isp_databuflen = datalen;
+ result->isp_magic = IDM_PDU_MAGIC;
+ }
return (result);
}
/*
+ * Typical idm_pdu_alloc invocation, will block for resources.
+ */
+idm_pdu_t *
+idm_pdu_alloc(uint_t hdrlen, uint_t datalen)
+{
+ return (idm_pdu_alloc_common(hdrlen, datalen, KM_SLEEP));
+}
+
+/*
+ * Non-blocking idm_pdu_alloc implementation, returns NULL if resources
+ * are not available. Needed for transport-layer allocations which may
+ * be invoking in interrupt context.
+ */
+idm_pdu_t *
+idm_pdu_alloc_nosleep(uint_t hdrlen, uint_t datalen)
+{
+ return (idm_pdu_alloc_common(hdrlen, datalen, KM_NOSLEEP));
+}
+
+/*
* Free a PDU previously allocated with idm_pdu_alloc() including any
* header and data space allocated as part of the original request.
* Additional memory regions referenced by subsequent modification of
@@ -2030,8 +2266,12 @@
cv_init(&idm.idm_tgt_svc_cv, NULL, CV_DEFAULT, NULL);
cv_init(&idm.idm_wd_cv, NULL, CV_DEFAULT, NULL);
+ /*
+ * The maximum allocation needs to be high here since there can be
+ * many concurrent tasks using the global taskq.
+ */
idm.idm_global_taskq = taskq_create("idm_global_taskq", 1, minclsyspri,
- 4, 4, TASKQ_PREPOPULATE);
+ 128, 16384, TASKQ_PREPOPULATE);
if (idm.idm_global_taskq == NULL) {
cv_destroy(&idm.idm_wd_cv);
cv_destroy(&idm.idm_tgt_svc_cv);
@@ -2115,7 +2355,15 @@
thread_join(idm.idm_wd_thread_did);
idm_idpool_destroy(&idm.idm_conn_id_pool);
+
+ /* Close any LDI handles we have open on transport drivers */
+ mutex_enter(&idm.idm_global_mutex);
+ idm_transport_teardown();
+ mutex_exit(&idm.idm_global_mutex);
+
+ /* Teardown the native sockets transport */
idm_so_fini();
+
list_destroy(&idm.idm_ini_conn_list);
list_destroy(&idm.idm_tgt_conn_list);
list_destroy(&idm.idm_tgt_svc_list);