summaryrefslogtreecommitdiff
path: root/usr/src/uts/common
diff options
context:
space:
mode:
authorPriya Krishnan <Priya.Krishnan@Sun.COM>2009-11-17 15:48:05 -0500
committerPriya Krishnan <Priya.Krishnan@Sun.COM>2009-11-17 15:48:05 -0500
commit60220f10412f6a7c5b45f950f6f6aa364658a179 (patch)
tree9b7570206c0f49cfe38f79f2911483b961627344 /usr/src/uts/common
parent31f572c226019e62b274ff14c94d5a53f5d79ee8 (diff)
downloadillumos-gate-60220f10412f6a7c5b45f950f6f6aa364658a179.tar.gz
6766563 iscsit should support phase collapse
Diffstat (limited to 'usr/src/uts/common')
-rw-r--r--usr/src/uts/common/io/comstar/port/iscsit/iscsit.c206
-rw-r--r--usr/src/uts/common/io/comstar/port/iscsit/iscsit.h2
-rw-r--r--usr/src/uts/common/io/comstar/port/iscsit/iscsit_login.c5
-rw-r--r--usr/src/uts/common/io/comstar/port/iscsit/iscsit_text.c2
-rw-r--r--usr/src/uts/common/io/comstar/port/iscsit/iscsit_tgt.c2
-rw-r--r--usr/src/uts/common/io/ib/clients/iser/iser_xfer.c17
-rw-r--r--usr/src/uts/common/io/idm/idm.c2
-rw-r--r--usr/src/uts/common/io/idm/idm_impl.c2
-rw-r--r--usr/src/uts/common/io/idm/idm_so.c37
-rw-r--r--usr/src/uts/common/sys/idm/idm.h4
-rw-r--r--usr/src/uts/common/sys/idm/idm_impl.h6
11 files changed, 199 insertions, 86 deletions
diff --git a/usr/src/uts/common/io/comstar/port/iscsit/iscsit.c b/usr/src/uts/common/io/comstar/port/iscsit/iscsit.c
index 896a1f2d07..801bf2904e 100644
--- a/usr/src/uts/common/io/comstar/port/iscsit/iscsit.c
+++ b/usr/src/uts/common/io/comstar/port/iscsit/iscsit.c
@@ -141,9 +141,6 @@ void
iscsit_set_cmdsn(iscsit_conn_t *ict, idm_pdu_t *rx_pdu);
static void
-iscsit_calc_rspsn(iscsit_conn_t *ict, idm_pdu_t *resp);
-
-static void
iscsit_deferred_dispatch(idm_pdu_t *rx_pdu);
static void
@@ -949,6 +946,48 @@ iscsit_client_notify(idm_conn_t *ic, idm_client_notify_t icn,
return (rc);
}
+/*
+ * iscsit_update_statsn is invoked for all the PDUs which have the StatSN
+ * field in the header. The StatSN is incremented if the IDM_PDU_ADVANCE_STATSN
+ * flag is set in the pdu flags field. The StatSN is connection-wide and is
+ * protected by the mutex ict_statsn_mutex. For Data-In PDUs, if the flag
+ * IDM_TASK_PHASECOLLAPSE_REQ is set, the status (phase-collapse) is also filled
+ */
+void
+iscsit_update_statsn(idm_task_t *idm_task, idm_pdu_t *pdu)
+{
+ iscsi_scsi_rsp_hdr_t *rsp = (iscsi_scsi_rsp_hdr_t *)pdu->isp_hdr;
+ iscsit_conn_t *ict = (iscsit_conn_t *)pdu->isp_ic->ic_handle;
+ iscsit_task_t *itask = NULL;
+ scsi_task_t *task = NULL;
+
+ mutex_enter(&ict->ict_statsn_mutex);
+ rsp->statsn = htonl(ict->ict_statsn);
+ if (pdu->isp_flags & IDM_PDU_ADVANCE_STATSN)
+ ict->ict_statsn++;
+ mutex_exit(&ict->ict_statsn_mutex);
+
+ /*
+ * The last SCSI Data PDU passed for a command may also contain the
+ * status if the status indicates termination with no expections, i.e.
+ * no sense data or response involved. If the command completes with
+ * an error, then the response and sense data will be sent in a
+ * separate iSCSI Response PDU.
+ */
+ if ((idm_task) && (idm_task->idt_flags & IDM_TASK_PHASECOLLAPSE_REQ)) {
+ itask = idm_task->idt_private;
+ task = itask->it_stmf_task;
+
+ rsp->cmd_status = task->task_scsi_status;
+ rsp->flags |= ISCSI_FLAG_DATA_STATUS;
+ if (task->task_status_ctrl & TASK_SCTRL_OVER) {
+ rsp->flags |= ISCSI_FLAG_CMD_OVERFLOW;
+ } else if (task->task_status_ctrl & TASK_SCTRL_UNDER) {
+ rsp->flags |= ISCSI_FLAG_CMD_UNDERFLOW;
+ }
+ rsp->residual_count = htonl(task->task_resid);
+ }
+}
void
iscsit_build_hdr(idm_task_t *idm_task, idm_pdu_t *pdu, uint8_t opcode)
@@ -971,6 +1010,7 @@ iscsit_build_hdr(idm_task_t *idm_task, idm_pdu_t *pdu, uint8_t opcode)
/* Maintain current statsn for RTT responses */
dh->statsn = (opcode == ISCSI_OP_RTT_RSP) ?
htonl(itask->it_ict->ict_statsn) : 0;
+
dh->expcmdsn = htonl(itask->it_ict->ict_sess->ist_expcmdsn);
dh->maxcmdsn = htonl(itask->it_ict->ict_sess->ist_maxcmdsn);
@@ -981,7 +1021,7 @@ iscsit_build_hdr(idm_task_t *idm_task, idm_pdu_t *pdu, uint8_t opcode)
* data.dlength
* data.datasn
* data.offset
- * residual_count and cmd_status (if we ever implement phase collapse)
+ * statsn, residual_count and cmd_status (for phase collapse)
* rtt.rttsn
* rtt.data_offset
* rtt.data_length
@@ -1006,13 +1046,20 @@ iscsit_keepalive(idm_conn_t *ic)
*/
nop_in_pdu = idm_pdu_alloc(sizeof (*nop_in), 0);
idm_pdu_init(nop_in_pdu, ic, NULL, NULL);
-
nop_in = (iscsi_nop_in_hdr_t *)nop_in_pdu->isp_hdr;
bzero(nop_in, sizeof (*nop_in));
nop_in->opcode = ISCSI_OP_NOOP_IN;
nop_in->flags = ISCSI_FLAG_FINAL;
nop_in->itt = ISCSI_RSVD_TASK_TAG;
/*
+ * When the target sends a NOP-In as a Ping, the target transfer tag
+ * is set to a valid (not reserved) value and the initiator task tag
+ * is set to ISCSI_RSVD_TASK_TAG (0xffffffff). In this case the StatSN
+ * will always contain the next sequence number but the StatSN for the
+ * connection is not advanced after this PDU is sent.
+ */
+ nop_in_pdu->isp_flags |= IDM_PDU_SET_STATSN;
+ /*
* This works because we don't currently allocate ttt's anywhere else
* in iscsit so as long as we stay out of IDM's range we are safe.
* If we need to allocate ttt's for other PDU's in the future this will
@@ -1058,6 +1105,7 @@ iscsit_conn_accept(idm_conn_t *ic)
ict->ict_keepalive_ttt = IDM_TASKIDS_MAX; /* Avoid IDM TT range */
ic->ic_handle = ict;
mutex_init(&ict->ict_mutex, NULL, MUTEX_DRIVER, NULL);
+ mutex_init(&ict->ict_statsn_mutex, NULL, MUTEX_DRIVER, NULL);
idm_refcnt_init(&ict->ict_refcnt, ict);
/*
@@ -1317,6 +1365,7 @@ iscsit_xfer_scsi_data(scsi_task_t *task, stmf_data_buf_t *dbuf,
uint32_t ioflags)
{
iscsit_task_t *iscsit_task = task->task_port_private;
+ iscsit_sess_t *ict_sess = iscsit_task->it_ict->ict_sess;
iscsit_buf_t *ibuf = dbuf->db_port_private;
int idm_rc;
@@ -1333,26 +1382,34 @@ iscsit_xfer_scsi_data(scsi_task_t *task, stmf_data_buf_t *dbuf,
ASSERT(ibuf->ibuf_is_immed == B_FALSE);
if (dbuf->db_flags & DB_DIRECTION_TO_RPORT) {
/*
+ * The DB_SEND_STATUS_GOOD flag in the STMF data buffer allows
+ * the port provider to phase-collapse, i.e. send the status
+ * along with the final data PDU for the command. The port
+ * provider passes this request to the transport layer by
+ * setting a flag IDM_TASK_PHASECOLLAPSE_REQ in the task.
+ */
+ if (dbuf->db_flags & DB_SEND_STATUS_GOOD)
+ iscsit_task->it_idm_task->idt_flags |=
+ IDM_TASK_PHASECOLLAPSE_REQ;
+ /*
* IDM will call iscsit_build_hdr so lock now to serialize
* access to the SN values. We need to lock here to enforce
* lock ordering
*/
- rw_enter(&iscsit_task->it_ict->ict_sess->ist_sn_rwlock,
- RW_READER);
+ rw_enter(&ict_sess->ist_sn_rwlock, RW_READER);
idm_rc = idm_buf_tx_to_ini(iscsit_task->it_idm_task,
ibuf->ibuf_idm_buf, dbuf->db_relative_offset,
dbuf->db_data_size, &iscsit_buf_xfer_cb, dbuf);
- rw_exit(&iscsit_task->it_ict->ict_sess->ist_sn_rwlock);
+ rw_exit(&ict_sess->ist_sn_rwlock);
return (iscsit_idm_to_stmf(idm_rc));
} else if (dbuf->db_flags & DB_DIRECTION_FROM_RPORT) {
/* Grab the SN lock (see comment above) */
- rw_enter(&iscsit_task->it_ict->ict_sess->ist_sn_rwlock,
- RW_READER);
+ rw_enter(&ict_sess->ist_sn_rwlock, RW_READER);
idm_rc = idm_buf_rx_from_ini(iscsit_task->it_idm_task,
ibuf->ibuf_idm_buf, dbuf->db_relative_offset,
dbuf->db_data_size, &iscsit_buf_xfer_cb, dbuf);
- rw_exit(&iscsit_task->it_ict->ict_sess->ist_sn_rwlock);
+ rw_exit(&ict_sess->ist_sn_rwlock);
return (iscsit_idm_to_stmf(idm_rc));
}
@@ -1377,13 +1434,24 @@ iscsit_buf_xfer_cb(idm_buf_t *idb, idm_status_t status)
}
/*
- * COMSTAR currently requires port providers to support
- * the DB_SEND_STATUS_GOOD flag even if phase collapse is
- * not supported. So we will roll our own... pretend we are
- * COMSTAR and ask for a status PDU.
+ * For ISCSI over TCP (not iSER), the last SCSI Data PDU passed
+ * for a successful command contains the status as requested by
+ * by COMSTAR (via the DB_SEND_STATUS_GOOD flag). But the iSER
+ * transport does not support phase-collapse. So pretend we are
+ * COMSTAR and send the status in a separate PDU now.
*/
- if ((dbuf->db_flags & DB_SEND_STATUS_GOOD) &&
+ if (idb->idb_task_binding->idt_flags & IDM_TASK_PHASECOLLAPSE_SUCCESS) {
+ /*
+ * Mark task complete, remove task from the session task
+ * list and notify COMSTAR that the status has been sent.
+ */
+ iscsit_task_done(itask);
+ itask->it_idm_task->idt_state = TASK_COMPLETE;
+ stmf_send_status_done(itask->it_stmf_task,
+ iscsit_idm_to_stmf(status), STMF_IOF_LPORT_DONE);
+ } else if ((dbuf->db_flags & DB_SEND_STATUS_GOOD) &&
status == IDM_STATUS_SUCCESS) {
+
/*
* If iscsit_send_scsi_status succeeds then the TX PDU
* callback will call stmf_send_status_done and set
@@ -1465,11 +1533,14 @@ iscsit_send_scsi_status(scsi_task_t *task, uint32_t ioflags)
/*
* Fast path. Cached status PDU's are already
* initialized. We just need to fill in
- * connection and task information.
+ * connection and task information. StatSN is
+ * incremented by 1 for every status sent a
+ * connection.
*/
pdu = kmem_cache_alloc(iscsit_status_pdu_cache, KM_SLEEP);
pdu->isp_ic = itask->it_ict->ict_ic;
pdu->isp_private = itask;
+ pdu->isp_flags |= IDM_PDU_SET_STATSN | IDM_PDU_ADVANCE_STATSN;
rsp = (iscsi_scsi_rsp_hdr_t *)pdu->isp_hdr;
rsp->itt = itask->it_itt;
@@ -1492,6 +1563,7 @@ iscsit_send_scsi_status(scsi_task_t *task, uint32_t ioflags)
pdu = idm_pdu_alloc(sizeof (iscsi_hdr_t), resp_datalen);
idm_pdu_init(pdu, itask->it_ict->ict_ic, itask,
iscsit_send_status_done);
+ pdu->isp_flags |= IDM_PDU_SET_STATSN | IDM_PDU_ADVANCE_STATSN;
rsp = (iscsi_scsi_rsp_hdr_t *)pdu->isp_hdr;
bzero(rsp, sizeof (*rsp));
@@ -2009,6 +2081,13 @@ iscsit_send_direct_scsi_resp(iscsit_conn_t *ict, idm_pdu_t *rx_pdu,
rsp_pdu = idm_pdu_alloc(sizeof (iscsi_scsi_rsp_hdr_t), 0);
idm_pdu_init(rsp_pdu, ic, NULL, NULL);
+ /*
+ * StatSN is incremented by 1 for every response sent on
+ * a connection except for responses sent as a result of
+ * a retry or SNACK
+ */
+ rsp_pdu->isp_flags |= IDM_PDU_SET_STATSN | IDM_PDU_ADVANCE_STATSN;
+
resp = (iscsi_scsi_rsp_hdr_t *)rsp_pdu->isp_hdr;
resp->opcode = ISCSI_OP_SCSI_RSP;
@@ -2038,6 +2117,13 @@ iscsit_send_task_mgmt_resp(idm_pdu_t *tm_resp_pdu, uint8_t tm_status)
{
iscsi_scsi_task_mgt_rsp_hdr_t *tm_resp;
+ /*
+ * The target must take note of the last-sent StatSN.
+ * The StatSN is to be incremented after sending a
+ * task management response. Digest recovery can only
+ * work if StatSN is incremented.
+ */
+ tm_resp_pdu->isp_flags |= IDM_PDU_SET_STATSN | IDM_PDU_ADVANCE_STATSN;
tm_resp = (iscsi_scsi_task_mgt_rsp_hdr_t *)tm_resp_pdu->isp_hdr;
tm_resp->response = tm_status;
@@ -2261,6 +2347,14 @@ iscsit_pdu_op_noop(iscsit_conn_t *ict, idm_pdu_t *rx_pdu)
bcopy(rx_pdu->isp_data, resp->isp_data, resp_datalen);
}
+ /*
+ * When sending a NOP-In as a response to a NOP-Out from the initiator,
+ * the target must respond with the same initiator task tag that was
+ * provided in the NOP-Out request, the target transfer tag must be
+ * ISCSI_RSVD_TASK_TAG (0xffffffff) and StatSN will contain the next
+ * status sequence number. The StatSN for the connection is advanced
+ * after this PDU is sent.
+ */
in = (iscsi_nop_in_hdr_t *)resp->isp_hdr;
bzero(in, sizeof (*in));
in->opcode = ISCSI_OP_NOOP_IN;
@@ -2269,7 +2363,7 @@ iscsit_pdu_op_noop(iscsit_conn_t *ict, idm_pdu_t *rx_pdu)
in->itt = out->itt;
in->ttt = ISCSI_RSVD_TASK_TAG;
hton24(in->dlength, resp_datalen);
-
+ resp->isp_flags |= IDM_PDU_SET_STATSN | IDM_PDU_ADVANCE_STATSN;
/* Any other field in resp to be set? */
iscsit_pdu_tx(resp);
idm_pdu_complete(rx_pdu, IDM_STATUS_SUCCESS);
@@ -2297,7 +2391,12 @@ iscsit_pdu_op_logout_cmd(iscsit_conn_t *ict, idm_pdu_t *rx_pdu)
/* Allocate a PDU to respond */
resp = idm_pdu_alloc(sizeof (iscsi_hdr_t), 0);
idm_pdu_init(resp, ict->ict_ic, NULL, NULL);
-
+ /*
+ * The StatSN is to be sent to the initiator,
+ * it is not required to increment the number
+ * as the connection is terminating.
+ */
+ resp->isp_flags |= IDM_PDU_SET_STATSN;
/*
* Logout results in the immediate termination of all tasks except
* if the logout reason is ISCSI_LOGOUT_REASON_RECOVERY. The
@@ -2350,64 +2449,26 @@ iscsit_set_cmdsn(iscsit_conn_t *ict, idm_pdu_t *rx_pdu)
}
/*
- * Update local StatSN and set SNs in response
- */
-static void
-iscsit_calc_rspsn(iscsit_conn_t *ict, idm_pdu_t *resp)
-{
- iscsit_sess_t *ist;
- iscsi_scsi_rsp_hdr_t *rsp;
-
- /* Get iSCSI session handle */
- ist = ict->ict_sess;
-
- rsp = (iscsi_scsi_rsp_hdr_t *)resp->isp_hdr;
-
- /* Update StatSN */
- rsp->statsn = htonl(ict->ict_statsn);
- switch (IDM_PDU_OPCODE(resp)) {
- case ISCSI_OP_RTT_RSP:
- /* Do nothing */
- break;
- case ISCSI_OP_NOOP_IN:
- /*
- * Refer to section 10.19.1, RFC3720.
- * Advance only if target is responding initiator
- */
- if (((iscsi_nop_in_hdr_t *)rsp)->ttt == ISCSI_RSVD_TASK_TAG)
- ict->ict_statsn++;
- break;
- case ISCSI_OP_SCSI_DATA_RSP:
- if (rsp->flags & ISCSI_FLAG_DATA_STATUS)
- ict->ict_statsn++;
- else
- rsp->statsn = 0;
- break;
- default:
- ict->ict_statsn++;
- break;
- }
-
- /* Set ExpCmdSN and MaxCmdSN */
- rsp->maxcmdsn = htonl(ist->ist_maxcmdsn);
- rsp->expcmdsn = htonl(ist->ist_expcmdsn);
-}
-
-/*
* Wrapper funtion, calls iscsi_calc_rspsn and idm_pdu_tx
*/
void
iscsit_pdu_tx(idm_pdu_t *pdu)
{
iscsit_conn_t *ict = pdu->isp_ic->ic_handle;
+ iscsi_scsi_rsp_hdr_t *rsp = (iscsi_scsi_rsp_hdr_t *)pdu->isp_hdr;
+ iscsit_sess_t *ist = ict->ict_sess;
/*
- * Protect ict->ict_statsn, ist->ist_maxcmdsn, and ist->ist_expcmdsn
- * (which are used by iscsit_calc_rspsn) with the session mutex
- * (ist->ist_sn_mutex).
+ * The command sequence numbers are session-wide and must stay
+ * consistent across the transfer, so protect the cmdsn with a
+ * reader lock on the session. The status sequence number will
+ * be updated just before the transport layer transmits the PDU.
*/
- rw_enter(&ict->ict_sess->ist_sn_rwlock, RW_WRITER);
- iscsit_calc_rspsn(ict, pdu);
+
+ rw_enter(&ict->ict_sess->ist_sn_rwlock, RW_READER);
+ /* Set ExpCmdSN and MaxCmdSN */
+ rsp->maxcmdsn = htonl(ist->ist_maxcmdsn);
+ rsp->expcmdsn = htonl(ist->ist_expcmdsn);
idm_pdu_tx(pdu);
rw_exit(&ict->ict_sess->ist_sn_rwlock);
}
@@ -2431,8 +2492,14 @@ iscsit_send_async_event(iscsit_conn_t *ict, uint8_t event)
return;
}
+ /*
+ * A asynchronous message is sent by the target to request a logout.
+ * The StatSN for the connection is advanced after the PDU is sent
+ * to allow for initiator and target state synchronization.
+ */
idm_pdu_init(abt, ict->ict_ic, NULL, NULL);
abt->isp_datalen = 0;
+ abt->isp_flags |= IDM_PDU_SET_STATSN | IDM_PDU_ADVANCE_STATSN;
async_abt = (iscsi_async_evt_hdr_t *)abt->isp_hdr;
bzero(async_abt, sizeof (*async_abt));
@@ -2475,7 +2542,8 @@ iscsit_send_reject(iscsit_conn_t *ict, idm_pdu_t *rejected_pdu, uint8_t reason)
return;
}
idm_pdu_init(reject_pdu, ict->ict_ic, NULL, NULL);
-
+ /* StatSN is advanced after a Reject PDU */
+ reject_pdu->isp_flags |= IDM_PDU_SET_STATSN | IDM_PDU_ADVANCE_STATSN;
reject_pdu->isp_datalen = rejected_pdu->isp_hdrlen;
bcopy(rejected_pdu->isp_hdr, reject_pdu->isp_data,
rejected_pdu->isp_hdrlen);
diff --git a/usr/src/uts/common/io/comstar/port/iscsit/iscsit.h b/usr/src/uts/common/io/comstar/port/iscsit/iscsit.h
index ac1be1683a..eea078a31d 100644
--- a/usr/src/uts/common/io/comstar/port/iscsit/iscsit.h
+++ b/usr/src/uts/common/io/comstar/port/iscsit/iscsit.h
@@ -491,6 +491,7 @@ typedef struct iscsit_conn_s {
iscsit_op_params_t ict_op;
uint16_t ict_cid;
uint32_t ict_statsn;
+ kmutex_t ict_statsn_mutex;
uint32_t ict_keepalive_ttt;
struct iscsit_conn_s *ict_reinstate_conn;
uint32_t ict_reinstating:1,
@@ -622,6 +623,7 @@ idm_rx_pdu_error_cb_t iscsit_rx_pdu_error;
idm_task_cb_t iscsit_task_aborted;
idm_client_notify_cb_t iscsit_client_notify;
idm_build_hdr_cb_t iscsit_build_hdr;
+idm_update_statsn_cb_t iscsit_update_statsn;
idm_keepalive_cb_t iscsit_keepalive;
/*
diff --git a/usr/src/uts/common/io/comstar/port/iscsit/iscsit_login.c b/usr/src/uts/common/io/comstar/port/iscsit/iscsit_login.c
index bd726eb9e5..dba720a3f6 100644
--- a/usr/src/uts/common/io/comstar/port/iscsit/iscsit_login.c
+++ b/usr/src/uts/common/io/comstar/port/iscsit/iscsit_login.c
@@ -890,8 +890,8 @@ login_sm_send_next_response(iscsit_conn_t *ict, idm_pdu_t *pdu)
hton24(lh_resp->dlength, pdu->isp_datalen);
/*
- * If this is going to be the last PDU of a login response
- * that moves us to FFP then generate the ILE_LOGIN_FFP event.
+ * If the login is successful, this login response will contain
+ * the next StatSN and advance the StatSN for the connection.
*/
if (lh_resp->status_class == ISCSI_STATUS_CLASS_SUCCESS) {
ASSERT(ict->ict_sess != NULL);
@@ -911,6 +911,7 @@ login_sm_send_next_response(iscsit_conn_t *ict, idm_pdu_t *pdu)
}
iscsit_conn_hold(ict);
+ pdu->isp_flags |= IDM_PDU_SET_STATSN | IDM_PDU_ADVANCE_STATSN;
iscsit_pdu_tx(pdu);
} else {
/*
diff --git a/usr/src/uts/common/io/comstar/port/iscsit/iscsit_text.c b/usr/src/uts/common/io/comstar/port/iscsit/iscsit_text.c
index 05f78f3f3e..c9111f42d4 100644
--- a/usr/src/uts/common/io/comstar/port/iscsit/iscsit_text.c
+++ b/usr/src/uts/common/io/comstar/port/iscsit/iscsit_text.c
@@ -397,6 +397,8 @@ iscsit_send_next_text_response(iscsit_conn_t *ict, idm_pdu_t *rx_pdu)
*/
resp = idm_pdu_alloc(sizeof (iscsi_hdr_t), len);
idm_pdu_init(resp, ict->ict_ic, ict, iscsit_text_resp_complete_cb);
+ /* Advance the StatSN for each Text Response sent */
+ resp->isp_flags |= IDM_PDU_SET_STATSN | IDM_PDU_ADVANCE_STATSN;
base = ict->ict_text_rsp_buf + ict->ict_text_rsp_off;
bcopy(base, resp->isp_data, len);
/*
diff --git a/usr/src/uts/common/io/comstar/port/iscsit/iscsit_tgt.c b/usr/src/uts/common/io/comstar/port/iscsit/iscsit_tgt.c
index eeadd82943..8700ec544c 100644
--- a/usr/src/uts/common/io/comstar/port/iscsit/iscsit_tgt.c
+++ b/usr/src/uts/common/io/comstar/port/iscsit/iscsit_tgt.c
@@ -1948,6 +1948,8 @@ iscsit_portal_online(iscsit_portal_t *portal)
sr.sr_conn_ops.icb_client_notify =
&iscsit_client_notify;
sr.sr_conn_ops.icb_build_hdr = &iscsit_build_hdr;
+ sr.sr_conn_ops.icb_update_statsn =
+ &iscsit_update_statsn;
sr.sr_conn_ops.icb_keepalive = &iscsit_keepalive;
if (idm_tgt_svc_create(&sr, &svc) !=
diff --git a/usr/src/uts/common/io/ib/clients/iser/iser_xfer.c b/usr/src/uts/common/io/ib/clients/iser/iser_xfer.c
index 9b4c09bc47..02661b55fa 100644
--- a/usr/src/uts/common/io/ib/clients/iser/iser_xfer.c
+++ b/usr/src/uts/common/io/ib/clients/iser/iser_xfer.c
@@ -246,10 +246,22 @@ iser_xfer_ctrlpdu(iser_chan_t *chan, idm_pdu_t *pdu)
return (ISER_STATUS_FAIL);
}
+ ic = chan->ic_conn->ic_idmc;
+
+ /* Pull the BHS out of the PDU handle */
+ bhs = (iscsi_data_hdr_t *)pdu->isp_hdr;
+
/*
* All SCSI command PDU (except SCSI Read and SCSI Write) and the SCSI
* Response PDU are sent to the remote end using the SendSE Message.
*
+ * The StatSN may need to be sent (and possibly advanced) at this time
+ * for some PDUs, identified by the IDM_PDU_SET_STATSN flag.
+ */
+ if (pdu->isp_flags & IDM_PDU_SET_STATSN) {
+ (ic->ic_conn_ops.icb_update_statsn)(NULL, pdu);
+ }
+ /*
* Setup a Send Message for carrying the iSCSI control-type PDU
* preceeded by an iSER header.
*/
@@ -268,11 +280,6 @@ iser_xfer_ctrlpdu(iser_chan_t *chan, idm_pdu_t *pdu)
return (ISER_STATUS_FAIL);
}
- /* Pull the BHS out of the PDU handle */
- bhs = (iscsi_data_hdr_t *)pdu->isp_hdr;
-
- ic = chan->ic_conn->ic_idmc;
-
hdr = (iser_ctrl_hdr_t *)(uintptr_t)msg->msg_ds.ds_va;
/*
diff --git a/usr/src/uts/common/io/idm/idm.c b/usr/src/uts/common/io/idm/idm.c
index 6538950282..6660910ba8 100644
--- a/usr/src/uts/common/io/idm/idm.c
+++ b/usr/src/uts/common/io/idm/idm.c
@@ -1285,7 +1285,7 @@ idm_task_alloc(idm_conn_t *ic)
idt->idt_private = NULL;
idt->idt_exp_datasn = 0;
idt->idt_exp_rttsn = 0;
-
+ idt->idt_flags = 0;
return (idt);
}
diff --git a/usr/src/uts/common/io/idm/idm_impl.c b/usr/src/uts/common/io/idm/idm_impl.c
index 1b3fa2e369..02ca53c2c6 100644
--- a/usr/src/uts/common/io/idm/idm_impl.c
+++ b/usr/src/uts/common/io/idm/idm_impl.c
@@ -1026,7 +1026,7 @@ idm_task_constructor(void *hdl, void *arg, int flags)
* transport that requires a different size, we'll revisit this.
*/
idt->idt_transport_hdr = (void *)(idt + 1); /* pointer arithmetic */
-
+ idt->idt_flags = 0;
return (0);
}
diff --git a/usr/src/uts/common/io/idm/idm_so.c b/usr/src/uts/common/io/idm/idm_so.c
index 6506591b77..12c04cf788 100644
--- a/usr/src/uts/common/io/idm/idm_so.c
+++ b/usr/src/uts/common/io/idm/idm_so.c
@@ -2298,8 +2298,10 @@ idm_i_so_tx(idm_pdu_t *pdu)
* DataSN starts with 0 for the first data PDU of an input command and advances
* by 1 for each subsequent data PDU. Each sequence will have its own F bit,
* which is set to 1 for the last data PDU of a sequence.
+ * If the initiator supports phase collapse, the status bit must be set along
+ * with the F bit to indicate that the status is shipped together with the last
+ * Data-In PDU.
*
- * Scope for Prototype build:
* The data PDUs within a sequence will be sent in order with the buffer offset
* in increasing order. i.e. initiator and target must have negotiated the
* "DataPDUInOrder" to "Yes". The order between sequences is not enforced.
@@ -2391,6 +2393,7 @@ idm_so_buf_rx_from_ini(idm_task_t *idt, idm_buf_t *idb)
pdu = kmem_cache_alloc(idm.idm_sotx_pdu_cache, KM_SLEEP);
pdu->isp_ic = idt->idt_ic;
+ pdu->isp_flags = 0; /* initialize isp_flags */
bzero(pdu->isp_hdr, sizeof (iscsi_rtt_hdr_t));
/* iSCSI layer fills the TTT, ITT, StatSN, ExpCmdSN, MaxCmdSN */
@@ -2588,6 +2591,7 @@ idm_so_send_buf_region(idm_task_t *idt, idm_buf_t *idb,
/* Data PDU headers will always be sizeof (iscsi_hdr_t) */
pdu = kmem_cache_alloc(idm.idm_sotx_pdu_cache, KM_SLEEP);
pdu->isp_ic = ic;
+ pdu->isp_flags = 0; /* initialize isp_flags */
/*
* We've already built a build a header template
@@ -2609,10 +2613,27 @@ idm_so_send_buf_region(idm_task_t *idt, idm_buf_t *idb,
hton24(bhs->dlength, chunk);
bhs->offset = htonl(idb->idb_bufoffset + data_offset);
+ /* setup data */
+ pdu->isp_data = (uint8_t *)idb->idb_buf + data_offset;
+ pdu->isp_datalen = (uint_t)chunk;
+
if (chunk == remainder) {
bhs->flags = ISCSI_FLAG_FINAL; /* F bit set to 1 */
+ /* Piggyback the status with the last data PDU */
+ if (idt->idt_flags & IDM_TASK_PHASECOLLAPSE_REQ) {
+ pdu->isp_flags |= IDM_PDU_SET_STATSN |
+ IDM_PDU_ADVANCE_STATSN;
+ (*idt->idt_ic->ic_conn_ops.icb_update_statsn)
+ (idt, pdu);
+ idt->idt_flags |=
+ IDM_TASK_PHASECOLLAPSE_SUCCESS;
+
+ }
}
+ remainder -= chunk;
+ data_offset += chunk;
+
/* Instrument the data-send DTrace probe. */
if (IDM_PDU_OPCODE(pdu) == ISCSI_OP_SCSI_DATA_RSP) {
DTRACE_ISCSI_2(data__send,
@@ -2620,11 +2641,6 @@ idm_so_send_buf_region(idm_task_t *idt, idm_buf_t *idb,
iscsi_data_rsp_hdr_t *,
(iscsi_data_rsp_hdr_t *)pdu->isp_hdr);
}
- /* setup data */
- pdu->isp_data = (uint8_t *)idb->idb_buf + data_offset;
- pdu->isp_datalen = (uint_t)chunk;
- remainder -= chunk;
- data_offset += chunk;
/*
* Now that we're done working with idt_exp_datasn,
@@ -2767,13 +2783,18 @@ idm_sotx_thread(void *arg)
mutex_exit(&so_conn->ic_tx_mutex);
switch (object->idm_tx_obj_magic) {
- case IDM_PDU_MAGIC:
+ case IDM_PDU_MAGIC: {
+ idm_pdu_t *pdu = (idm_pdu_t *)object;
DTRACE_PROBE2(soconn__tx__pdu, idm_conn_t *, ic,
idm_pdu_t *, (idm_pdu_t *)object);
+ if (pdu->isp_flags & IDM_PDU_SET_STATSN) {
+ /* No IDM task */
+ (ic->ic_conn_ops.icb_update_statsn)(NULL, pdu);
+ }
status = idm_i_so_tx((idm_pdu_t *)object);
break;
-
+ }
case IDM_BUF_MAGIC: {
idm_buf_t *idb = (idm_buf_t *)object;
idm_task_t *idt = idb->idb_task_binding;
diff --git a/usr/src/uts/common/sys/idm/idm.h b/usr/src/uts/common/sys/idm/idm.h
index ada422a1b0..0be365e42e 100644
--- a/usr/src/uts/common/sys/idm/idm.h
+++ b/usr/src/uts/common/sys/idm/idm.h
@@ -149,6 +149,9 @@ typedef void (idm_task_cb_t)(struct idm_task_s *task, idm_status_t status);
typedef void (idm_build_hdr_cb_t)(
struct idm_task_s *task, struct idm_pdu_s *pdu, uint8_t opcode);
+typedef void (idm_update_statsn_cb_t)(
+ struct idm_task_s *task, struct idm_pdu_s *pdu);
+
typedef void (idm_keepalive_cb_t)(struct idm_conn_s *ic);
typedef union idm_sockaddr {
@@ -169,6 +172,7 @@ typedef struct {
idm_task_cb_t *icb_task_aborted;
idm_client_notify_cb_t *icb_client_notify;
idm_build_hdr_cb_t *icb_build_hdr;
+ idm_update_statsn_cb_t *icb_update_statsn; /* advance statsn */
idm_keepalive_cb_t *icb_keepalive;
} idm_conn_ops_t;
diff --git a/usr/src/uts/common/sys/idm/idm_impl.h b/usr/src/uts/common/sys/idm/idm_impl.h
index ef507ed3be..c015216018 100644
--- a/usr/src/uts/common/sys/idm/idm_impl.h
+++ b/usr/src/uts/common/sys/idm/idm_impl.h
@@ -260,6 +260,7 @@ typedef struct idm_task_s {
*/
int idt_transport_hdrlen;
void *idt_transport_hdr;
+ uint32_t idt_flags; /* phase collapse */
} idm_task_t;
int idm_task_constructor(void *task_void, void *arg, int flags);
@@ -268,6 +269,9 @@ void idm_task_destructor(void *task_void, void *arg);
#define IDM_TASKIDS_MAX 16384
#define IDM_BUF_MAGIC 0x49425546 /* "IBUF" */
+#define IDM_TASK_PHASECOLLAPSE_REQ 0x00000001 /* request phase collapse */
+#define IDM_TASK_PHASECOLLAPSE_SUCCESS 0x00000002 /* phase collapse success */
+
/* Protect with task mutex */
typedef struct idm_buf_s {
uint32_t idb_magic; /* "IBUF" */
@@ -392,6 +396,8 @@ typedef struct {
#define IDM_PDU_ADDL_HDR 0x00000002
#define IDM_PDU_ADDL_DATA 0x00000004
#define IDM_PDU_LOGIN_TX 0x00000008
+#define IDM_PDU_SET_STATSN 0x00000010
+#define IDM_PDU_ADVANCE_STATSN 0x00000020
#define OSD_EXT_CDB_AHSLEN (200 - 15)
#define BIDI_AHS_LENGTH 5