#include <sys/socket.h>
#include <sys/t_kuser.h>
#include <sys/tihdr.h>
#include <sys/strsubr.h>
#include <netinet/tcp.h>
#include <sys/socketvar.h>
#include <sys/strsun.h>
#include <sys/scsi/generic/sense.h>
#include "iscsi.h"
#include <sys/iscsi_protocol.h>
#define ISCSI_INI_TASK_TTT 0xffffffff
#define ISCSI_CONN_TIEMOUT_DETECT 20
boolean_t iscsi_io_logging = B_FALSE;
#define ISCSI_CHECK_SCSI_READ(ICHK_CMD, ICHK_HDR, ICHK_LEN, ICHK_TYPE) \
if (idm_pattern_checking) { \
struct scsi_pkt *pkt = (ICHK_CMD)->cmd_un.scsi.pkt; \
if (((ICHK_HDR)->response == 0) && \
((ICHK_HDR)->cmd_status == 0) && \
((pkt->pkt_cdbp[0] == SCMD_READ_G1) || \
(pkt->pkt_cdbp[0] == SCMD_READ_G4) || \
(pkt->pkt_cdbp[0] == SCMD_READ) || \
(pkt->pkt_cdbp[0] == SCMD_READ_G5))) { \
idm_buf_t *idb = (ICHK_CMD)->cmd_un.scsi.ibp_ibuf; \
IDM_BUFPAT_CHECK(idb, ICHK_LEN, ICHK_TYPE); \
} \
}
#define ISCSI_ARQ_STATUS_NOSENSE_LEN (sizeof (struct scsi_arq_status) - \
sizeof (struct scsi_extended_sense))
static uint32_t n2h24(uchar_t *ptr);
static int iscsi_sna_lt(uint32_t n1, uint32_t n2);
void iscsi_update_flow_control(iscsi_sess_t *isp,
uint32_t max, uint32_t exp);
static iscsi_status_t iscsi_rx_process_scsi_itt_to_icmdp(iscsi_sess_t *isp,
idm_conn_t *ic, iscsi_scsi_rsp_hdr_t *ihp, iscsi_cmd_t **icmdp);
static iscsi_status_t iscsi_rx_process_itt_to_icmdp(iscsi_sess_t *isp,
iscsi_hdr_t *ihp, iscsi_cmd_t **icmdp);
static void iscsi_process_rsp_status(iscsi_sess_t *isp, iscsi_conn_t *icp,
idm_status_t status);
static void iscsi_drop_conn_cleanup(iscsi_conn_t *icp);
static boolean_t iscsi_nop_timeout_checks(iscsi_cmd_t *icmdp);
static idm_pdu_cb_t iscsi_tx_done;
static idm_status_t iscsi_rx_process_nop(idm_conn_t *ic, idm_pdu_t *pdu);
static idm_status_t iscsi_rx_process_data_rsp(idm_conn_t *ic,
idm_pdu_t *pdu);
static idm_status_t iscsi_rx_process_cmd_rsp(idm_conn_t *ic, idm_pdu_t *pdu);
static idm_status_t iscsi_rx_process_reject_rsp(idm_conn_t *ic,
idm_pdu_t *pdu);
static idm_status_t iscsi_rx_process_rejected_tsk_mgt(idm_conn_t *ic,
iscsi_hdr_t *old_ihp);
static idm_status_t iscsi_rx_process_task_mgt_rsp(idm_conn_t *ic,
idm_pdu_t *pdu);
static idm_status_t iscsi_rx_process_logout_rsp(idm_conn_t *ic,
idm_pdu_t *pdu);
static idm_status_t iscsi_rx_process_async_rsp(idm_conn_t *ic,
idm_pdu_t *pdu);
static idm_status_t iscsi_rx_process_text_rsp(idm_conn_t *ic,
idm_pdu_t *pdu);
static iscsi_status_t iscsi_tx_scsi(iscsi_sess_t *isp, iscsi_cmd_t *icmdp);
static iscsi_status_t iscsi_tx_nop(iscsi_sess_t *isp, iscsi_cmd_t *icmdp);
static iscsi_status_t iscsi_tx_abort(iscsi_sess_t *isp, iscsi_cmd_t *icmdp);
static iscsi_status_t iscsi_tx_reset(iscsi_sess_t *isp, iscsi_cmd_t *icmdp);
static iscsi_status_t iscsi_tx_logout(iscsi_sess_t *isp, iscsi_cmd_t *icmdp);
static iscsi_status_t iscsi_tx_text(iscsi_sess_t *isp, iscsi_cmd_t *icmdp);
static void iscsi_logout_start(void *arg);
static void iscsi_handle_passthru_callback(struct scsi_pkt *pkt);
static void iscsi_handle_nop(iscsi_conn_t *icp, uint32_t itt, uint32_t ttt);
static void iscsi_timeout_checks(iscsi_sess_t *isp);
static void iscsi_nop_checks(iscsi_sess_t *isp);
static boolean_t iscsi_decode_sense(uint8_t *sense_data, iscsi_cmd_t *icmdp);
static void iscsi_flush_cmd_after_reset(uint32_t cmd_sn, uint16_t lun_num,
iscsi_conn_t *icp);
static uint32_t
n2h24(uchar_t *ptr)
{
uint32_t idx;
bcopy(ptr, &idx, 3);
return (ntohl(idx) >> 8);
}
static int
iscsi_sna_lt(uint32_t n1, uint32_t n2)
{
return ((n1 != n2) &&
(((n1 < n2) && ((n2 - n1) < ISCSI_SNA32_CHECK)) ||
((n1 > n2) && ((n1 - n2) > ISCSI_SNA32_CHECK))));
}
int
iscsi_sna_lte(uint32_t n1, uint32_t n2)
{
return ((n1 == n2) ||
(((n1 < n2) && ((n2 - n1) < ISCSI_SNA32_CHECK)) ||
((n1 > n2) && ((n1 - n2) > ISCSI_SNA32_CHECK))));
}
void
iscsi_update_flow_control(iscsi_sess_t *isp, uint32_t max, uint32_t exp)
{
ASSERT(isp != NULL);
ASSERT(mutex_owned(&isp->sess_cmdsn_mutex));
if (!iscsi_sna_lt(max, (exp - 1))) {
if (!iscsi_sna_lte(exp, isp->sess_expcmdsn)) {
isp->sess_expcmdsn = exp;
}
if (!iscsi_sna_lte(max, isp->sess_maxcmdsn)) {
isp->sess_maxcmdsn = max;
if (iscsi_sna_lte(isp->sess_cmdsn,
isp->sess_maxcmdsn)) {
iscsi_sess_redrive_io(isp);
}
}
}
}
void
iscsi_rx_scsi_rsp(idm_conn_t *ic, idm_pdu_t *pdu)
{
iscsi_conn_t *icp;
iscsi_sess_t *isp;
iscsi_hdr_t *ihp;
idm_status_t status;
ASSERT(ic != NULL);
ASSERT(pdu != NULL);
icp = ic->ic_handle;
ASSERT(icp != NULL);
ihp = (iscsi_hdr_t *)pdu->isp_hdr;
ASSERT(ihp != NULL);
isp = icp->conn_sess;
ASSERT(isp != NULL);
isp->sess_rx_lbolt = icp->conn_rx_lbolt = ddi_get_lbolt();
switch (ihp->opcode & ISCSI_OPCODE_MASK) {
case ISCSI_OP_SCSI_DATA_RSP:
status = iscsi_rx_process_data_rsp(ic, pdu);
break;
case ISCSI_OP_SCSI_RSP:
status = iscsi_rx_process_cmd_rsp(ic, pdu);
idm_pdu_complete(pdu, status);
break;
default:
cmn_err(CE_WARN, "iscsi connection(%u) protocol error - "
"received pdu with unsupported opcode 0x%02x",
icp->conn_oid, ihp->opcode);
status = IDM_STATUS_PROTOCOL_ERROR;
}
iscsi_process_rsp_status(isp, icp, status);
}
void
iscsi_task_cleanup(int opcode, iscsi_cmd_t *icmdp)
{
struct buf *bp;
idm_buf_t *ibp, *obp;
idm_task_t *itp;
itp = icmdp->cmd_itp;
ASSERT(itp != NULL);
ASSERT((opcode == ISCSI_OP_SCSI_DATA_RSP) ||
(opcode == ISCSI_OP_SCSI_RSP));
bp = icmdp->cmd_un.scsi.bp;
ibp = icmdp->cmd_un.scsi.ibp_ibuf;
obp = icmdp->cmd_un.scsi.ibp_obuf;
ISCSI_IO_LOG(CE_NOTE, "DEBUG: task_cleanup: itp: %p opcode: %d "
"icmdp: %p bp: %p ibp: %p", (void *)itp, opcode,
(void *)icmdp, (void *)bp, (void *)ibp);
if (bp && bp->b_bcount) {
if (ibp != NULL && bp->b_flags & B_READ) {
idm_buf_unbind_in(itp, ibp);
idm_buf_free(ibp);
icmdp->cmd_un.scsi.ibp_ibuf = NULL;
} else if (obp != NULL && !(bp->b_flags & B_READ)) {
idm_buf_unbind_out(itp, obp);
idm_buf_free(obp);
icmdp->cmd_un.scsi.ibp_obuf = NULL;
}
}
idm_task_done(itp);
}
idm_status_t
iscsi_rx_chk(iscsi_conn_t *icp, iscsi_sess_t *isp,
iscsi_scsi_rsp_hdr_t *irhp, iscsi_cmd_t **icmdp)
{
iscsi_status_t rval;
mutex_enter(&isp->sess_cmdsn_mutex);
if (icp->conn_expstatsn == ntohl(irhp->statsn)) {
icp->conn_expstatsn++;
} else {
cmn_err(CE_WARN, "iscsi connection(%u/%x) protocol error - "
"received status out of order itt:0x%x statsn:0x%x "
"expstatsn:0x%x", icp->conn_oid, irhp->opcode,
irhp->itt, ntohl(irhp->statsn), icp->conn_expstatsn);
mutex_exit(&isp->sess_cmdsn_mutex);
return (IDM_STATUS_PROTOCOL_ERROR);
}
if ((irhp->opcode == ISCSI_OP_SCSI_DATA_RSP) ||
(irhp->opcode == ISCSI_OP_SCSI_RSP)) {
rval = iscsi_rx_process_scsi_itt_to_icmdp(isp, icp->conn_ic,
irhp, icmdp);
} else {
rval = iscsi_rx_process_itt_to_icmdp(isp,
(iscsi_hdr_t *)irhp, icmdp);
}
if (!ISCSI_SUCCESS(rval)) {
mutex_exit(&isp->sess_cmdsn_mutex);
return (IDM_STATUS_PROTOCOL_ERROR);
}
iscsi_update_flow_control(isp, ntohl(irhp->maxcmdsn),
ntohl(irhp->expcmdsn));
mutex_exit(&isp->sess_cmdsn_mutex);
return (IDM_STATUS_SUCCESS);
}
static void
iscsi_cmd_rsp_chk(iscsi_cmd_t *icmdp, iscsi_scsi_rsp_hdr_t *issrhp)
{
struct scsi_pkt *pkt;
size_t data_transferred;
pkt = icmdp->cmd_un.scsi.pkt;
pkt->pkt_resid = 0;
data_transferred = icmdp->cmd_un.scsi.data_transferred;
if ((icmdp->cmd_un.scsi.bp) &&
(data_transferred != icmdp->cmd_un.scsi.bp->b_bcount)) {
if (issrhp->flags & ISCSI_FLAG_CMD_UNDERFLOW) {
pkt->pkt_resid = ntohl(issrhp->residual_count);
} else {
if (icmdp->cmd_un.scsi.bp->b_bcount >
data_transferred) {
pkt->pkt_resid =
icmdp->cmd_un.scsi.bp->b_bcount -
data_transferred;
}
}
ISCSI_IO_LOG(CE_NOTE,
"DEBUG: iscsi_rx_cmd_rsp_chk: itt: %u"
"data_trans != b_count data_transferred: %lu "
"b_count: %lu cmd_status: %d flags: %d resid: %lu",
issrhp->itt, data_transferred,
icmdp->cmd_un.scsi.bp->b_bcount,
issrhp->cmd_status & STATUS_MASK,
issrhp->flags, pkt->pkt_resid);
}
if (icmdp->cmd_crc_error_seen == B_FALSE) {
pkt->pkt_reason = CMD_CMPLT;
if (icmdp->cmd_un.scsi.bp) {
pkt->pkt_state |= (STATE_XFERRED_DATA |
STATE_GOT_STATUS);
} else {
pkt->pkt_state |= STATE_GOT_STATUS;
}
} else {
pkt->pkt_reason = CMD_PER_FAIL;
pkt->pkt_statistics |= STAT_PERR;
if (icmdp->cmd_un.scsi.bp) {
pkt->pkt_resid =
icmdp->cmd_un.scsi.bp->b_bcount;
} else {
pkt->pkt_resid = 0;
}
}
}
static boolean_t
iscsi_cmd_rsp_cmd_status(iscsi_cmd_t *icmdp, iscsi_scsi_rsp_hdr_t *issrhp,
uint8_t *data)
{
int32_t dlength;
struct scsi_arq_status *arqstat;
size_t senselen;
int32_t statuslen;
int32_t sensebuf_len;
struct scsi_pkt *pkt;
boolean_t affect = B_FALSE;
int32_t senselen_to_copy;
pkt = icmdp->cmd_un.scsi.pkt;
dlength = n2h24(issrhp->dlength);
switch (issrhp->cmd_status & STATUS_MASK) {
case STATUS_GOOD:
if (pkt->pkt_scbp) {
pkt->pkt_scbp[0] = issrhp->cmd_status;
}
break;
case STATUS_CHECK:
if ((dlength > 1) &&
(pkt->pkt_scbp != NULL) &&
(icmdp->cmd_un.scsi.statuslen >=
sizeof (struct scsi_arq_status))) {
pkt->pkt_resid = 0;
senselen = ((data[0] << 8) | data[1]) &
(size_t)0xFFFF;
ISCSI_IO_LOG(CE_NOTE,
"DEBUG: iscsi_rx_cmd_rsp_cmd_status status_check: "
"dlen: %d scbp: %p statuslen: %d arq: %d senselen:"
" %lu", dlength, (void *)pkt->pkt_scbp,
icmdp->cmd_un.scsi.statuslen,
(int)sizeof (struct scsi_arq_status),
senselen);
if ((senselen + 2) > dlength) {
senselen = dlength - 2;
}
if (icmdp->cmd_crc_error_seen) {
senselen = 0;
}
arqstat =
(struct scsi_arq_status *)pkt->pkt_scbp;
*((uchar_t *)&arqstat->sts_status) =
issrhp->cmd_status;
arqstat->sts_rqpkt_state = (STATE_GOT_BUS |
STATE_GOT_TARGET | STATE_SENT_CMD |
STATE_XFERRED_DATA | STATE_GOT_STATUS |
STATE_ARQ_DONE);
*((uchar_t *)&arqstat->sts_rqpkt_status) =
STATUS_GOOD;
arqstat->sts_rqpkt_reason = CMD_CMPLT;
statuslen = icmdp->cmd_un.scsi.statuslen;
if (senselen == 0) {
arqstat->sts_rqpkt_status.sts_chk = 1;
arqstat->sts_rqpkt_resid = statuslen;
} else if (senselen < statuslen) {
arqstat->sts_rqpkt_resid = statuslen - senselen;
} else {
arqstat->sts_rqpkt_resid = 0;
}
arqstat->sts_rqpkt_statistics = 0;
pkt->pkt_state |= STATE_ARQ_DONE;
if (icmdp->cmd_misc_flags & ISCSI_CMD_MISCFLAG_XARQ) {
pkt->pkt_state |= STATE_XARQ_DONE;
}
sensebuf_len = statuslen - ISCSI_ARQ_STATUS_NOSENSE_LEN;
senselen_to_copy = min(senselen, sensebuf_len);
if (senselen_to_copy > 0) {
bcopy(&data[2], (uchar_t *)&arqstat->
sts_sensedata, senselen_to_copy);
affect = iscsi_decode_sense(
(uint8_t *)&arqstat->sts_sensedata, icmdp);
}
arqstat->sts_rqpkt_resid = sensebuf_len -
senselen_to_copy;
ISCSI_IO_LOG(CE_NOTE, "DEBUG: iscsi_cmd_rsp_cmd_status:"
" sts_rqpkt_resid: %d pkt_scblen: %d senselen: %lu"
" sensebuf_len: %d senselen_to_copy: %d affect: %d",
arqstat->sts_rqpkt_resid, pkt->pkt_scblen, senselen,
sensebuf_len, senselen_to_copy, affect);
break;
}
case STATUS_BUSY:
case STATUS_RESERVATION_CONFLICT:
case STATUS_QFULL:
case STATUS_ACA_ACTIVE:
default:
ISCSI_IO_LOG(CE_NOTE,
"DEBUG: iscsi_rx_cmd_rsp_cmd_status: status: "
"%d cmd_status: %d dlen: %u scbp: %p statuslen: %d "
"arg_len: %d", issrhp->cmd_status & STATUS_MASK,
issrhp->cmd_status, dlength, (void *)pkt->pkt_scbp,
icmdp->cmd_un.scsi.statuslen,
(int)sizeof (struct scsi_arq_status));
pkt->pkt_resid = 0;
if (pkt->pkt_scbp) {
pkt->pkt_scbp[0] = issrhp->cmd_status;
}
}
return (affect);
}
idm_status_t
iscsi_rx_process_login_pdu(idm_conn_t *ic, idm_pdu_t *pdu)
{
iscsi_conn_t *icp;
icp = ic->ic_handle;
mutex_enter(&icp->conn_login_mutex);
if (icp->conn_login_state == LOGIN_TX) {
icp->conn_login_datalen = pdu->isp_datalen;
bcopy(pdu->isp_hdr, &icp->conn_login_resp_hdr,
sizeof (iscsi_hdr_t));
bzero(icp->conn_login_data, icp->conn_login_max_data_length);
bcopy(pdu->isp_data, icp->conn_login_data,
MIN(pdu->isp_datalen, icp->conn_login_max_data_length));
iscsi_login_update_state_locked(icp, LOGIN_RX);
}
mutex_exit(&icp->conn_login_mutex);
return (IDM_STATUS_SUCCESS);
}
static idm_status_t
iscsi_rx_process_cmd_rsp(idm_conn_t *ic, idm_pdu_t *pdu)
{
iscsi_conn_t *icp = ic->ic_handle;
iscsi_sess_t *isp = icp->conn_sess;
iscsi_scsi_rsp_hdr_t *issrhp = (iscsi_scsi_rsp_hdr_t *)pdu->isp_hdr;
uint8_t *data = pdu->isp_data;
iscsi_cmd_t *icmdp = NULL;
struct scsi_pkt *pkt = NULL;
idm_status_t rval;
struct buf *bp;
boolean_t flush = B_FALSE;
uint32_t cmd_sn = 0;
uint16_t lun_num = 0;
mutex_enter(&icp->conn_queue_active.mutex);
if ((rval = iscsi_rx_chk(icp, isp, issrhp,
&icmdp)) != IDM_STATUS_SUCCESS) {
if (icmdp != NULL) {
iscsi_task_cleanup(issrhp->opcode, icmdp);
}
mutex_exit(&icp->conn_queue_active.mutex);
return (rval);
}
mutex_enter(&icmdp->cmd_mutex);
if (icmdp->cmd_state == ISCSI_CMD_STATE_IDM_ABORTING) {
mutex_exit(&icmdp->cmd_mutex);
mutex_exit(&icp->conn_queue_active.mutex);
return (IDM_STATUS_SUCCESS);
}
mutex_exit(&icmdp->cmd_mutex);
bp = icmdp->cmd_un.scsi.bp;
if (ic->ic_conn_flags & IDM_CONN_USE_SCOREBOARD) {
if (bp && (bp->b_flags & B_READ)) {
icmdp->cmd_un.scsi.data_transferred +=
icmdp->cmd_itp->idt_rx_bytes;
} else {
icmdp->cmd_un.scsi.data_transferred +=
icmdp->cmd_itp->idt_tx_bytes;
}
} else {
if (issrhp->response != 0) {
icmdp->cmd_un.scsi.data_transferred = 0;
} else {
icmdp->cmd_un.scsi.data_transferred =
(bp == NULL) ? 0 : bp->b_bcount;
if (issrhp->flags & ISCSI_FLAG_CMD_UNDERFLOW) {
icmdp->cmd_un.scsi.data_transferred -=
ntohl(issrhp->residual_count);
}
}
}
ISCSI_CHECK_SCSI_READ(icmdp, issrhp,
icmdp->cmd_un.scsi.data_transferred,
BP_CHECK_THOROUGH);
ISCSI_IO_LOG(CE_NOTE, "DEBUG: rx_process_cmd_rsp: ic: %p pdu: %p itt:"
" %x expcmdsn: %x sess_cmd: %x sess_expcmdsn: %x data_transfered:"
" %lu ibp: %p obp: %p", (void *)ic, (void *)pdu, issrhp->itt,
issrhp->expcmdsn, isp->sess_cmdsn, isp->sess_expcmdsn,
icmdp->cmd_un.scsi.data_transferred,
(void *)icmdp->cmd_un.scsi.ibp_ibuf,
(void *)icmdp->cmd_un.scsi.ibp_obuf);
iscsi_task_cleanup(issrhp->opcode, icmdp);
if (issrhp->response) {
ISCSI_IO_LOG(CE_NOTE, "DEBUG: rx_process_cmd_rsp: ic: %p pdu:"
" %p response: %d bcount: %lu", (void *)ic, (void *)pdu,
issrhp->response, icmdp->cmd_un.scsi.bp->b_bcount);
pkt = icmdp->cmd_un.scsi.pkt;
pkt->pkt_reason = CMD_TRAN_ERR;
if (icmdp->cmd_un.scsi.bp) {
pkt->pkt_resid = icmdp->cmd_un.scsi.bp->b_bcount;
} else {
pkt->pkt_resid = 0;
}
} else {
iscsi_cmd_rsp_chk(icmdp, issrhp);
flush = iscsi_cmd_rsp_cmd_status(icmdp, issrhp, data);
ASSERT(icmdp->cmd_lun == NULL || icmdp->cmd_lun->lun_num ==
(icmdp->cmd_un.scsi.lun & ISCSI_LUN_MASK));
if (flush == B_TRUE) {
cmd_sn = icmdp->cmd_sn;
lun_num = icmdp->cmd_un.scsi.lun & ISCSI_LUN_MASK;
}
}
iscsi_cmd_state_machine(icmdp, ISCSI_CMD_EVENT_E3, isp);
if (flush == B_TRUE) {
iscsi_flush_cmd_after_reset(cmd_sn, lun_num, icp);
}
mutex_exit(&icp->conn_queue_active.mutex);
return (IDM_STATUS_SUCCESS);
}
static void
iscsi_data_rsp_pkt(iscsi_cmd_t *icmdp, iscsi_data_rsp_hdr_t *idrhp)
{
struct buf *bp = NULL;
size_t data_transferred;
struct scsi_pkt *pkt;
bp = icmdp->cmd_un.scsi.bp;
pkt = icmdp->cmd_un.scsi.pkt;
data_transferred = icmdp->cmd_un.scsi.data_transferred;
pkt->pkt_resid = 0;
if (bp && (data_transferred != bp->b_bcount)) {
if (idrhp->flags & ISCSI_FLAG_DATA_UNDERFLOW) {
pkt->pkt_resid = ntohl(idrhp->residual_count);
ISCSI_IO_LOG(CE_NOTE, "DEBUG: iscsi_data_rsp_pkt: "
"underflow: itt: %d "
"transferred: %lu count: %lu", idrhp->itt,
data_transferred, bp->b_bcount);
} else {
if (bp->b_bcount > data_transferred) {
ISCSI_IO_LOG(CE_NOTE, "DEBUG: "
"iscsi_data_rsp_pkt: data fell: itt: %d "
"transferred: %lu count: %lu", idrhp->itt,
data_transferred, bp->b_bcount);
pkt->pkt_resid =
bp->b_bcount - data_transferred;
}
}
}
pkt->pkt_reason = CMD_CMPLT;
pkt->pkt_state |= (STATE_XFERRED_DATA | STATE_GOT_STATUS);
if (((idrhp->cmd_status & STATUS_MASK) != STATUS_GOOD) &&
(icmdp->cmd_un.scsi.statuslen >=
sizeof (struct scsi_arq_status)) && pkt->pkt_scbp) {
struct scsi_arq_status *arqstat =
(struct scsi_arq_status *)pkt->pkt_scbp;
bzero(arqstat, sizeof (struct scsi_arq_status));
*((uchar_t *)&arqstat->sts_status) =
idrhp->cmd_status;
arqstat->sts_rqpkt_resid = icmdp->cmd_un.scsi.statuslen -
ISCSI_ARQ_STATUS_NOSENSE_LEN;
ISCSI_IO_LOG(CE_NOTE, "DEBUG: iscsi_data_rsp_pkt: "
"exception status: itt: %d resid: %d",
idrhp->itt, arqstat->sts_rqpkt_resid);
} else if (pkt->pkt_scbp) {
pkt->pkt_scbp[0] = idrhp->cmd_status;
}
}
static idm_status_t
iscsi_rx_process_data_rsp(idm_conn_t *ic, idm_pdu_t *pdu)
{
iscsi_sess_t *isp = NULL;
iscsi_data_rsp_hdr_t *idrhp = (iscsi_data_rsp_hdr_t *)pdu->isp_hdr;
iscsi_cmd_t *icmdp = NULL;
struct buf *bp = NULL;
iscsi_conn_t *icp = ic->ic_handle;
idm_buf_t *ibp;
idm_status_t rval;
ASSERT(idrhp->flags & ISCSI_FLAG_DATA_STATUS);
isp = icp->conn_sess;
mutex_enter(&icp->conn_queue_active.mutex);
if ((rval = iscsi_rx_chk(icp, isp, (iscsi_scsi_rsp_hdr_t *)idrhp,
&icmdp)) != IDM_STATUS_SUCCESS) {
if (icmdp != NULL) {
iscsi_task_cleanup(idrhp->opcode, icmdp);
}
mutex_exit(&icp->conn_queue_active.mutex);
return (rval);
}
mutex_enter(&icmdp->cmd_mutex);
if (icmdp->cmd_state == ISCSI_CMD_STATE_IDM_ABORTING) {
mutex_exit(&icmdp->cmd_mutex);
mutex_exit(&icp->conn_queue_active.mutex);
return (IDM_STATUS_SUCCESS);
}
mutex_exit(&icmdp->cmd_mutex);
iscsi_dequeue_active_cmd(icp, icmdp);
mutex_exit(&icp->conn_queue_active.mutex);
bp = icmdp->cmd_un.scsi.bp;
if (bp != NULL) {
if (!(bp->b_flags & B_READ)) {
cmn_err(CE_WARN,
"iscsi connection(%u) protocol error - "
"received data response during write operation "
"itt:0x%x",
icp->conn_oid, idrhp->itt);
mutex_enter(&icp->conn_queue_active.mutex);
iscsi_enqueue_active_cmd(icp, icmdp);
mutex_exit(&icp->conn_queue_active.mutex);
return (IDM_STATUS_PROTOCOL_ERROR);
}
}
ibp = icmdp->cmd_un.scsi.ibp_ibuf;
if (ibp == NULL) {
mutex_enter(&icp->conn_queue_active.mutex);
iscsi_enqueue_active_cmd(icp, icmdp);
mutex_exit(&icp->conn_queue_active.mutex);
return (IDM_STATUS_FAIL);
}
if (ic->ic_conn_flags & IDM_CONN_USE_SCOREBOARD) {
icmdp->cmd_un.scsi.data_transferred =
icmdp->cmd_itp->idt_rx_bytes;
} else {
icmdp->cmd_un.scsi.data_transferred = bp->b_bcount;
if (idrhp->flags & ISCSI_FLAG_CMD_UNDERFLOW) {
icmdp->cmd_un.scsi.data_transferred -=
ntohl(idrhp->residual_count);
}
}
ISCSI_IO_LOG(CE_NOTE, "DEBUG: rx_process_data_rsp: icp: %p pdu: %p "
"itt: %d ibp: %p icmdp: %p xfer_len: %lu transferred: %lu dlen: %u",
(void *)icp, (void *)pdu, idrhp->itt, (void *)bp, (void *)icmdp,
(ibp == NULL) ? 0 : ibp->idb_xfer_len,
icmdp->cmd_un.scsi.data_transferred,
n2h24(idrhp->dlength));
iscsi_task_cleanup(idrhp->opcode, icmdp);
iscsi_data_rsp_pkt(icmdp, idrhp);
mutex_enter(&icp->conn_queue_active.mutex);
iscsi_enqueue_active_cmd(icp, icmdp);
iscsi_cmd_state_machine(icmdp, ISCSI_CMD_EVENT_E3, isp);
mutex_exit(&icp->conn_queue_active.mutex);
return (IDM_STATUS_SUCCESS);
}
static idm_status_t
iscsi_rx_process_nop(idm_conn_t *ic, idm_pdu_t *pdu)
{
iscsi_sess_t *isp = NULL;
iscsi_nop_in_hdr_t *inihp = (iscsi_nop_in_hdr_t *)pdu->isp_hdr;
iscsi_cmd_t *icmdp = NULL;
iscsi_conn_t *icp = ic->ic_handle;
if (icp->conn_expstatsn != ntohl(inihp->statsn)) {
cmn_err(CE_WARN, "iscsi connection(%u/%x) protocol error - "
"received status out of order itt:0x%x statsn:0x%x "
"expstatsn:0x%x", icp->conn_oid, inihp->opcode, inihp->itt,
ntohl(inihp->statsn), icp->conn_expstatsn);
return (IDM_STATUS_PROTOCOL_ERROR);
}
isp = icp->conn_sess;
ASSERT(isp != NULL);
mutex_enter(&isp->sess_queue_pending.mutex);
mutex_enter(&icp->conn_queue_active.mutex);
mutex_enter(&isp->sess_cmdsn_mutex);
if (inihp->itt != ISCSI_RSVD_TASK_TAG) {
if (!ISCSI_SUCCESS(iscsi_rx_process_itt_to_icmdp(
isp, (iscsi_hdr_t *)inihp, &icmdp))) {
cmn_err(CE_WARN, "iscsi connection(%u) protocol error "
"- can not find cmd for itt:0x%x",
icp->conn_oid, inihp->itt);
mutex_exit(&isp->sess_cmdsn_mutex);
mutex_exit(&icp->conn_queue_active.mutex);
mutex_exit(&isp->sess_queue_pending.mutex);
return (IDM_STATUS_PROTOCOL_ERROR);
}
}
iscsi_update_flow_control(isp, ntohl(inihp->maxcmdsn),
ntohl(inihp->expcmdsn));
mutex_exit(&isp->sess_cmdsn_mutex);
if ((inihp->itt != ISCSI_RSVD_TASK_TAG) &&
(inihp->ttt == ISCSI_RSVD_TASK_TAG)) {
icp->conn_expstatsn++;
iscsi_cmd_state_machine(icmdp, ISCSI_CMD_EVENT_E3, isp);
} else if (inihp->ttt != ISCSI_RSVD_TASK_TAG) {
iscsi_handle_nop(icp, ISCSI_RSVD_TASK_TAG, inihp->ttt);
} else {
}
mutex_exit(&icp->conn_queue_active.mutex);
mutex_exit(&isp->sess_queue_pending.mutex);
return (IDM_STATUS_SUCCESS);
}
static idm_status_t
iscsi_rx_process_reject_rsp(idm_conn_t *ic, idm_pdu_t *pdu)
{
iscsi_reject_rsp_hdr_t *irrhp = (iscsi_reject_rsp_hdr_t *)pdu->isp_hdr;
iscsi_sess_t *isp = NULL;
uint32_t dlength = 0;
iscsi_hdr_t *old_ihp = NULL;
iscsi_conn_t *icp = ic->ic_handle;
uint8_t *data = pdu->isp_data;
idm_status_t status = IDM_STATUS_SUCCESS;
int i = 0;
ASSERT(data != NULL);
isp = icp->conn_sess;
ASSERT(isp != NULL);
for (i = 0; i < 4; i++) {
if (irrhp->must_be_ff[i] != 0xff) {
return (IDM_STATUS_PROTOCOL_ERROR);
}
}
mutex_enter(&isp->sess_cmdsn_mutex);
if (icp->conn_expstatsn == ntohl(irrhp->statsn)) {
icp->conn_expstatsn++;
} else {
cmn_err(CE_WARN, "iscsi connection(%u/%x) protocol error - "
"received status out of order statsn:0x%x "
"expstatsn:0x%x", icp->conn_oid, irrhp->opcode,
ntohl(irrhp->statsn), icp->conn_expstatsn);
mutex_exit(&isp->sess_cmdsn_mutex);
return (IDM_STATUS_PROTOCOL_ERROR);
}
iscsi_update_flow_control(isp, ntohl(irrhp->maxcmdsn),
ntohl(irrhp->expcmdsn));
mutex_exit(&isp->sess_cmdsn_mutex);
dlength = n2h24(irrhp->dlength);
if (dlength < sizeof (iscsi_hdr_t)) {
return (IDM_STATUS_PROTOCOL_ERROR);
}
old_ihp = (iscsi_hdr_t *)data;
switch (irrhp->reason) {
case ISCSI_REJECT_IMM_CMD_REJECT:
if (!(old_ihp->opcode & ISCSI_OP_IMMEDIATE)) {
return (IDM_STATUS_PROTOCOL_ERROR);
}
switch (old_ihp->opcode & ISCSI_OPCODE_MASK) {
case ISCSI_OP_NOOP_OUT:
break;
case ISCSI_OP_SCSI_TASK_MGT_MSG:
status =
iscsi_rx_process_rejected_tsk_mgt(ic, old_ihp);
break;
default:
cmn_err(CE_WARN, "iscsi connection(%u) protocol error "
"- received a reject for a command(0x%02x) not "
"sent as an immediate", icp->conn_oid,
old_ihp->opcode);
status = IDM_STATUS_PROTOCOL_ERROR;
break;
}
break;
case ISCSI_REJECT_DATA_DIGEST_ERROR:
case ISCSI_REJECT_CMD_BEFORE_LOGIN:
case ISCSI_REJECT_SNACK_REJECT:
case ISCSI_REJECT_PROTOCOL_ERROR:
case ISCSI_REJECT_CMD_NOT_SUPPORTED:
case ISCSI_REJECT_TASK_IN_PROGRESS:
case ISCSI_REJECT_INVALID_DATA_ACK:
case ISCSI_REJECT_INVALID_PDU_FIELD:
case ISCSI_REJECT_LONG_OPERATION_REJECT:
case ISCSI_REJECT_NEGOTIATION_RESET:
default:
cmn_err(CE_WARN, "iscsi connection(%u/%x) closing connection - "
"target requested reason:0x%x",
icp->conn_oid, irrhp->opcode, irrhp->reason);
status = IDM_STATUS_PROTOCOL_ERROR;
break;
}
return (status);
}
static idm_status_t
iscsi_rx_process_rejected_tsk_mgt(idm_conn_t *ic, iscsi_hdr_t *old_ihp)
{
iscsi_sess_t *isp = NULL;
iscsi_cmd_t *icmdp = NULL;
iscsi_conn_t *icp = ic->ic_handle;
isp = icp->conn_sess;
ASSERT(old_ihp != NULL);
ASSERT(isp != NULL);
mutex_enter(&icp->conn_queue_active.mutex);
mutex_enter(&isp->sess_cmdsn_mutex);
if (!ISCSI_SUCCESS(iscsi_rx_process_itt_to_icmdp(
isp, old_ihp, &icmdp))) {
mutex_exit(&isp->sess_cmdsn_mutex);
mutex_exit(&icp->conn_queue_active.mutex);
return (IDM_STATUS_PROTOCOL_ERROR);
}
mutex_exit(&isp->sess_cmdsn_mutex);
switch (icmdp->cmd_type) {
case ISCSI_CMD_TYPE_ABORT:
case ISCSI_CMD_TYPE_RESET:
iscsi_cmd_state_machine(icmdp, ISCSI_CMD_EVENT_E4,
icp->conn_sess);
break;
default:
ASSERT(B_FALSE);
break;
}
mutex_exit(&icp->conn_queue_active.mutex);
return (IDM_STATUS_SUCCESS);
}
static idm_status_t
iscsi_rx_process_task_mgt_rsp(idm_conn_t *ic, idm_pdu_t *pdu)
{
iscsi_sess_t *isp = NULL;
iscsi_scsi_task_mgt_rsp_hdr_t *istmrhp = NULL;
iscsi_cmd_t *icmdp = NULL;
iscsi_conn_t *icp = ic->ic_handle;
idm_status_t status = IDM_STATUS_SUCCESS;
isp = icp->conn_sess;
istmrhp = (iscsi_scsi_task_mgt_rsp_hdr_t *)pdu->isp_hdr;
mutex_enter(&icp->conn_queue_active.mutex);
if ((status = iscsi_rx_chk(icp, isp, (iscsi_scsi_rsp_hdr_t *)istmrhp,
&icmdp)) != IDM_STATUS_SUCCESS) {
mutex_exit(&icp->conn_queue_active.mutex);
return (status);
}
switch (icmdp->cmd_type) {
case ISCSI_CMD_TYPE_ABORT:
case ISCSI_CMD_TYPE_RESET:
switch (istmrhp->response) {
case SCSI_TCP_TM_RESP_COMPLETE:
iscsi_cmd_state_machine(icmdp,
ISCSI_CMD_EVENT_E3, isp);
break;
case SCSI_TCP_TM_RESP_NO_TASK:
if ((icmdp->cmd_type == ISCSI_CMD_TYPE_ABORT) &&
(icmdp->cmd_un.abort.icmdp == NULL)) {
iscsi_cmd_state_machine(icmdp,
ISCSI_CMD_EVENT_E4, isp);
break;
}
case SCSI_TCP_TM_RESP_REJECTED:
if (icmdp->cmd_type == ISCSI_CMD_TYPE_RESET) {
icmdp->cmd_un.reset.response =
istmrhp->response;
iscsi_cmd_state_machine(icmdp,
ISCSI_CMD_EVENT_E3, isp);
break;
}
case SCSI_TCP_TM_RESP_NO_LUN:
case SCSI_TCP_TM_RESP_TASK_ALLEGIANT:
case SCSI_TCP_TM_RESP_NO_FAILOVER:
case SCSI_TCP_TM_RESP_IN_PRGRESS:
default:
status = IDM_STATUS_PROTOCOL_ERROR;
}
break;
default:
cmn_err(CE_WARN, "iscsi connection(%u) protocol error - "
"received a task mgt response for a non-task mgt "
"cmd itt:0x%x type:%d", icp->conn_oid, istmrhp->itt,
icmdp->cmd_type);
status = IDM_STATUS_PROTOCOL_ERROR;
break;
}
mutex_exit(&icp->conn_queue_active.mutex);
return (status);
}
idm_status_t
iscsi_rx_process_logout_rsp(idm_conn_t *ic, idm_pdu_t *pdu)
{
iscsi_conn_t *icp = ic->ic_handle;
iscsi_logout_rsp_hdr_t *ilrhp =
(iscsi_logout_rsp_hdr_t *)pdu->isp_hdr;
iscsi_cmd_t *icmdp = NULL;
iscsi_sess_t *isp;
idm_status_t status = IDM_STATUS_SUCCESS;
isp = icp->conn_sess;
if (icp->conn_expstatsn != ntohl(ilrhp->statsn)) {
cmn_err(CE_WARN, "iscsi connection(%u/%x) protocol error - "
"received status out of order itt:0x%x statsn:0x%x "
"expstatsn:0x%x", icp->conn_oid, ilrhp->opcode, ilrhp->itt,
ntohl(ilrhp->statsn), icp->conn_expstatsn);
return (IDM_STATUS_PROTOCOL_ERROR);
}
mutex_enter(&icp->conn_queue_active.mutex);
mutex_enter(&isp->sess_cmdsn_mutex);
if (ilrhp->itt != ISCSI_RSVD_TASK_TAG) {
if (!ISCSI_SUCCESS(iscsi_rx_process_itt_to_icmdp(
isp, (iscsi_hdr_t *)ilrhp, &icmdp))) {
mutex_exit(&isp->sess_cmdsn_mutex);
mutex_exit(&icp->conn_queue_active.mutex);
return (IDM_STATUS_PROTOCOL_ERROR);
}
}
iscsi_update_flow_control(isp, ntohl(ilrhp->maxcmdsn),
ntohl(ilrhp->expcmdsn));
mutex_exit(&isp->sess_cmdsn_mutex);
ISCSI_IO_LOG(CE_NOTE,
"DEBUG: iscsi_rx_process_logout_rsp: response: %d",
ilrhp->response);
switch (ilrhp->response) {
case ISCSI_LOGOUT_CID_NOT_FOUND:
case ISCSI_LOGOUT_RECOVERY_UNSUPPORTED:
case ISCSI_LOGOUT_CLEANUP_FAILED:
case ISCSI_LOGOUT_SUCCESS:
iscsi_cmd_state_machine(icmdp, ISCSI_CMD_EVENT_E3, isp);
mutex_exit(&icp->conn_queue_active.mutex);
iscsi_drop_conn_cleanup(icp);
break;
default:
mutex_exit(&icp->conn_queue_active.mutex);
status = IDM_STATUS_PROTOCOL_ERROR;
break;
}
return (status);
}
static idm_status_t
iscsi_rx_process_async_rsp(idm_conn_t *ic, idm_pdu_t *pdu)
{
iscsi_conn_t *icp = ic->ic_handle;
iscsi_sess_t *isp = icp->conn_sess;
idm_status_t rval = IDM_STATUS_SUCCESS;
iscsi_task_t *itp;
iscsi_async_evt_hdr_t *iaehp =
(iscsi_async_evt_hdr_t *)pdu->isp_hdr;
ASSERT(icp != NULL);
ASSERT(pdu != NULL);
ASSERT(isp != NULL);
mutex_enter(&isp->sess_cmdsn_mutex);
if (icp->conn_expstatsn == ntohl(iaehp->statsn)) {
icp->conn_expstatsn++;
} else {
cmn_err(CE_WARN, "iscsi connection(%u) protocol error - "
"received status out of order statsn:0x%x "
"expstatsn:0x%x", icp->conn_oid,
ntohl(iaehp->statsn), icp->conn_expstatsn);
mutex_exit(&isp->sess_cmdsn_mutex);
return (IDM_STATUS_PROTOCOL_ERROR);
}
mutex_exit(&isp->sess_cmdsn_mutex);
switch (iaehp->async_event) {
case ISCSI_ASYNC_EVENT_SCSI_EVENT:
if (isp->sess_type == ISCSI_SESS_TYPE_NORMAL) {
rw_enter(&isp->sess_state_rwlock, RW_READER);
if (isp->sess_state == ISCSI_SESS_STATE_LOGGED_IN) {
(void) iscsi_sess_enum_request(isp, B_FALSE,
isp->sess_state_event_count);
}
rw_exit(&isp->sess_state_rwlock);
}
break;
case ISCSI_ASYNC_EVENT_REQUEST_LOGOUT:
mutex_enter(&icp->conn_state_mutex);
icp->conn_async_logout = B_TRUE;
mutex_exit(&icp->conn_state_mutex);
idm_conn_hold(ic);
itp = kmem_zalloc(sizeof (iscsi_task_t), KM_SLEEP);
itp->t_arg = icp;
itp->t_blocking = B_FALSE;
if (ddi_taskq_dispatch(isp->sess_login_taskq,
(void(*)())iscsi_logout_start, itp, DDI_SLEEP) !=
DDI_SUCCESS) {
idm_conn_rele(ic);
idm_ini_conn_disconnect(ic);
}
break;
case ISCSI_ASYNC_EVENT_DROPPING_CONNECTION:
iscsi_conn_set_login_min_max(icp, iaehp->param2,
iaehp->param3);
idm_ini_conn_disconnect(ic);
break;
case ISCSI_ASYNC_EVENT_DROPPING_ALL_CONNECTIONS:
iscsi_conn_set_login_min_max(icp, iaehp->param2,
iaehp->param3);
idm_ini_conn_disconnect(ic);
break;
case ISCSI_ASYNC_EVENT_PARAM_NEGOTIATION:
idm_conn_hold(ic);
itp = kmem_zalloc(sizeof (iscsi_task_t), KM_SLEEP);
itp->t_arg = icp;
itp->t_blocking = B_FALSE;
if (ddi_taskq_dispatch(isp->sess_login_taskq,
(void(*)())iscsi_logout_start, itp, DDI_SLEEP) !=
DDI_SUCCESS) {
idm_conn_rele(ic);
idm_ini_conn_disconnect(ic);
}
break;
case ISCSI_ASYNC_EVENT_VENDOR_SPECIFIC:
idm_ini_conn_disconnect(ic);
break;
default:
rval = IDM_STATUS_PROTOCOL_ERROR;
}
return (rval);
}
static idm_status_t
iscsi_rx_process_text_rsp(idm_conn_t *ic, idm_pdu_t *pdu)
{
iscsi_sess_t *isp = NULL;
iscsi_text_rsp_hdr_t *ithp =
(iscsi_text_rsp_hdr_t *)pdu->isp_hdr;
iscsi_conn_t *icp = ic->ic_handle;
iscsi_cmd_t *icmdp = NULL;
boolean_t final = B_FALSE;
uint32_t data_len;
uint8_t *data = pdu->isp_data;
idm_status_t rval;
isp = icp->conn_sess;
mutex_enter(&icp->conn_queue_active.mutex);
if ((rval = iscsi_rx_chk(icp, isp, (iscsi_scsi_rsp_hdr_t *)ithp,
&icmdp)) != IDM_STATUS_SUCCESS) {
mutex_exit(&icp->conn_queue_active.mutex);
return (rval);
}
if (ithp->flags & ISCSI_FLAG_FINAL) {
final = B_TRUE;
}
if (((final == B_TRUE) && (ithp->ttt != ISCSI_RSVD_TASK_TAG)) ||
((final == B_FALSE) && (ithp->ttt == ISCSI_RSVD_TASK_TAG))) {
icmdp->cmd_result = ISCSI_STATUS_PROTOCOL_ERROR;
icmdp->cmd_un.text.stage = ISCSI_CMD_TEXT_FINAL_RSP;
mutex_exit(&icp->conn_queue_active.mutex);
cmn_err(CE_WARN, "iscsi connection(%u) protocol error - "
"received text response with invalid flags:0x%x or "
"ttt:0x%x", icp->conn_oid, ithp->flags, ithp->itt);
return (IDM_STATUS_PROTOCOL_ERROR);
}
if ((icmdp->cmd_un.text.stage == ISCSI_CMD_TEXT_INITIAL_REQ) &&
(ithp->ttt == ISCSI_RSVD_TASK_TAG) &&
(final == B_FALSE)) {
icmdp->cmd_result = ISCSI_STATUS_PROTOCOL_ERROR;
icmdp->cmd_un.text.stage = ISCSI_CMD_TEXT_FINAL_RSP;
mutex_exit(&icp->conn_queue_active.mutex);
cmn_err(CE_WARN, "iscsi connection(%u) protocol "
"error - received text response with invalid "
"ttt:0x%x", icp->conn_oid, ithp->ttt);
return (IDM_STATUS_PROTOCOL_ERROR);
}
if (icmdp->cmd_un.text.stage == ISCSI_CMD_TEXT_INITIAL_REQ) {
icmdp->cmd_un.text.ttt = ithp->ttt;
}
data_len = ntoh24(ithp->dlength);
if ((icmdp->cmd_un.text.total_rx_len + data_len) >
icmdp->cmd_un.text.buf_len) {
icmdp->cmd_un.text.total_rx_len += data_len;
icmdp->cmd_result = ISCSI_STATUS_DATA_OVERFLOW;
} else {
char *buf_data = (icmdp->cmd_un.text.buf +
icmdp->cmd_un.text.offset);
bcopy(data, buf_data, data_len);
icmdp->cmd_un.text.offset += data_len;
icmdp->cmd_un.text.total_rx_len += data_len;
icmdp->cmd_result = ISCSI_STATUS_SUCCESS;
bcopy(ithp->rsvd4, icmdp->cmd_un.text.lun,
sizeof (icmdp->cmd_un.text.lun));
}
if (final == B_TRUE) {
icmdp->cmd_un.text.stage = ISCSI_CMD_TEXT_FINAL_RSP;
} else {
icmdp->cmd_un.text.stage = ISCSI_CMD_TEXT_CONTINUATION;
}
iscsi_cmd_state_machine(icmdp, ISCSI_CMD_EVENT_E3, isp);
mutex_exit(&icp->conn_queue_active.mutex);
return (IDM_STATUS_SUCCESS);
}
static iscsi_status_t
iscsi_rx_process_scsi_itt_to_icmdp(iscsi_sess_t *isp, idm_conn_t *ic,
iscsi_scsi_rsp_hdr_t *ihp, iscsi_cmd_t **icmdp)
{
idm_task_t *itp;
ASSERT(isp != NULL);
ASSERT(ihp != NULL);
ASSERT(icmdp != NULL);
ASSERT(mutex_owned(&isp->sess_cmdsn_mutex));
itp = idm_task_find_and_complete(ic, ihp->itt, ISCSI_INI_TASK_TTT);
if (itp == NULL) {
cmn_err(CE_WARN, "iscsi session(%u) protocol error - "
"received unknown itt:0x%x - protocol error",
isp->sess_oid, ihp->itt);
return (ISCSI_STATUS_INTERNAL_ERROR);
}
*icmdp = itp->idt_private;
idm_task_rele(itp);
return (ISCSI_STATUS_SUCCESS);
}
static iscsi_status_t
iscsi_rx_process_itt_to_icmdp(iscsi_sess_t *isp, iscsi_hdr_t *ihp,
iscsi_cmd_t **icmdp)
{
int cmd_table_idx = 0;
ASSERT(isp != NULL);
ASSERT(ihp != NULL);
ASSERT(icmdp != NULL);
ASSERT(mutex_owned(&isp->sess_cmdsn_mutex));
cmd_table_idx = (ihp->itt - IDM_TASKIDS_MAX) % ISCSI_CMD_TABLE_SIZE;
if (isp->sess_cmd_table[cmd_table_idx] == NULL) {
cmn_err(CE_WARN, "iscsi session(%u) protocol error - "
"received unknown itt:0x%x - protocol error",
isp->sess_oid, ihp->itt);
return (ISCSI_STATUS_INTERNAL_ERROR);
}
if (isp->sess_cmd_table[cmd_table_idx]->cmd_itt != ihp->itt) {
cmn_err(CE_WARN, "iscsi session(%u) received itt:0x%x "
" which is out of sync with itt:0x%x", isp->sess_oid,
ihp->itt, isp->sess_cmd_table[cmd_table_idx]->cmd_itt);
return (ISCSI_STATUS_INTERNAL_ERROR);
}
if (isp->sess_cmd_table[cmd_table_idx]->cmd_state !=
ISCSI_CMD_STATE_ACTIVE) {
cmn_err(CE_WARN, "iscsi session(%u) received itt:0x%x "
"but icmdp (%p) is not in active state",
isp->sess_oid, ihp->itt,
(void *)isp->sess_cmd_table[cmd_table_idx]);
return (ISCSI_STATUS_INTERNAL_ERROR);
}
*icmdp = isp->sess_cmd_table[cmd_table_idx];
return (ISCSI_STATUS_SUCCESS);
}
void
iscsi_tx_thread(iscsi_thread_t *thread, void *arg)
{
iscsi_conn_t *icp = (iscsi_conn_t *)arg;
iscsi_sess_t *isp = NULL;
iscsi_cmd_t *icmdp = NULL;
clock_t tout;
int ret = 1;
ASSERT(icp != NULL);
isp = icp->conn_sess;
ASSERT(isp != NULL);
ASSERT(thread != NULL);
ASSERT(thread->signature == SIG_ISCSI_THREAD);
tout = SEC_TO_TICK(1);
while (ret != 0) {
isp->sess_window_open = B_TRUE;
mutex_enter(&isp->sess_queue_pending.mutex);
while ((isp->sess_window_open == B_TRUE) &&
((icmdp = isp->sess_queue_pending.head) != NULL)) {
if (((icmdp->cmd_type != ISCSI_CMD_TYPE_SCSI) &&
(ISCSI_CONN_STATE_FULL_FEATURE(icp->conn_state))) ||
(icp->conn_state == ISCSI_CONN_STATE_LOGGED_IN)) {
icmdp->cmd_conn = icp;
iscsi_cmd_state_machine(icmdp,
ISCSI_CMD_EVENT_E2, isp);
ASSERT(!mutex_owned(
&isp->sess_queue_pending.mutex));
mutex_enter(&isp->sess_queue_pending.mutex);
} else {
while (icmdp != NULL) {
if ((icmdp->cmd_type !=
ISCSI_CMD_TYPE_SCSI) &&
(ISCSI_CONN_STATE_FULL_FEATURE
(icp->conn_state) != B_TRUE)) {
icmdp->cmd_misc_flags |=
ISCSI_CMD_MISCFLAG_STUCK;
} else if (icp->conn_state !=
ISCSI_CONN_STATE_LOGGED_IN) {
icmdp->cmd_misc_flags |=
ISCSI_CMD_MISCFLAG_STUCK;
}
icmdp = icmdp->cmd_next;
}
break;
}
}
mutex_exit(&isp->sess_queue_pending.mutex);
ret = iscsi_thread_wait(thread, tout);
}
}
iscsi_status_t
iscsi_tx_cmd(iscsi_sess_t *isp, iscsi_cmd_t *icmdp)
{
iscsi_status_t rval = ISCSI_STATUS_INTERNAL_ERROR;
ASSERT(isp != NULL);
ASSERT(icmdp != NULL);
switch (icmdp->cmd_type) {
case ISCSI_CMD_TYPE_SCSI:
rval = iscsi_tx_scsi(isp, icmdp);
break;
case ISCSI_CMD_TYPE_NOP:
rval = iscsi_tx_nop(isp, icmdp);
break;
case ISCSI_CMD_TYPE_ABORT:
rval = iscsi_tx_abort(isp, icmdp);
break;
case ISCSI_CMD_TYPE_RESET:
rval = iscsi_tx_reset(isp, icmdp);
break;
case ISCSI_CMD_TYPE_LOGOUT:
rval = iscsi_tx_logout(isp, icmdp);
break;
case ISCSI_CMD_TYPE_TEXT:
rval = iscsi_tx_text(isp, icmdp);
break;
default:
cmn_err(CE_WARN, "iscsi_tx_cmd: invalid cmdtype: %d",
icmdp->cmd_type);
ASSERT(FALSE);
}
ASSERT(!mutex_owned(&isp->sess_queue_pending.mutex));
return (rval);
}
#define DEF_CDB_LEN 200
#define ADDLHDRSZ(x) (sizeof (iscsi_addl_hdr_t) + (x) - \
16 - 4)
static void
iscsi_tx_init_hdr(iscsi_sess_t *isp, iscsi_conn_t *icp,
iscsi_text_hdr_t *ihp, int opcode, iscsi_cmd_t *icmdp)
{
ihp->opcode = opcode;
ihp->itt = icmdp->cmd_itt;
mutex_enter(&isp->sess_cmdsn_mutex);
icmdp->cmd_sn = isp->sess_cmdsn;
ihp->cmdsn = htonl(isp->sess_cmdsn);
isp->sess_cmdsn++;
mutex_exit(&isp->sess_cmdsn_mutex);
ihp->expstatsn = htonl(icp->conn_expstatsn);
icp->conn_laststatsn = icp->conn_expstatsn;
}
static void
iscsi_tx_scsi_data(iscsi_cmd_t *icmdp, iscsi_scsi_cmd_hdr_t *ihp,
iscsi_conn_t *icp, idm_pdu_t *pdu)
{
struct buf *bp = NULL;
size_t buflen = 0;
uint32_t first_burst_length = 0;
struct scsi_pkt *pkt;
pkt = icmdp->cmd_un.scsi.pkt;
bp = icmdp->cmd_un.scsi.bp;
if ((bp != NULL) && bp->b_bcount) {
buflen = bp->b_bcount;
first_burst_length =
icp->conn_params.first_burst_length;
if (bp->b_flags & B_READ) {
ihp->flags = ISCSI_FLAG_FINAL;
if ((pkt->pkt_cdbp[0] != SCMD_TEST_UNIT_READY) &&
(pkt->pkt_cdbp[0] != SCMD_START_STOP)) {
ihp->flags |= ISCSI_FLAG_CMD_READ;
ihp->data_length = htonl(buflen);
}
} else {
ihp->flags = ISCSI_FLAG_CMD_WRITE;
if (icp->conn_params.initial_r2t) {
ihp->flags |= ISCSI_FLAG_FINAL;
}
if (icp->conn_params.immediate_data) {
pdu->isp_data =
(uint8_t *)icmdp->
cmd_un.scsi.bp->b_un.b_addr;
pdu->isp_datalen = MIN(MIN(buflen,
first_burst_length),
icmdp->cmd_conn->conn_params.
max_xmit_data_seg_len);
if ((pdu->isp_datalen == buflen) ||
(pdu->isp_datalen == first_burst_length)) {
ihp->flags |= ISCSI_FLAG_FINAL;
}
hton24(ihp->dlength, pdu->isp_datalen);
}
ihp->data_length = htonl(buflen);
}
} else {
ihp->flags = ISCSI_FLAG_FINAL;
}
icmdp->cmd_un.scsi.data_transferred += pdu->isp_datalen;
if ((buflen > 0) &&
((bp->b_flags & B_READ) == 0) &&
(icp->conn_params.initial_r2t == 0) &&
pdu->isp_datalen == 0) {
pdu->isp_datalen = MIN(first_burst_length, buflen);
if ((pdu->isp_datalen == buflen) ||
(pdu->isp_datalen == first_burst_length)) {
ihp->flags |= ISCSI_FLAG_FINAL;
}
pdu->isp_data = (uint8_t *)icmdp->cmd_un.scsi.bp->b_un.b_addr;
hton24(ihp->dlength, pdu->isp_datalen);
}
}
static void
iscsi_tx_scsi_init_pkt(iscsi_cmd_t *icmdp, iscsi_scsi_cmd_hdr_t *ihp)
{
struct scsi_pkt *pkt;
pkt = icmdp->cmd_un.scsi.pkt;
pkt->pkt_state = (STATE_GOT_BUS | STATE_GOT_TARGET);
pkt->pkt_reason = CMD_INCOMPLETE;
if (pkt->pkt_flags & FLAG_HTAG) {
ihp->flags |= ISCSI_ATTR_HEAD_OF_QUEUE;
} else if (pkt->pkt_flags & FLAG_OTAG) {
ihp->flags |= ISCSI_ATTR_ORDERED;
} else if (pkt->pkt_flags & FLAG_STAG) {
ihp->flags |= ISCSI_ATTR_SIMPLE;
} else {
}
ISCSI_LUN_BYTE_COPY(ihp->lun, icmdp->cmd_un.scsi.lun);
if (icmdp->cmd_un.scsi.cmdlen <= 16) {
bcopy(pkt->pkt_cdbp, ihp->scb,
icmdp->cmd_un.scsi.cmdlen);
} else {
iscsi_addl_hdr_t *iahp;
iahp = (iscsi_addl_hdr_t *)ihp;
ihp->hlength = (ADDLHDRSZ(icmdp->cmd_un.scsi.cmdlen) -
sizeof (iscsi_scsi_cmd_hdr_t) + 3) / 4;
iahp->ahs_hlen_hi = 0;
iahp->ahs_hlen_lo = (icmdp->cmd_un.scsi.cmdlen - 15);
iahp->ahs_key = 0x01;
iahp->ahs_resv = 0;
bcopy(pkt->pkt_cdbp, ihp->scb, 16);
bcopy(((char *)pkt->pkt_cdbp) + 16, &iahp->ahs_extscb[0],
icmdp->cmd_un.scsi.cmdlen);
}
pkt->pkt_state |= STATE_SENT_CMD;
}
static void
iscsi_tx_scsi_init_task(iscsi_cmd_t *icmdp, iscsi_conn_t *icp,
iscsi_scsi_cmd_hdr_t *ihp)
{
idm_task_t *itp;
struct buf *bp = NULL;
uint32_t data_length;
bp = icmdp->cmd_un.scsi.bp;
itp = icmdp->cmd_itp;
ASSERT(itp != NULL);
data_length = ntohl(ihp->data_length);
ISCSI_IO_LOG(CE_NOTE,
"DEBUG: iscsi_tx_init_task: task_start: %p idt_tt: %x cmdsn: %x "
"sess_cmdsn: %x cmd: %p "
"cmdtype: %d datalen: %u",
(void *)itp, itp->idt_tt, ihp->cmdsn, icp->conn_sess->sess_cmdsn,
(void *)icmdp, icmdp->cmd_type, data_length);
if (data_length > 0) {
if (bp->b_flags & B_READ) {
icmdp->cmd_un.scsi.ibp_ibuf =
idm_buf_alloc(icp->conn_ic,
bp->b_un.b_addr, bp->b_bcount);
if (icmdp->cmd_un.scsi.ibp_ibuf)
idm_buf_bind_in(itp,
icmdp->cmd_un.scsi.ibp_ibuf);
} else {
icmdp->cmd_un.scsi.ibp_obuf =
idm_buf_alloc(icp->conn_ic,
bp->b_un.b_addr, bp->b_bcount);
if (icmdp->cmd_un.scsi.ibp_obuf)
idm_buf_bind_out(itp,
icmdp->cmd_un.scsi.ibp_obuf);
}
ISCSI_IO_LOG(CE_NOTE,
"DEBUG: pdu_tx: task_start(%s): %p ic: %p idt_tt: %x "
"cmdsn: %x sess_cmdsn: %x sess_expcmdsn: %x obuf: %p "
"cmdp: %p cmdtype: %d "
"buflen: %lu " "bpaddr: %p datalen: %u ",
bp->b_flags & B_READ ? "B_READ" : "B_WRITE",
(void *)itp, (void *)icp->conn_ic,
itp->idt_tt, ihp->cmdsn,
icp->conn_sess->sess_cmdsn,
icp->conn_sess->sess_expcmdsn,
(void *)icmdp->cmd_un.scsi.ibp_ibuf,
(void *)icmdp, icmdp->cmd_type, bp->b_bcount,
(void *)bp->b_un.b_addr,
data_length);
}
idm_task_start(itp, ISCSI_INI_TASK_TTT);
}
static iscsi_status_t
iscsi_tx_scsi(iscsi_sess_t *isp, iscsi_cmd_t *icmdp)
{
iscsi_status_t rval = ISCSI_STATUS_SUCCESS;
iscsi_conn_t *icp = NULL;
struct scsi_pkt *pkt = NULL;
iscsi_scsi_cmd_hdr_t *ihp = NULL;
int cdblen = 0;
idm_pdu_t *pdu;
int len;
ASSERT(isp != NULL);
ASSERT(icmdp != NULL);
pdu = kmem_zalloc(sizeof (idm_pdu_t), KM_SLEEP);
pkt = icmdp->cmd_un.scsi.pkt;
ASSERT(pkt != NULL);
icp = icmdp->cmd_conn;
ASSERT(icp != NULL);
icmdp->cmd_un.scsi.data_transferred = 0;
if (icmdp->cmd_un.scsi.cmdlen > DEF_CDB_LEN) {
cdblen = icmdp->cmd_un.scsi.cmdlen;
ihp = kmem_zalloc(ADDLHDRSZ(cdblen), KM_SLEEP);
len = ADDLHDRSZ(cdblen);
} else {
ihp = kmem_zalloc(sizeof (iscsi_scsi_cmd_hdr_t), KM_SLEEP);
len = sizeof (iscsi_scsi_cmd_hdr_t);
}
iscsi_tx_init_hdr(isp, icp, (iscsi_text_hdr_t *)ihp,
ISCSI_OP_SCSI_CMD, icmdp);
idm_pdu_init(pdu, icp->conn_ic, (void *)icmdp, &iscsi_tx_done);
idm_pdu_init_hdr(pdu, (uint8_t *)ihp, len);
pdu->isp_data = NULL;
pdu->isp_datalen = 0;
iscsi_tx_scsi_data(icmdp, ihp, icp, pdu);
iscsi_tx_scsi_init_pkt(icmdp, ihp);
iscsi_tx_scsi_init_task(icmdp, icp, ihp);
mutex_exit(&isp->sess_queue_pending.mutex);
idm_pdu_tx(pdu);
icmdp->cmd_misc_flags |= ISCSI_CMD_MISCFLAG_SENT;
return (rval);
}
static void
iscsi_tx_done(idm_pdu_t *pdu, idm_status_t status)
{
kmem_free((iscsi_hdr_t *)pdu->isp_hdr, pdu->isp_hdrlen);
kmem_free(pdu, sizeof (idm_pdu_t));
}
static void
iscsi_tx_pdu(iscsi_conn_t *icp, int opcode, void *hdr, int hdrlen,
iscsi_cmd_t *icmdp)
{
idm_pdu_t *tx_pdu;
iscsi_hdr_t *ihp = (iscsi_hdr_t *)hdr;
tx_pdu = kmem_zalloc(sizeof (idm_pdu_t), KM_SLEEP);
ASSERT(tx_pdu != NULL);
idm_pdu_init(tx_pdu, icp->conn_ic, icmdp, &iscsi_tx_done);
idm_pdu_init_hdr(tx_pdu, hdr, hdrlen);
if (opcode == ISCSI_OP_TEXT_CMD) {
idm_pdu_init_data(tx_pdu,
(uint8_t *)icmdp->cmd_un.text.buf,
ntoh24(ihp->dlength));
}
mutex_exit(&icp->conn_sess->sess_queue_pending.mutex);
idm_pdu_tx(tx_pdu);
icmdp->cmd_misc_flags |= ISCSI_CMD_MISCFLAG_SENT;
}
static iscsi_status_t
iscsi_tx_nop(iscsi_sess_t *isp, iscsi_cmd_t *icmdp)
{
iscsi_status_t rval = ISCSI_STATUS_SUCCESS;
iscsi_conn_t *icp = NULL;
iscsi_nop_out_hdr_t *inohp;
ASSERT(isp != NULL);
ASSERT(icmdp != NULL);
icp = icmdp->cmd_conn;
ASSERT(icp != NULL);
inohp = kmem_zalloc(sizeof (iscsi_nop_out_hdr_t), KM_SLEEP);
ASSERT(inohp != NULL);
inohp->opcode = ISCSI_OP_NOOP_OUT | ISCSI_OP_IMMEDIATE;
inohp->flags = ISCSI_FLAG_FINAL;
inohp->itt = icmdp->cmd_itt;
inohp->ttt = icmdp->cmd_ttt;
mutex_enter(&isp->sess_cmdsn_mutex);
icmdp->cmd_sn = isp->sess_cmdsn;
inohp->cmdsn = htonl(isp->sess_cmdsn);
mutex_exit(&isp->sess_cmdsn_mutex);
inohp->expstatsn = htonl(icp->conn_expstatsn);
icp->conn_laststatsn = icp->conn_expstatsn;
iscsi_tx_pdu(icp, ISCSI_OP_NOOP_OUT, inohp,
sizeof (iscsi_nop_out_hdr_t), icmdp);
return (rval);
}
static iscsi_status_t
iscsi_tx_abort(iscsi_sess_t *isp, iscsi_cmd_t *icmdp)
{
iscsi_status_t rval = ISCSI_STATUS_SUCCESS;
iscsi_conn_t *icp = NULL;
iscsi_scsi_task_mgt_hdr_t *istmh;
ASSERT(isp != NULL);
ASSERT(icmdp != NULL);
icp = icmdp->cmd_conn;
ASSERT(icp != NULL);
istmh = kmem_zalloc(sizeof (iscsi_scsi_task_mgt_hdr_t), KM_SLEEP);
ASSERT(istmh != NULL);
mutex_enter(&isp->sess_cmdsn_mutex);
icmdp->cmd_sn = isp->sess_cmdsn;
istmh->cmdsn = htonl(isp->sess_cmdsn);
mutex_exit(&isp->sess_cmdsn_mutex);
istmh->expstatsn = htonl(icp->conn_expstatsn);
icp->conn_laststatsn = icp->conn_expstatsn;
istmh->itt = icmdp->cmd_itt;
istmh->opcode = ISCSI_OP_SCSI_TASK_MGT_MSG | ISCSI_OP_IMMEDIATE;
istmh->function = ISCSI_FLAG_FINAL | ISCSI_TM_FUNC_ABORT_TASK;
ISCSI_LUN_BYTE_COPY(istmh->lun,
icmdp->cmd_un.abort.icmdp->cmd_un.scsi.lun);
istmh->rtt = icmdp->cmd_un.abort.icmdp->cmd_itt;
iscsi_tx_pdu(icp, ISCSI_OP_SCSI_TASK_MGT_MSG, istmh,
sizeof (iscsi_scsi_task_mgt_hdr_t), icmdp);
return (rval);
}
static iscsi_status_t
iscsi_tx_reset(iscsi_sess_t *isp, iscsi_cmd_t *icmdp)
{
iscsi_status_t rval = ISCSI_STATUS_SUCCESS;
iscsi_conn_t *icp = NULL;
iscsi_scsi_task_mgt_hdr_t *istmh;
ASSERT(isp != NULL);
ASSERT(icmdp != NULL);
icp = icmdp->cmd_conn;
ASSERT(icp != NULL);
istmh = kmem_zalloc(sizeof (iscsi_scsi_task_mgt_hdr_t), KM_SLEEP);
ASSERT(istmh != NULL);
istmh->opcode = ISCSI_OP_SCSI_TASK_MGT_MSG | ISCSI_OP_IMMEDIATE;
mutex_enter(&isp->sess_cmdsn_mutex);
icmdp->cmd_sn = isp->sess_cmdsn;
istmh->cmdsn = htonl(isp->sess_cmdsn);
mutex_exit(&isp->sess_cmdsn_mutex);
istmh->expstatsn = htonl(icp->conn_expstatsn);
istmh->itt = icmdp->cmd_itt;
switch (icmdp->cmd_un.reset.level) {
case RESET_LUN:
istmh->function = ISCSI_FLAG_FINAL |
ISCSI_TM_FUNC_LOGICAL_UNIT_RESET;
ISCSI_LUN_BYTE_COPY(istmh->lun, icmdp->cmd_lun->lun_num);
break;
case RESET_TARGET:
case RESET_BUS:
istmh->function = ISCSI_FLAG_FINAL |
ISCSI_TM_FUNC_TARGET_WARM_RESET;
break;
default:
ASSERT(FALSE);
break;
}
iscsi_tx_pdu(icp, ISCSI_OP_SCSI_TASK_MGT_MSG, istmh,
sizeof (iscsi_scsi_task_mgt_hdr_t), icmdp);
return (rval);
}
static iscsi_status_t
iscsi_tx_logout(iscsi_sess_t *isp, iscsi_cmd_t *icmdp)
{
iscsi_status_t rval = ISCSI_STATUS_SUCCESS;
iscsi_conn_t *icp = NULL;
iscsi_logout_hdr_t *ilh;
ASSERT(isp != NULL);
ASSERT(icmdp != NULL);
icp = icmdp->cmd_conn;
ASSERT(icp != NULL);
ilh = kmem_zalloc(sizeof (iscsi_logout_hdr_t), KM_SLEEP);
ilh->opcode = ISCSI_OP_LOGOUT_CMD | ISCSI_OP_IMMEDIATE;
ilh->flags = ISCSI_FLAG_FINAL | ISCSI_LOGOUT_REASON_CLOSE_SESSION;
ilh->itt = icmdp->cmd_itt;
ilh->cid = icp->conn_cid;
mutex_enter(&isp->sess_cmdsn_mutex);
icmdp->cmd_sn = isp->sess_cmdsn;
ilh->cmdsn = htonl(isp->sess_cmdsn);
mutex_exit(&isp->sess_cmdsn_mutex);
ilh->expstatsn = htonl(icp->conn_expstatsn);
iscsi_tx_pdu(icp, ISCSI_OP_LOGOUT_CMD, ilh,
sizeof (iscsi_logout_hdr_t), icmdp);
return (rval);
}
static iscsi_status_t
iscsi_tx_text(iscsi_sess_t *isp, iscsi_cmd_t *icmdp)
{
iscsi_status_t rval = ISCSI_STATUS_SUCCESS;
iscsi_conn_t *icp = NULL;
iscsi_text_hdr_t *ith;
ASSERT(icmdp != NULL);
icp = icmdp->cmd_conn;
ASSERT(icp != NULL);
ith = kmem_zalloc(sizeof (iscsi_text_hdr_t), KM_SLEEP);
ASSERT(ith != NULL);
ith->flags = ISCSI_FLAG_FINAL;
hton24(ith->dlength, icmdp->cmd_un.text.data_len);
ith->ttt = icmdp->cmd_un.text.ttt;
iscsi_tx_init_hdr(isp, icp, (iscsi_text_hdr_t *)ith,
ISCSI_OP_TEXT_CMD, icmdp);
bcopy(icmdp->cmd_un.text.lun, ith->rsvd4, sizeof (ith->rsvd4));
iscsi_tx_pdu(icp, ISCSI_OP_TEXT_CMD, ith, sizeof (iscsi_text_hdr_t),
icmdp);
return (rval);
}
void
iscsi_handle_abort(void *arg)
{
iscsi_sess_t *isp = NULL;
iscsi_cmd_t *icmdp = (iscsi_cmd_t *)arg;
iscsi_cmd_t *new_icmdp;
iscsi_conn_t *icp;
ASSERT(icmdp != NULL);
icp = icmdp->cmd_conn;
ASSERT(icp != NULL);
isp = icp->conn_sess;
ASSERT(isp != NULL);
ASSERT(icmdp->cmd_un.scsi.abort_icmdp == NULL);
new_icmdp = iscsi_cmd_alloc(icp, KM_SLEEP);
new_icmdp->cmd_type = ISCSI_CMD_TYPE_ABORT;
new_icmdp->cmd_lun = icmdp->cmd_lun;
new_icmdp->cmd_un.abort.icmdp = icmdp;
new_icmdp->cmd_conn = icmdp->cmd_conn;
icmdp->cmd_un.scsi.abort_icmdp = new_icmdp;
iscsi_cmd_state_machine(new_icmdp, ISCSI_CMD_EVENT_E1, isp);
}
void
iscsi_task_aborted(idm_task_t *idt, idm_status_t status)
{
iscsi_cmd_t *icmdp = idt->idt_private;
iscsi_conn_t *icp = icmdp->cmd_conn;
iscsi_sess_t *isp = icp->conn_sess;
ASSERT(icmdp->cmd_conn != NULL);
switch (status) {
case IDM_STATUS_SUSPENDED:
break;
case IDM_STATUS_ABORTED:
mutex_enter(&icp->conn_queue_active.mutex);
iscsi_cmd_state_machine(icmdp, ISCSI_CMD_EVENT_E9, isp);
mutex_exit(&icp->conn_queue_active.mutex);
break;
default:
ASSERT(0);
}
}
static void
iscsi_handle_nop(iscsi_conn_t *icp, uint32_t itt, uint32_t ttt)
{
iscsi_sess_t *isp = NULL;
iscsi_cmd_t *icmdp = NULL;
ASSERT(icp != NULL);
isp = icp->conn_sess;
ASSERT(isp != NULL);
icmdp = iscsi_cmd_alloc(icp, KM_NOSLEEP);
if (icmdp == NULL) {
return;
}
icmdp->cmd_type = ISCSI_CMD_TYPE_NOP;
icmdp->cmd_itt = itt;
icmdp->cmd_ttt = ttt;
icmdp->cmd_lun = NULL;
icp->conn_nop_lbolt = ddi_get_lbolt();
iscsi_cmd_state_machine(icmdp, ISCSI_CMD_EVENT_E1, isp);
}
iscsi_status_t
iscsi_handle_reset(iscsi_sess_t *isp, int level, iscsi_lun_t *ilp)
{
iscsi_status_t rval = ISCSI_STATUS_SUCCESS;
iscsi_conn_t *icp;
iscsi_cmd_t icmd;
ASSERT(isp != NULL);
if (level == RESET_LUN) {
rw_enter(&isp->sess_lun_list_rwlock, RW_WRITER);
ASSERT(ilp != NULL);
if (ilp->lun_state & ISCSI_LUN_STATE_BUSY) {
rw_exit(&isp->sess_lun_list_rwlock);
return (ISCSI_STATUS_SUCCESS);
}
ilp->lun_state |= ISCSI_LUN_STATE_BUSY;
rw_exit(&isp->sess_lun_list_rwlock);
} else {
mutex_enter(&isp->sess_reset_mutex);
if (isp->sess_reset_in_progress == B_TRUE) {
mutex_exit(&isp->sess_reset_mutex);
return (ISCSI_STATUS_SUCCESS);
}
isp->sess_reset_in_progress = B_TRUE;
mutex_exit(&isp->sess_reset_mutex);
}
bzero(&icmd, sizeof (iscsi_cmd_t));
icmd.cmd_sig = ISCSI_SIG_CMD;
icmd.cmd_state = ISCSI_CMD_STATE_FREE;
icmd.cmd_type = ISCSI_CMD_TYPE_RESET;
icmd.cmd_lun = ilp;
icmd.cmd_un.reset.level = level;
icmd.cmd_result = ISCSI_STATUS_SUCCESS;
icmd.cmd_completed = B_FALSE;
icmd.cmd_un.reset.response = SCSI_TCP_TM_RESP_COMPLETE;
mutex_init(&icmd.cmd_mutex, NULL, MUTEX_DRIVER, NULL);
cv_init(&icmd.cmd_completion, NULL, CV_DRIVER, NULL);
rw_enter(&isp->sess_state_rwlock, RW_READER);
if (!ISCSI_SESS_STATE_FULL_FEATURE(isp->sess_state)) {
rw_exit(&isp->sess_state_rwlock);
if (level == RESET_LUN) {
rw_enter(&isp->sess_lun_list_rwlock, RW_WRITER);
ilp->lun_state &= ~ISCSI_LUN_STATE_BUSY;
rw_exit(&isp->sess_lun_list_rwlock);
} else {
mutex_enter(&isp->sess_reset_mutex);
isp->sess_reset_in_progress = B_FALSE;
mutex_exit(&isp->sess_reset_mutex);
}
return (ISCSI_STATUS_SUCCESS);
}
mutex_enter(&isp->sess_queue_pending.mutex);
iscsi_cmd_state_machine(&icmd, ISCSI_CMD_EVENT_E1, isp);
mutex_exit(&isp->sess_queue_pending.mutex);
rw_exit(&isp->sess_state_rwlock);
mutex_enter(&icmd.cmd_mutex);
while (icmd.cmd_completed == B_FALSE) {
cv_wait(&icmd.cmd_completion, &icmd.cmd_mutex);
}
mutex_exit(&icmd.cmd_mutex);
rval = icmd.cmd_result;
if (rval == ISCSI_STATUS_SUCCESS) {
rw_enter(&isp->sess_conn_list_rwlock, RW_READER);
icp = isp->sess_conn_list;
while (icp != NULL) {
iscsi_cmd_t *t_icmdp = NULL;
iscsi_cmd_t *next_icmdp = NULL;
mutex_enter(&icp->conn_queue_active.mutex);
t_icmdp = icp->conn_queue_active.head;
while (t_icmdp != NULL) {
next_icmdp = t_icmdp->cmd_next;
mutex_enter(&t_icmdp->cmd_mutex);
if (!(t_icmdp->cmd_misc_flags &
ISCSI_CMD_MISCFLAG_SENT)) {
mutex_exit(&t_icmdp->cmd_mutex);
t_icmdp = next_icmdp;
continue;
}
if (level == RESET_LUN) {
if (icmd.cmd_lun == NULL ||
t_icmdp->cmd_lun == NULL ||
(icmd.cmd_lun->lun_num !=
t_icmdp->cmd_lun->lun_num)) {
mutex_exit(&t_icmdp->cmd_mutex);
t_icmdp = next_icmdp;
continue;
}
}
if (icmd.cmd_sn == t_icmdp->cmd_sn) {
t_icmdp->cmd_misc_flags |=
ISCSI_CMD_MISCFLAG_FLUSH;
if (t_icmdp->cmd_type ==
ISCSI_CMD_TYPE_SCSI) {
t_icmdp->cmd_un.scsi.pkt_stat |=
STAT_BUS_RESET;
}
mutex_exit(&t_icmdp->cmd_mutex);
} else if ((icmd.cmd_sn > t_icmdp->cmd_sn) ||
((t_icmdp->cmd_sn - icmd.cmd_sn) >
ISCSI_CMD_SN_WRAP)) {
if (t_icmdp->cmd_type ==
ISCSI_CMD_TYPE_SCSI) {
t_icmdp->cmd_un.scsi.pkt_stat |=
STAT_BUS_RESET;
}
mutex_exit(&t_icmdp->cmd_mutex);
iscsi_cmd_state_machine(t_icmdp,
ISCSI_CMD_EVENT_E7, isp);
} else {
mutex_exit(&t_icmdp->cmd_mutex);
}
t_icmdp = next_icmdp;
}
mutex_exit(&icp->conn_queue_active.mutex);
icp = icp->conn_next;
}
rw_exit(&isp->sess_conn_list_rwlock);
}
cv_destroy(&icmd.cmd_completion);
mutex_destroy(&icmd.cmd_mutex);
if (level == RESET_LUN) {
rw_enter(&isp->sess_lun_list_rwlock, RW_WRITER);
ilp->lun_state &= ~ISCSI_LUN_STATE_BUSY;
rw_exit(&isp->sess_lun_list_rwlock);
} else {
mutex_enter(&isp->sess_reset_mutex);
isp->sess_reset_in_progress = B_FALSE;
mutex_exit(&isp->sess_reset_mutex);
}
return (rval);
}
static void
iscsi_logout_start(void *arg)
{
iscsi_task_t *itp = (iscsi_task_t *)arg;
iscsi_conn_t *icp;
icp = (iscsi_conn_t *)itp->t_arg;
mutex_enter(&icp->conn_state_mutex);
(void) iscsi_handle_logout(icp);
mutex_exit(&icp->conn_state_mutex);
}
iscsi_status_t
iscsi_handle_logout(iscsi_conn_t *icp)
{
iscsi_sess_t *isp;
idm_conn_t *ic;
iscsi_cmd_t *icmdp;
int rval;
ASSERT(icp != NULL);
isp = icp->conn_sess;
ic = icp->conn_ic;
ASSERT(isp != NULL);
ASSERT(isp->sess_hba != NULL);
ASSERT(mutex_owned(&icp->conn_state_mutex));
if ((icp->conn_state == ISCSI_CONN_STATE_FREE) ||
(icp->conn_state == ISCSI_CONN_STATE_FAILED) ||
(icp->conn_state == ISCSI_CONN_STATE_POLLING)) {
idm_conn_rele(ic);
return (0);
}
icmdp = iscsi_cmd_alloc(icp, KM_SLEEP);
ASSERT(icmdp != NULL);
icmdp->cmd_type = ISCSI_CMD_TYPE_LOGOUT;
icmdp->cmd_result = ISCSI_STATUS_SUCCESS;
icmdp->cmd_completed = B_FALSE;
mutex_enter(&isp->sess_queue_pending.mutex);
iscsi_cmd_state_machine(icmdp, ISCSI_CMD_EVENT_E1, isp);
mutex_exit(&isp->sess_queue_pending.mutex);
mutex_exit(&icp->conn_state_mutex);
mutex_enter(&icmdp->cmd_mutex);
while (icmdp->cmd_completed == B_FALSE) {
cv_wait(&icmdp->cmd_completion, &icmdp->cmd_mutex);
}
mutex_exit(&icmdp->cmd_mutex);
mutex_enter(&icp->conn_state_mutex);
rval = icmdp->cmd_result;
iscsi_cmd_free(icmdp);
if (rval != 0) {
idm_ini_conn_disconnect(icp->conn_ic);
}
while ((icp->conn_state != ISCSI_CONN_STATE_FREE) &&
(icp->conn_state != ISCSI_CONN_STATE_FAILED) &&
(icp->conn_state != ISCSI_CONN_STATE_POLLING)) {
cv_wait(&icp->conn_state_change, &icp->conn_state_mutex);
}
idm_conn_rele(ic);
return (rval);
}
iscsi_status_t
iscsi_handle_text(iscsi_conn_t *icp, char *buf, uint32_t buf_len,
uint32_t data_len, uint32_t *rx_data_len)
{
iscsi_sess_t *isp;
iscsi_cmd_t *icmdp;
iscsi_status_t rval = ISCSI_STATUS_SUCCESS;
ASSERT(icp != NULL);
ASSERT(buf != NULL);
ASSERT(rx_data_len != NULL);
isp = icp->conn_sess;
ASSERT(isp != NULL);
if (data_len > icp->conn_params.max_xmit_data_seg_len) {
return (ISCSI_STATUS_CMD_FAILED);
}
icmdp = iscsi_cmd_alloc(icp, KM_SLEEP);
ASSERT(icmdp != NULL);
icmdp->cmd_type = ISCSI_CMD_TYPE_TEXT;
icmdp->cmd_result = ISCSI_STATUS_SUCCESS;
icmdp->cmd_misc_flags &= ~ISCSI_CMD_MISCFLAG_FREE;
icmdp->cmd_completed = B_FALSE;
icmdp->cmd_un.text.buf = buf;
icmdp->cmd_un.text.buf_len = buf_len;
icmdp->cmd_un.text.offset = 0;
icmdp->cmd_un.text.data_len = data_len;
icmdp->cmd_un.text.total_rx_len = 0;
icmdp->cmd_un.text.ttt = ISCSI_RSVD_TASK_TAG;
icmdp->cmd_un.text.stage = ISCSI_CMD_TEXT_INITIAL_REQ;
long_text_response:
rw_enter(&isp->sess_state_rwlock, RW_READER);
if (!ISCSI_SESS_STATE_FULL_FEATURE(isp->sess_state)) {
iscsi_cmd_free(icmdp);
rw_exit(&isp->sess_state_rwlock);
return (ISCSI_STATUS_NO_CONN_LOGGED_IN);
}
mutex_enter(&isp->sess_queue_pending.mutex);
iscsi_cmd_state_machine(icmdp, ISCSI_CMD_EVENT_E1, isp);
mutex_exit(&isp->sess_queue_pending.mutex);
rw_exit(&isp->sess_state_rwlock);
mutex_enter(&icmdp->cmd_mutex);
while (icmdp->cmd_completed == B_FALSE) {
cv_wait(&icmdp->cmd_completion, &icmdp->cmd_mutex);
}
mutex_exit(&icmdp->cmd_mutex);
if ((icmdp->cmd_result != ISCSI_STATUS_SUCCESS) &&
(icmdp->cmd_result != ISCSI_STATUS_DATA_OVERFLOW)) {
cmn_err(CE_NOTE, "iscsi: SendTarget discovery failed (%d)",
icmdp->cmd_result);
rval = icmdp->cmd_result;
iscsi_cmd_free(icmdp);
return (rval);
}
if (icmdp->cmd_un.text.stage != ISCSI_CMD_TEXT_FINAL_RSP) {
icmdp->cmd_misc_flags &= ~ISCSI_CMD_MISCFLAG_FREE;
icmdp->cmd_completed = B_FALSE;
icmdp->cmd_un.text.data_len = 0;
icmdp->cmd_un.text.stage = ISCSI_CMD_TEXT_CONTINUATION;
goto long_text_response;
}
*rx_data_len = icmdp->cmd_un.text.total_rx_len;
rval = icmdp->cmd_result;
iscsi_cmd_free(icmdp);
return (rval);
}
iscsi_status_t
iscsi_handle_passthru(iscsi_sess_t *isp, uint16_t lun, struct uscsi_cmd *ucmdp)
{
iscsi_status_t rval;
iscsi_cmd_t *icmdp;
struct scsi_pkt *pkt;
struct buf *bp;
struct scsi_arq_status *arqstat;
int statuslen;
ASSERT(isp != NULL);
ASSERT(ucmdp != NULL);
if (ucmdp->uscsi_rqlen > SENSE_LENGTH) {
statuslen = ucmdp->uscsi_rqlen + ISCSI_ARQ_STATUS_NOSENSE_LEN;
} else {
statuslen = sizeof (struct scsi_arq_status);
}
bp = kmem_zalloc(sizeof (struct buf), KM_SLEEP);
ASSERT(bp != NULL);
pkt = kmem_zalloc(sizeof (struct scsi_pkt), KM_SLEEP);
ASSERT(pkt != NULL);
icmdp = iscsi_cmd_alloc(NULL, KM_SLEEP);
ASSERT(icmdp != NULL);
bp->b_flags = B_READ;
bp->b_bcount = ucmdp->uscsi_buflen;
bp->b_un.b_addr = ucmdp->uscsi_bufaddr;
pkt->pkt_ha_private = icmdp;
pkt->pkt_scbp = kmem_zalloc(statuslen, KM_SLEEP);
pkt->pkt_cdbp = kmem_zalloc(ucmdp->uscsi_cdblen, KM_SLEEP);
pkt->pkt_comp = iscsi_handle_passthru_callback;
pkt->pkt_time = ucmdp->uscsi_timeout;
icmdp->cmd_lun = NULL;
icmdp->cmd_type = ISCSI_CMD_TYPE_SCSI;
icmdp->cmd_un.scsi.lun = lun;
icmdp->cmd_un.scsi.pkt = pkt;
icmdp->cmd_un.scsi.bp = bp;
bcopy(ucmdp->uscsi_cdb, pkt->pkt_cdbp, ucmdp->uscsi_cdblen);
icmdp->cmd_un.scsi.cmdlen = ucmdp->uscsi_cdblen;
icmdp->cmd_un.scsi.statuslen = statuslen;
icmdp->cmd_crc_error_seen = B_FALSE;
icmdp->cmd_completed = B_FALSE;
icmdp->cmd_result = ISCSI_STATUS_SUCCESS;
rw_enter(&isp->sess_state_rwlock, RW_READER);
if (!ISCSI_SESS_STATE_FULL_FEATURE(isp->sess_state)) {
rw_exit(&isp->sess_state_rwlock);
iscsi_cmd_free(icmdp);
kmem_free(pkt->pkt_cdbp, ucmdp->uscsi_cdblen);
kmem_free(pkt->pkt_scbp, statuslen);
kmem_free(pkt, sizeof (struct scsi_pkt));
kmem_free(bp, sizeof (struct buf));
return (ISCSI_STATUS_CMD_FAILED);
}
mutex_enter(&isp->sess_queue_pending.mutex);
iscsi_cmd_state_machine(icmdp, ISCSI_CMD_EVENT_E1, isp);
mutex_exit(&isp->sess_queue_pending.mutex);
rw_exit(&isp->sess_state_rwlock);
mutex_enter(&icmdp->cmd_mutex);
while (icmdp->cmd_completed == B_FALSE) {
cv_wait(&icmdp->cmd_completion, &icmdp->cmd_mutex);
}
mutex_exit(&icmdp->cmd_mutex);
rval = icmdp->cmd_result;
ucmdp->uscsi_resid = pkt->pkt_resid;
arqstat = (struct scsi_arq_status *)pkt->pkt_scbp;
ucmdp->uscsi_status = ((char *)&arqstat->sts_status)[0];
if ((ucmdp->uscsi_rqlen > 0) &&
(ucmdp->uscsi_rqbuf != NULL)) {
ASSERT(ucmdp->uscsi_rqlen >= arqstat->sts_rqpkt_resid);
ucmdp->uscsi_rqresid = arqstat->sts_rqpkt_resid;
bcopy(&arqstat->sts_sensedata, ucmdp->uscsi_rqbuf,
ucmdp->uscsi_rqlen - arqstat->sts_rqpkt_resid);
}
if ((ucmdp->uscsi_status == STATUS_CHECK) &&
((icmdp->cmd_misc_flags & ISCSI_CMD_MISCFLAG_INTERNAL)) == B_TRUE) {
(void) iscsi_decode_sense(
(uint8_t *)&arqstat->sts_sensedata, icmdp);
}
iscsi_cmd_free(icmdp);
kmem_free(pkt->pkt_cdbp, ucmdp->uscsi_cdblen);
kmem_free(pkt->pkt_scbp, statuslen);
kmem_free(pkt, sizeof (struct scsi_pkt));
kmem_free(bp, sizeof (struct buf));
return (rval);
}
static void
iscsi_handle_passthru_callback(struct scsi_pkt *pkt)
{
iscsi_cmd_t *icmdp = NULL;
ASSERT(pkt != NULL);
icmdp = (iscsi_cmd_t *)pkt->pkt_ha_private;
ASSERT(icmdp != NULL);
mutex_enter(&icmdp->cmd_mutex);
icmdp->cmd_completed = B_TRUE;
icmdp->cmd_result = ISCSI_STATUS_SUCCESS;
cv_broadcast(&icmdp->cmd_completion);
mutex_exit(&icmdp->cmd_mutex);
}
void
iscsi_build_hdr(idm_task_t *idm_task, idm_pdu_t *pdu, uint8_t opcode)
{
iscsi_cmd_t *icmdp = idm_task->idt_private;
iscsi_conn_t *icp = icmdp->cmd_conn;
iscsi_data_hdr_t *ihp = (iscsi_data_hdr_t *)pdu->isp_hdr;
mutex_enter(&icmdp->cmd_mutex);
if (opcode == ISCSI_OP_SCSI_DATA) {
uint32_t data_sn;
uint32_t lun;
icmdp = idm_task->idt_private;
icp = icmdp->cmd_conn;
ihp->opcode = opcode;
ihp->itt = icmdp->cmd_itt;
ihp->ttt = idm_task->idt_r2t_ttt;
ihp->expstatsn = htonl(icp->conn_expstatsn);
icp->conn_laststatsn = icp->conn_expstatsn;
data_sn = ntohl(ihp->datasn);
data_sn++;
lun = icmdp->cmd_un.scsi.lun;
ISCSI_LUN_BYTE_COPY(ihp->lun, lun);
ISCSI_IO_LOG(CE_NOTE, "DEBUG: iscsi_build_hdr"
"(ISCSI_OP_SCSI_DATA): task: %p icp: %p ic: %p itt: %x "
"exp: %d data_sn: %d", (void *)idm_task, (void *)icp,
(void *)icp->conn_ic, ihp->itt, icp->conn_expstatsn,
data_sn);
} else {
cmn_err(CE_WARN, "iscsi_build_hdr: unprocessed build "
"header opcode: %x", opcode);
}
mutex_exit(&icmdp->cmd_mutex);
}
static void
iscsi_process_rsp_status(iscsi_sess_t *isp, iscsi_conn_t *icp,
idm_status_t status)
{
switch (status) {
case IDM_STATUS_SUCCESS:
if ((isp->sess_state == ISCSI_SESS_STATE_IN_FLUSH) &&
(icp->conn_queue_active.count == 0)) {
iscsi_drop_conn_cleanup(icp);
}
break;
case IDM_STATUS_PROTOCOL_ERROR:
KSTAT_INC_CONN_ERR_PROTOCOL(icp);
iscsi_drop_conn_cleanup(icp);
break;
default:
break;
}
}
static void
iscsi_drop_conn_cleanup(iscsi_conn_t *icp)
{
mutex_enter(&icp->conn_state_mutex);
idm_ini_conn_disconnect(icp->conn_ic);
mutex_exit(&icp->conn_state_mutex);
}
void
iscsi_rx_error_pdu(idm_conn_t *ic, idm_pdu_t *pdu, idm_status_t status)
{
iscsi_conn_t *icp = (iscsi_conn_t *)ic->ic_handle;
iscsi_sess_t *isp;
ASSERT(icp != NULL);
isp = icp->conn_sess;
ASSERT(isp != NULL);
iscsi_process_rsp_status(isp, icp, status);
idm_pdu_complete(pdu, status);
}
void
iscsi_rx_misc_pdu(idm_conn_t *ic, idm_pdu_t *pdu)
{
iscsi_conn_t *icp;
iscsi_hdr_t *ihp = (iscsi_hdr_t *)pdu->isp_hdr;
iscsi_sess_t *isp;
idm_status_t status;
icp = ic->ic_handle;
isp = icp->conn_sess;
isp->sess_rx_lbolt = icp->conn_rx_lbolt = ddi_get_lbolt();
switch (ihp->opcode & ISCSI_OPCODE_MASK) {
case ISCSI_OP_LOGIN_RSP:
status = iscsi_rx_process_login_pdu(ic, pdu);
idm_pdu_complete(pdu, status);
break;
case ISCSI_OP_LOGOUT_RSP:
status = iscsi_rx_process_logout_rsp(ic, pdu);
idm_pdu_complete(pdu, status);
break;
case ISCSI_OP_REJECT_MSG:
status = iscsi_rx_process_reject_rsp(ic, pdu);
break;
case ISCSI_OP_SCSI_TASK_MGT_RSP:
status = iscsi_rx_process_task_mgt_rsp(ic, pdu);
idm_pdu_complete(pdu, status);
break;
case ISCSI_OP_NOOP_IN:
status = iscsi_rx_process_nop(ic, pdu);
idm_pdu_complete(pdu, status);
break;
case ISCSI_OP_ASYNC_EVENT:
status = iscsi_rx_process_async_rsp(ic, pdu);
break;
case ISCSI_OP_TEXT_RSP:
status = iscsi_rx_process_text_rsp(ic, pdu);
idm_pdu_complete(pdu, status);
break;
default:
cmn_err(CE_WARN, "iscsi connection(%u) protocol error "
"- received misc unsupported opcode 0x%02x",
icp->conn_oid, ihp->opcode);
status = IDM_STATUS_PROTOCOL_ERROR;
break;
}
iscsi_process_rsp_status(isp, icp, status);
}
void
iscsi_ic_thread(iscsi_thread_t *thread, void *arg)
{
iscsi_sess_t *isp = (iscsi_sess_t *)arg;
int ret;
iscsi_queue_t q;
iscsi_cmd_t *icmdp;
iscsi_cmd_t *next_icmdp;
ASSERT(isp != NULL);
ASSERT(thread != NULL);
ASSERT(thread->signature == SIG_ISCSI_THREAD);
for (;;) {
ret = iscsi_thread_wait(thread, -1);
ASSERT(ret >= 0);
q.count = 0;
q.head = NULL;
q.tail = NULL;
mutex_enter(&isp->sess_queue_completion.mutex);
icmdp = isp->sess_queue_completion.head;
while (icmdp != NULL) {
next_icmdp = icmdp->cmd_next;
mutex_enter(&icmdp->cmd_mutex);
if ((icmdp->cmd_un.scsi.r2t_icmdp == NULL) &&
(icmdp->cmd_un.scsi.abort_icmdp == NULL)) {
mutex_exit(&icmdp->cmd_mutex);
(void) iscsi_dequeue_cmd(&isp->
sess_queue_completion.head,
&isp->sess_queue_completion.tail,
icmdp);
--isp->sess_queue_completion.count;
iscsi_enqueue_cmd_head(&q.head,
&q.tail, icmdp);
} else {
mutex_exit(&icmdp->cmd_mutex);
}
icmdp = next_icmdp;
}
mutex_exit(&isp->sess_queue_completion.mutex);
icmdp = q.head;
while (icmdp != NULL) {
next_icmdp = icmdp->cmd_next;
iscsi_cmd_state_machine(icmdp, ISCSI_CMD_EVENT_E8, isp);
icmdp = next_icmdp;
}
if (ret > 0)
continue;
else
break;
}
}
void
iscsi_iodone(iscsi_sess_t *isp, iscsi_cmd_t *icmdp)
{
struct scsi_pkt *pkt = NULL;
struct buf *bp = icmdp->cmd_un.scsi.bp;
ASSERT(isp != NULL);
ASSERT(icmdp != NULL);
pkt = icmdp->cmd_un.scsi.pkt;
ASSERT(pkt != NULL);
ASSERT(icmdp->cmd_un.scsi.abort_icmdp == NULL);
ASSERT(icmdp->cmd_un.scsi.r2t_icmdp == NULL);
if (pkt->pkt_reason == CMD_CMPLT) {
if (bp) {
if (bp->b_flags & B_READ) {
KSTAT_SESS_RX_IO_DONE(isp, bp->b_bcount);
} else {
KSTAT_SESS_TX_IO_DONE(isp, bp->b_bcount);
}
}
}
if (pkt->pkt_flags & FLAG_NOINTR) {
cv_broadcast(&icmdp->cmd_completion);
mutex_exit(&icmdp->cmd_mutex);
} else {
mutex_exit(&icmdp->cmd_mutex);
(*icmdp->cmd_un.scsi.pkt->pkt_comp)(icmdp->cmd_un.scsi.pkt);
}
}
void
iscsi_wd_thread(iscsi_thread_t *thread, void *arg)
{
iscsi_sess_t *isp = (iscsi_sess_t *)arg;
int rc = 1;
ASSERT(isp != NULL);
while (rc != 0) {
iscsi_timeout_checks(isp);
iscsi_nop_checks(isp);
rc = iscsi_thread_wait(thread, SEC_TO_TICK(1));
}
}
static void
iscsi_timeout_checks(iscsi_sess_t *isp)
{
clock_t now = ddi_get_lbolt();
iscsi_conn_t *icp;
iscsi_cmd_t *icmdp, *nicmdp;
ASSERT(isp != NULL);
rw_enter(&isp->sess_state_rwlock, RW_READER);
mutex_enter(&isp->sess_queue_pending.mutex);
for (icmdp = isp->sess_queue_pending.head;
icmdp; icmdp = nicmdp) {
nicmdp = icmdp->cmd_next;
if (icmdp->cmd_lbolt_timeout == 0)
continue;
if (((icmdp->cmd_type == ISCSI_CMD_TYPE_SCSI) ||
(icmdp->cmd_type == ISCSI_CMD_TYPE_TEXT)) &&
!(icmdp->cmd_misc_flags & ISCSI_CMD_MISCFLAG_STUCK))
continue;
if (now <= icmdp->cmd_lbolt_timeout)
continue;
iscsi_cmd_state_machine(icmdp, ISCSI_CMD_EVENT_E6, isp);
}
mutex_exit(&isp->sess_queue_pending.mutex);
rw_exit(&isp->sess_state_rwlock);
rw_enter(&isp->sess_conn_list_rwlock, RW_READER);
icp = isp->sess_conn_list;
while (icp != NULL) {
icp->conn_timeout = B_FALSE;
mutex_enter(&icp->conn_state_mutex);
mutex_enter(&isp->sess_queue_pending.mutex);
mutex_enter(&icp->conn_queue_active.mutex);
for (icmdp = icp->conn_queue_active.head;
icmdp; icmdp = nicmdp) {
nicmdp = icmdp->cmd_next;
if (iscsi_nop_timeout_checks(icmdp) == B_TRUE) {
icp->conn_timeout = B_TRUE;
}
if (icmdp->cmd_lbolt_timeout == 0)
continue;
if (icmdp->cmd_state != ISCSI_CMD_STATE_ACTIVE &&
!(icmdp->cmd_misc_flags & ISCSI_CMD_MISCFLAG_FLUSH))
continue;
if (now <= icmdp->cmd_lbolt_timeout)
continue;
if (icmdp->cmd_misc_flags & ISCSI_CMD_MISCFLAG_FLUSH) {
iscsi_cmd_state_machine(icmdp,
ISCSI_CMD_EVENT_E7, isp);
} else if (icmdp->cmd_state == ISCSI_CMD_STATE_ACTIVE) {
iscsi_cmd_state_machine(icmdp,
ISCSI_CMD_EVENT_E6, isp);
}
}
mutex_exit(&icp->conn_queue_active.mutex);
mutex_exit(&isp->sess_queue_pending.mutex);
mutex_exit(&icp->conn_state_mutex);
icp = icp->conn_next;
}
icp = isp->sess_conn_list;
while (icp != NULL) {
mutex_enter(&icp->conn_state_mutex);
if ((icp->conn_timeout == B_TRUE) &&
(icp->conn_state_idm_connected == B_TRUE)) {
idm_ini_conn_disconnect(icp->conn_ic);
icp->conn_timeout = B_FALSE;
}
mutex_exit(&icp->conn_state_mutex);
icp = icp->conn_next;
}
rw_exit(&isp->sess_conn_list_rwlock);
}
static void
iscsi_nop_checks(iscsi_sess_t *isp)
{
iscsi_conn_t *icp;
ASSERT(isp != NULL);
if (isp->sess_type == ISCSI_SESS_TYPE_DISCOVERY) {
return;
}
rw_enter(&isp->sess_conn_list_rwlock, RW_READER);
icp = isp->sess_conn_act;
if (icp != NULL) {
mutex_enter(&icp->conn_state_mutex);
if ((ISCSI_CONN_STATE_FULL_FEATURE(icp->conn_state)) &&
(ddi_get_lbolt() > isp->sess_conn_act->conn_rx_lbolt +
SEC_TO_TICK(iscsi_nop_delay)) && (ddi_get_lbolt() >
isp->sess_conn_act->conn_nop_lbolt +
SEC_TO_TICK(iscsi_nop_delay))) {
mutex_enter(&isp->sess_queue_pending.mutex);
iscsi_handle_nop(isp->sess_conn_act,
0, ISCSI_RSVD_TASK_TAG);
mutex_exit(&isp->sess_queue_pending.mutex);
}
mutex_exit(&icp->conn_state_mutex);
icp = icp->conn_next;
}
rw_exit(&isp->sess_conn_list_rwlock);
}
static boolean_t
iscsi_nop_timeout_checks(iscsi_cmd_t *icmdp)
{
if (icmdp->cmd_type == ISCSI_CMD_TYPE_NOP) {
if ((ddi_get_lbolt() - icmdp->cmd_lbolt_active) >
SEC_TO_TICK(ISCSI_CONN_TIEMOUT_DETECT)) {
return (B_TRUE);
} else {
return (B_FALSE);
}
}
return (B_FALSE);
}
static void
iscsi_flush_cmd_after_reset(uint32_t cmd_sn, uint16_t lun_num,
iscsi_conn_t *icp)
{
iscsi_cmd_t *t_icmdp = NULL;
iscsi_cmd_t *next_icmdp = NULL;
ASSERT(icp != NULL);
t_icmdp = icp->conn_queue_active.head;
while (t_icmdp != NULL) {
next_icmdp = t_icmdp->cmd_next;
mutex_enter(&t_icmdp->cmd_mutex);
if ((t_icmdp->cmd_misc_flags & ISCSI_CMD_MISCFLAG_SENT) &&
((cmd_sn > t_icmdp->cmd_sn) ||
((t_icmdp->cmd_sn - cmd_sn) >
ISCSI_CMD_SN_WRAP))) {
if ((t_icmdp->cmd_lun != NULL &&
t_icmdp->cmd_lun->lun_num == lun_num) ||
(t_icmdp->cmd_type == ISCSI_CMD_TYPE_SCSI &&
(t_icmdp->cmd_un.scsi.lun & ISCSI_LUN_MASK) ==
lun_num)) {
t_icmdp->cmd_misc_flags |=
ISCSI_CMD_MISCFLAG_FLUSH;
if (t_icmdp->cmd_type == ISCSI_CMD_TYPE_SCSI) {
t_icmdp->cmd_un.scsi.pkt_stat |=
STAT_BUS_RESET;
}
}
}
mutex_exit(&t_icmdp->cmd_mutex);
t_icmdp = next_icmdp;
}
}
static boolean_t
iscsi_decode_sense(uint8_t *sense_data, iscsi_cmd_t *icmdp)
{
uint8_t sense_key = 0;
uint8_t asc = 0;
uint8_t ascq = 0;
boolean_t flush_io = B_FALSE;
boolean_t reconfig_lun = B_FALSE;
iscsi_sess_t *isp = NULL;
ASSERT(sense_data != NULL);
isp = icmdp->cmd_conn->conn_sess;
sense_key = scsi_sense_key(sense_data);
switch (sense_key) {
case KEY_UNIT_ATTENTION:
asc = scsi_sense_asc(sense_data);
switch (asc) {
case ISCSI_SCSI_RESET_SENSE_CODE:
flush_io = B_TRUE;
break;
case ISCSI_SCSI_LUNCHANGED_CODE:
ascq = scsi_sense_ascq(sense_data);
if (ascq == ISCSI_SCSI_LUNCHANGED_ASCQ)
reconfig_lun = B_TRUE;
default:
break;
}
break;
default:
break;
}
if (reconfig_lun == B_TRUE) {
rw_enter(&isp->sess_state_rwlock, RW_READER);
if ((isp->sess_state == ISCSI_SESS_STATE_LOGGED_IN) &&
(iscsi_sess_enum_request(isp, B_FALSE,
isp->sess_state_event_count) !=
ISCSI_SESS_ENUM_SUBMITTED)) {
cmn_err(CE_WARN, "Unable to commit re-enumeration for"
" session(%u) %s", isp->sess_oid, isp->sess_name);
}
rw_exit(&isp->sess_state_rwlock);
}
return (flush_io);
}