/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2000 by Cisco Systems, Inc. All rights reserved.
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
*
* iSCSI Pseudo HBA Driver
*/
#include "iscsi.h" /* iscsi driver */
if (idm_pattern_checking) { \
((ICHK_HDR)->cmd_status == 0) && \
} \
}
/* Size of structure scsi_arq_status without sense data. */
sizeof (struct scsi_extended_sense))
/* generic io helpers */
/* callbacks from idm */
/* receivers */
/* senders */
/* helpers */
static void iscsi_logout_start(void *arg);
iscsi_conn_t *icp);
/*
* This file contains the main guts of the iSCSI protocol layer.
* It's broken into 5 sections; Basic helper functions, RX IO path,
* TX IO path, Completion (IC) IO path, and watchdog (WD) routines.
*
* The IO flow model is similiar to the below diagram. The
* iscsi session, connection and command state machines are used
* to drive IO through this flow diagram. Reference those files
* to get a detailed description of their respective state models
* prior to their xxx_state_machine_function().
*
* tran_start() -> CMD_E1 TX_THREAD RX_THREAD
* | T T
* V T T
* PENDING_Q --CMD_E2--> ACTIVE_Q - --CMD_E3--+
* T \ C T |
* T \M T |
* D T |
* WD_THREAD TT|TT T |
* /E T |
* / 6 T |
* ABORTING_Q<- --CMD_E3--+
* T |
* T T |
* T |
* callback() <--CMD_E#-- COMPLETION_Q <------------+
* T
* T
* IC_THREAD
*
* External and internal command are ran thru this same state
* machine. All commands enter the state machine by receiving an
* ISCSI_CMD_EVENT_E1. This event places the command into the
* PENDING_Q. Next when resources are available the TX_THREAD
* issues a E2 event on the command. This sends the command
* to the TCP stack and places the command on the ACTIVE_Q. While
* on the PENDIING_Q and ACTIVE_Q, the command is monitored via the
* WD_THREAD to ensure the pkt_time has not elapsed. If elapsed the
* command is issued an E6(timeout) event which moves either (if pending)
* completed the command or (if active) moves the command to the
* aborting queue and issues a SCSI TASK MANAGEMENT ABORT command
* to cancel the IO request. If the original command is completed
* or the TASK MANAGEMENT command completes the command is moved
* to the COMPLETION_Q via a E3 event. The IC_THREAD then processes
* the COMPLETION_Q and issues the scsi_pkt callback. This
* callback can not be processed directly from the RX_THREAD
* because the callback might call back into the iscsi driver
* causing a deadlock condition.
*
* For more details on the complete CMD state machine reference
* the state machine diagram in iscsi_cmd.c. The connection state
* machine is driven via IO events in this file. Then session
* events are driven by the connection events. For complete
* details on these state machines reference iscsi_sess.c and
*/
/*
* +--------------------------------------------------------------------+
* | io helper routines |
* +--------------------------------------------------------------------+
*/
/*
* n2h24 - native to host 24 bit integer translation.
*/
static uint32_t
{
}
/*
* iscsi_sna_lt - Serial Number Arithmetic, 32 bits, less than, RFC1982
*/
static int
{
}
/*
* iscsi_sna_lte - Serial Number Arithmetic, 32 bits, less than or equal,
* RFC1982
*/
int
{
}
/*
* iscsi_update_flow_control - Update expcmdsn and maxcmdsn iSCSI
* flow control information for a session
*/
void
{
}
isp->sess_maxcmdsn)) {
/*
* the window is open again - schedule
* to send any held tasks soon
*/
}
}
}
}
/*
* +--------------------------------------------------------------------+
* | io receive and processing routines |
* +--------------------------------------------------------------------+
*/
/*
* iscsi_rx_scsi_rsp - called from idm
* For each opcode type fan out the processing.
*/
void
{
/* reset the session timer when we receive the response */
/* fan out the hdr processing */
case ISCSI_OP_SCSI_DATA_RSP:
break;
case ISCSI_OP_SCSI_RSP:
break;
default:
"received pdu with unsupported opcode 0x%02x",
}
}
void
{
(opcode == ISCSI_OP_SCSI_RSP));
}
}
}
{
icp->conn_expstatsn++;
} else {
"received status out of order itt:0x%x statsn:0x%x "
return (IDM_STATUS_PROTOCOL_ERROR);
}
/* get icmdp so we can cleanup on error */
} else {
}
if (!ISCSI_SUCCESS(rval)) {
return (IDM_STATUS_PROTOCOL_ERROR);
}
/* update expcmdsn and maxcmdsn */
return (IDM_STATUS_SUCCESS);
}
static void
{
/* Check the residual count */
/*
* We didn't xfer the expected amount of data -
* the residual_count in the header is only
* valid if the underflow flag is set.
*/
} else {
/*
* Some data fell on the floor
* somehow - probably a CRC error
*/
}
}
"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",
}
/* set flags that tell SCSA that the command is complete */
/* Set successful completion */
} else {
}
} else {
/*
* Some of the data was found to have an incorrect
* error at the protocol error.
*/
} else {
}
}
}
static boolean_t
{
/*
* Process iSCSI Cmd Response Status
* RFC 3720 Sectionn 10.4.2.
*/
case STATUS_GOOD:
/* pass SCSI status up stack */
}
break;
case STATUS_CHECK:
/*
* Verify we received a sense buffer and
* that there is the correct amount of
* request sense space to copy it to.
*/
if ((dlength > 1) &&
sizeof (struct scsi_arq_status))) {
/*
* If a bad command status is received we
* need to reset the pkt_resid to zero.
* The target driver compares its value
* before checking other error flags.
* (ex. check conditions)
*/
/* get sense length from first 2 bytes */
(size_t)0xFFFF;
"DEBUG: iscsi_rx_cmd_rsp_cmd_status status_check: "
"dlen: %d scbp: %p statuslen: %d arq: %d senselen:"
(int)sizeof (struct scsi_arq_status),
senselen);
/* Sanity-check on the sense length */
}
/*
* If there was a Data Digest error then
* the sense data cannot be trusted.
*/
if (icmdp->cmd_crc_error_seen) {
senselen = 0;
}
/* automatic request sense */
arqstat =
/* pass SCSI status up stack */
/*
* Set the status for the automatic
* request sense command
*/
if (senselen == 0) {
/* auto request sense failed */
/* auto request sense short */
} else {
/* auto request sense complete */
arqstat->sts_rqpkt_resid = 0;
}
arqstat->sts_rqpkt_statistics = 0;
}
/*
* Calculate size of space reserved for sense data in
* pkt->pkt_scbp.
*/
/* copy auto request sense */
if (senselen_to_copy > 0) {
}
" sts_rqpkt_resid: %d pkt_scblen: %d senselen: %lu"
" sensebuf_len: %d senselen_to_copy: %d affect: %d",
break;
}
/* FALLTHRU */
case STATUS_BUSY:
case STATUS_QFULL:
case STATUS_ACA_ACTIVE:
default:
/*
* If a bad command status is received we need to
* reset the pkt_resid to zero. The target driver
* compares its value before checking other error
* flags. (ex. check conditions)
*/
"DEBUG: iscsi_rx_cmd_rsp_cmd_status: status: "
"%d cmd_status: %d dlen: %u scbp: %p statuslen: %d "
(int)sizeof (struct scsi_arq_status));
/* pass SCSI status up stack */
}
}
return (affect);
}
/*
* iscsi_rx_process_login_pdup - Process login response PDU. This function
* copies the data into the connection context so that the login code can
* interpret it.
*/
{
/*
* Copy header and data into connection structure so iscsi_login()
* can process it.
*/
/*
* If conn_login_state != LOGIN_TX then we are not ready to handle
* this login response and we should just drop it.
*/
sizeof (iscsi_hdr_t));
/*
* Login code is sloppy with it's NULL handling so make sure
* we don't leave any stale data in there.
*/
}
return (IDM_STATUS_SUCCESS);
}
/*
* iscsi_rx_process_cmd_rsp - Process received scsi command response. This
* will contain sense data if the command was not successful. This data needs
* to be copied into the scsi_pkt. Otherwise we just complete the IO.
*/
static idm_status_t
{
/* make sure we get status in order */
&icmdp)) != IDM_STATUS_SUCCESS) {
}
return (rval);
}
/*
* If we are in "idm aborting" state then we shouldn't continue
* to process this command. By definition this command is no longer
* on the active queue so we shouldn't try to remove it either.
*/
return (IDM_STATUS_SUCCESS);
}
/* Get the IDM buffer and bytes transferred */
/* Transport tracks bytes transferred so use those counts */
} else {
}
} else {
/*
* Some transports cannot track the bytes transferred on
* the initiator side (like iSER) so we have to use the
* status info. If the response field indicates that
* the command actually completed then we will assume
* the data_transferred value represents the entire buffer
* unless the resid field says otherwise. This is a bit
* unintuitive but it's really impossible to know what
* has been transferred without detailed consideration
* of the SCSI status and sense key and that is outside
* can consider these values along with the resid and figure
* it out. The data_transferred concept is just belt and
* suspenders anyway -- RFC 3720 actually explicitly rejects
* scoreboarding ("Initiators SHOULD NOT keep track of the
* data transferred to or from the target (scoreboarding)")
* perhaps for this very reason.
*/
} else {
}
}
}
" %x expcmdsn: %x sess_cmd: %x sess_expcmdsn: %x data_transfered:"
/* The target failed the command. */
} else {
}
} else {
/* success */
}
}
}
return (IDM_STATUS_SUCCESS);
}
static void
{
/*
* The command* must be completed now, since we won't get a command
* response PDU. The cmd_status and residual_count are
* not meaningful unless status_present is set.
*/
/* Check the residual count */
/*
* We didn't xfer the expected amount of data -
* the residual_count in the header is only valid
* if the underflow flag is set.
*/
"underflow: itt: %d "
} else {
/* Some data fell on the floor somehw */
"iscsi_data_rsp_pkt: data fell: itt: %d "
}
}
}
/*
* Not supposed to get exception status here!
* We have no request sense data so just do the
* best we can
*/
/* sense residual is set to whole size of sense buffer */
"exception status: itt: %d resid: %d",
/* just pass along the status we got */
}
}
/*
* iscsi_rx_process_data_rsp -
* This currently processes the final data sequence denoted by the data response
* PDU Status bit being set. We will not receive the SCSI response.
* This bit denotes that the PDU is the successful completion of the
* command.
*/
static idm_status_t
{
/* should only call this when the data rsp contains final rsp */
&icmdp)) != IDM_STATUS_SUCCESS) {
}
return (rval);
}
/*
* If we are in "idm aborting" state then we shouldn't continue
* to process this command. By definition this command is no longer
* on the active queue so we shouldn't try to remove it either.
*/
return (IDM_STATUS_SUCCESS);
}
/*
* iscsi_rx_data call later in this function may cause
* deadlock during fault injections. Instead remove
* the cmd from the active queue and release the locks.
* Then before returning or completing the command
* return the cmd to the active queue and reacquire
* the locks.
*/
/* shorthand some values */
/*
* some poorly behaved targets have been observed
* sending data-in pdu's during a write operation
*/
"iscsi connection(%u) protocol error - "
"received data response during write operation "
"itt:0x%x",
return (IDM_STATUS_PROTOCOL_ERROR);
}
}
/*
* After the check of bp above we *should* have a corresponding
* idm_buf_t (ibp). It's possible that the original call
* to idm_buf_alloc failed due to a pending connection state
* transition in which case this value can be NULL. It's
* highly unlikely that the connection would be shutting down
* *and* we manage to process a data response and get to this
* point in the code but just in case we should check for it.
* This isn't really a protocol error -- we are almost certainly
* closing the connection anyway so just return a generic error.
*/
return (IDM_STATUS_FAIL);
}
} else {
}
}
"itt: %d ibp: %p icmdp: %p xfer_len: %lu transferred: %lu dlen: %u",
return (IDM_STATUS_SUCCESS);
}
/*
* iscsi_rx_process_nop - Process a received nop. If nop is in response
* to a ping we sent update stats. If initiated by the target we need
* to response back to the target with a nop. Schedule the response.
*/
/* ARGSUSED */
static idm_status_t
{
"received status out of order itt:0x%x statsn:0x%x "
return (IDM_STATUS_PROTOCOL_ERROR);
}
"- can not find cmd for itt:0x%x",
return (IDM_STATUS_PROTOCOL_ERROR);
}
}
/* update expcmdsn and maxcmdsn */
/* This is the only type of nop that incs. the expstatsn */
icp->conn_expstatsn++;
/*
* This is a targets response to our nop
*/
/*
* Target requested a nop. Send one.
*/
} else {
/*
* This is a target-initiated ping that doesn't expect
* a response; nothing to do except update our flow control
* (which we do in all cases above).
*/
/* EMPTY */
}
return (IDM_STATUS_SUCCESS);
}
/*
* iscsi_rx_process_reject_rsp - The server rejected a PDU
*/
static idm_status_t
{
int i = 0;
/*
* In RFC3720 section 10.17, this 4 bytes should be all 0xff.
*/
for (i = 0; i < 4; i++) {
return (IDM_STATUS_PROTOCOL_ERROR);
}
}
icp->conn_expstatsn++;
} else {
"received status out of order statsn:0x%x "
return (IDM_STATUS_PROTOCOL_ERROR);
}
/* update expcmdsn and maxcmdsn */
/* If we don't have the rejected header we can't do anything */
if (dlength < sizeof (iscsi_hdr_t)) {
return (IDM_STATUS_PROTOCOL_ERROR);
}
/* map old ihp */
/*
* ISCSI_REJECT_IMM_CMD_REJECT - Immediate Command Reject
* too many immediate commands (original cmd can be resent)
*/
/*
* We have exceeded the server's capacity for outstanding
* immediate commands. This must be a task management
* command so try to find it in the abortingqueue and
* complete it.
*/
/* Rejecting IMM but old old_hdr wasn't IMM */
return (IDM_STATUS_PROTOCOL_ERROR);
}
/*
* We only send NOP and TASK_MGT as IMM. All other
* cases should be considered as a protocol error.
*/
case ISCSI_OP_NOOP_OUT:
/*
* A ping was rejected - treat this like
* ping response. The down side is we
* didn't get an updated MaxCmdSn.
*/
break;
status =
break;
default:
"- received a reject for a command(0x%02x) not "
break;
}
break;
/*
* For the rest of the reject cases just use the general
* hammer of dis/reconnecting. This will resolve all
* noted issues although could be more graceful.
*/
default:
"target requested reason:0x%x",
break;
}
return (status);
}
/*
* iscsi_rx_process_rejected_tsk_mgt -
*/
/* ARGSUSED */
static idm_status_t
{
return (IDM_STATUS_PROTOCOL_ERROR);
}
case ISCSI_CMD_TYPE_ABORT:
case ISCSI_CMD_TYPE_RESET:
break;
/* We don't send any other task mgr types */
default:
break;
}
return (IDM_STATUS_SUCCESS);
}
/*
* iscsi_rx_process_task_mgt_rsp -
*/
/* ARGSUSED */
static idm_status_t
{
&icmdp)) != IDM_STATUS_SUCCESS) {
return (status);
}
case ISCSI_CMD_TYPE_ABORT:
case ISCSI_CMD_TYPE_RESET:
/* success */
break;
case SCSI_TCP_TM_RESP_NO_TASK:
/*
* If the array no longer knows about
* an ABORT RTT and we no longer have
* a parent SCSI command it was just
* completed, free this ABORT resource.
* Otherwise FALLTHRU this will flag a
* protocol problem.
*/
break;
}
/* FALLTHRU */
/*
* If the target rejects our reset task,
* we should record the response and complete
* this command with the result.
*/
break;
}
/* FALLTHRU */
case SCSI_TCP_TM_RESP_NO_LUN:
default:
/*
* Something is out of sync. Flush
* active queues and resync the
* the connection to try and recover
* to a known state.
*/
}
break;
default:
"received a task mgt response for a non-task mgt "
break;
}
return (status);
}
/*
* iscsi_rx_process_logout_rsp -
*
*/
/* ARGSUSED */
{
"received status out of order itt:0x%x statsn:0x%x "
return (IDM_STATUS_PROTOCOL_ERROR);
}
return (IDM_STATUS_PROTOCOL_ERROR);
}
}
/* update expcmdsn and maxcmdsn */
"DEBUG: iscsi_rx_process_logout_rsp: response: %d",
/*
* If the target doesn't know about our connection
* then we can consider our self disconnected.
*/
/* FALLTHRU */
/*
* We don't support ErrorRecovery levels above 0
* currently so consider this success.
*/
/* FALLTHRU */
/*
* per spec. "cleanup failed for various reasons."
* Although those various reasons are undefined.
* Not sure what to do here. So fake success,
* which will disconnect the connection.
*/
/* FALLTHRU */
case ISCSI_LOGOUT_SUCCESS:
break;
default:
break;
}
return (status);
}
/*
* iscsi_rx_process_async_rsp
*
*/
/* ARGSUSED */
static idm_status_t
{
icp->conn_expstatsn++;
} else {
"received status out of order statsn:0x%x "
return (IDM_STATUS_PROTOCOL_ERROR);
}
switch (iaehp->async_event) {
/*
* SCSI asynchronous event is reported in
* the sense data. Sense data that accompanies
* the report in the data segment identifies the
* condition. If the target supports SCSI
* asynchronous events reporting (see [SAM2])
* as indicated in the stardard INQUIRY data
* (see [SPC3]), its use may be enabled by
* parameters in the SCSI control mode page
* (see [SPC3]).
*
* T-10 has removed SCSI asunchronous events
* from the standard. Although we have seen
* a couple targets still spending these requests.
* Those targets were specifically sending them
* to handle the change.
*/
}
}
break;
/*
* We've been asked to logout by the target --
* we need to treat this differently from a normal logout
* due to a discovery failure. Normal logouts result in
* an N3 event to the session state machine and an offline
* of the lun. In this case we want to put the connection
* into "failed" state and generate N5 to the session state
* machine since the initiator logged out at the target's
* request. To track this we set a flag indicating we
* received this async logout request from the tharget
*/
/* Hold is released in iscsi_handle_logout. */
/* Target has requested this connection to logout. */
DDI_SUCCESS) {
/* Disconnect if we couldn't dispatch the task */
}
break;
/*
* Target is going to drop our connection.
* param1 - CID which will be dropped.
* param2 - Min time to reconnect.
* param3 - Max time to reconnect.
*
* For now just let fail as another disconnect.
*
* MC/S Once we support > 1 connections then
* we need to check the CID and drop that
* specific connection.
*/
break;
/*
* Target is going to drop ALL connections.
* param2 - Min time to reconnect.
* param3 - Max time to reconnect.
*
* For now just let fail as anyother disconnect.
*
* MC/S Once we support more than > 1 connections
* then we need to drop all connections on the
* session.
*/
break;
/*
* Target requests parameter negotiation
* on this connection.
*
* The initiator must honor this request. For
* now we will request a logout. We can't
* just ignore this or it might force corruption?
*/
/* Hold is released in iscsi_handle_logout */
DDI_SUCCESS) {
/* Disconnect if we couldn't dispatch the task */
}
break;
/*
* We currently don't handle any vendor
* specific async events. So just ignore
* the request.
*/
break;
default:
}
return (rval);
}
/*
* iscsi_rx_process_text_rsp - processes iSCSI text response. It sets
* the cmd_result field of the command data structure with the actual
* status value instead of returning the status value. The return value
* is SUCCESS in order to let iscsi_handle_text control the operation of
* a text request.
* Text requests are a handled a little different than other types of
* iSCSI commands because the initiator sends additional empty text requests
* in order to obtain the remaining responses required to complete the
* request. iscsi_handle_text controls the operation of text request, while
* iscsi_rx_process_text_rsp just process the current response.
*/
static idm_status_t
{
&icmdp)) != IDM_STATUS_SUCCESS) {
return (rval);
}
/* update local final response flag */
}
/*
* validate received TTT value. RFC3720 specifies the following:
* - F bit set to 1 MUST have a reserved TTT value 0xffffffff
* - F bit set to 0 MUST have a non-reserved TTT value !0xffffffff
* In addition, the received TTT value must not change between
* responses of a long text response
*/
"received text response with invalid flags:0x%x or "
return (IDM_STATUS_PROTOCOL_ERROR);
}
/* TTT should have matched reserved value */
"error - received text response with invalid "
return (IDM_STATUS_PROTOCOL_ERROR);
}
/*
* If this is first response, save away TTT value for later use
*/
}
/* check whether enough buffer available to copy data */
/*
* DATA_OVERFLOW will result in a SUCCESS return so that
* iscsi_handle_text can continue to obtain the remaining
* text response if needed.
*/
} else {
}
/* update stage */
} else {
}
return (IDM_STATUS_SUCCESS);
}
/*
* iscsi_rx_process_scsi_itt_to_icmdp - Lookup itt using IDM to find matching
* icmdp. Verify itt in hdr and icmdp are the same.
*/
static iscsi_status_t
{
"received unknown itt:0x%x - protocol error",
return (ISCSI_STATUS_INTERNAL_ERROR);
}
return (ISCSI_STATUS_SUCCESS);
}
/*
* iscsi_rx_process_itt_to_icmdp - Lookup itt in the session's
* cmd table to find matching icmdp. Verify itt in hdr and
* icmdp are the same.
*/
static iscsi_status_t
iscsi_cmd_t **icmdp)
{
int cmd_table_idx = 0;
/* try to find an associated iscsi_pkt */
"received unknown itt:0x%x - protocol error",
return (ISCSI_STATUS_INTERNAL_ERROR);
}
/* verify itt */
return (ISCSI_STATUS_INTERNAL_ERROR);
}
/* ensure that icmdp is still in Active state */
"but icmdp (%p) is not in active state",
return (ISCSI_STATUS_INTERNAL_ERROR);
}
/* make sure this is a SCSI cmd */
return (ISCSI_STATUS_SUCCESS);
}
/*
* +--------------------------------------------------------------------+
* | End of protocol receive routines |
* +--------------------------------------------------------------------+
*/
/*
* +--------------------------------------------------------------------+
* | Beginning of protocol send routines |
* +--------------------------------------------------------------------+
*/
/*
* iscsi_tx_thread - This thread is the driving point for all
* iSCSI PDUs after login. No PDUs should call idm_pdu_tx()
* directly they should be funneled through iscsi_tx_thread.
*/
void
{
/*
* Transfer icmdps until shutdown by owning session.
*/
while (ret != 0) {
/*
* While the window is open, there are commands available
* to send and the session state allows those commands to
* be sent try to transfer them.
*/
/* update command with this connection info */
/* attempt to send this command */
} else {
icmdp->cmd_misc_flags |=
} else if (icp->conn_state !=
icmdp->cmd_misc_flags |=
}
}
break;
}
}
/*
* Go to sleep until there is something new
* to process (awoken via cv_boardcast).
* Or the timer goes off.
*/
}
}
/*
* iscsi_tx_cmd - transfers icmdp across wire as iscsi pdu
*
* Just prior to sending the command to the networking layer the
* pending queue lock will be dropped. At this point only local
* resources will be used, not the icmdp. Holding the queue lock
* across the networking call can lead to a hang. (This is due
* to the the target driver and networking layers competing use
* of the timeout() resources and the queue lock being held for
* both sides.) Upon the completion of this command the lock
* will have been re-acquired.
*/
{
/* transfer specific command type */
case ISCSI_CMD_TYPE_SCSI:
break;
case ISCSI_CMD_TYPE_NOP:
break;
case ISCSI_CMD_TYPE_ABORT:
break;
case ISCSI_CMD_TYPE_RESET:
break;
case ISCSI_CMD_TYPE_LOGOUT:
break;
case ISCSI_CMD_TYPE_TEXT:
break;
default:
}
return (rval);
}
/*
* a variable length cdb can be up to 16K, but we obviously don't want
* to put that on the stack; go with 200 bytes; if we get something
* bigger than that we will kmem_alloc a buffer
*/
/*
* given the size of the cdb, return how many bytes the header takes,
* which is the sizeof addl_hdr_t + the CDB size, minus the 16 bytes
* stored in the basic header, minus sizeof (ahs_extscb)
*/
16 - 4)
static void
{
isp->sess_cmdsn++;
}
static void
{
/*
* fix problem where OS sends bp (B_READ &
* b_bcount!=0) for a TUR or START_STOP.
* (comment came from cisco code.)
*/
}
} else {
/*
* FinalBit on the the iSCSI PDU denotes this
* is the last PDU in the sequence.
*
* initial_r2t = true means R2T is required
* for additional PDU, so there will be no more
* unsolicited PDUs following
*/
}
/* Check if we should send ImmediateData */
/*
* if everything fits immediate, or
* we can send all burst data immediate
* (not unsol), set F
*/
/*
* XXX This doesn't look right -- it's not
* clear how we can handle transmitting
* any unsolicited data. It looks like
* we only support immediate data. So what
* happens if we don't set ISCSI_FLAG_FINAL?
*
* Unless there's magic code somewhere that
* is sending the remaining PDU's we should
* simply set ISCSI_FLAG_FINAL and forget
* about sending unsolicited data. The big
* win is the immediate data anyway for small
* PDU's.
*/
}
}
/* total data transfer length */
}
} else {
}
/* XXX How is this different from the code above? */
/* will idm send the next data command up to burst length? */
/* send the burstlen if we haven't sent immediate data */
/* CRM: should idm send difference min(buflen, first_burst) and imm? */
/* (MIN(first_burst_length, buflen) - imdata > 0) */
/* CRM_LATER: change this to generate unsolicited pdu */
if ((buflen > 0) &&
pdu->isp_datalen == 0) {
}
}
}
static void
{
/* tagged queuing */
} else {
/* ihp->flags |= ISCSI_ATTR_UNTAGGED; */
/* EMPTY */
}
/* iscsi states lun is based on spc.2 */
/* copy the SCSI Command Block into the PDU */
} else {
iahp->ahs_hlen_hi = 0;
}
/*
* Update all values before transfering.
* We should never touch the icmdp after
* transfering if there is no more data
* to send. The only case the idm_pdu_tx()
* will fail is a on a connection disconnect
* in that case the command will be flushed.
*/
}
static void
{
"DEBUG: iscsi_tx_init_task: task_start: %p idt_tt: %x cmdsn: %x "
"sess_cmdsn: %x cmd: %p "
"cmdtype: %d datalen: %u",
if (data_length > 0) {
} else {
}
"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 ",
}
/*
* Task is now active
*/
}
/*
* iscsi_tx_scsi -
*
*/
static iscsi_status_t
{
int cdblen = 0;
int len;
/* Reset counts in case we are on a retry */
} else {
/*
* only bzero the basic header; the additional header
* will be set up correctly later, if needed
*/
len = sizeof (iscsi_scsi_cmd_hdr_t);
}
pdu->isp_datalen = 0;
/*
* Sestion 12.11 of the iSCSI specification has a good table
* should be sent.
*/
/* Calls idm_task_start */
return (rval);
}
/* ARGSUSED */
static void
{
}
static void
{
if (opcode == ISCSI_OP_TEXT_CMD) {
}
}
/*
* iscsi_tx_nop -
*
*/
static iscsi_status_t
{
sizeof (iscsi_nop_out_hdr_t), icmdp);
return (rval);
}
/*
* iscsi_tx_abort -
*
*/
static iscsi_status_t
{
sizeof (iscsi_scsi_task_mgt_hdr_t), icmdp);
return (rval);
}
/*
* iscsi_tx_reset -
*
*/
static iscsi_status_t
{
case RESET_LUN:
break;
case RESET_TARGET:
case RESET_BUS:
break;
default:
/* unsupported / unknown level */
break;
}
sizeof (iscsi_scsi_task_mgt_hdr_t), icmdp);
return (rval);
}
/*
* iscsi_tx_logout -
*
*/
static iscsi_status_t
{
sizeof (iscsi_logout_hdr_t), icmdp);
return (rval);
}
/*
* iscsi_tx_text - setup iSCSI text request header and send PDU with
* data given in the buffer attached to the command. For a single
* text request, the target may need to send its response in multiple
* text response. In this case, empty text requests are sent after
* each received response to notify the target the initiator is ready
* for more response. For the initial request, the data_len field in
* the text specific portion of a command is set to the amount of data
* the initiator wants to send as part of the request. If additional
* empty text requests are required for long responses, the data_len
* field is set to 0 by the iscsi_handle_text function.
*/
static iscsi_status_t
{
icmdp);
return (rval);
}
/*
* +--------------------------------------------------------------------+
* | End of protocol send routines |
* +--------------------------------------------------------------------+
*/
/*
* iscsi_handle_abort -
*
*/
void
{
/* there should only be one abort */
/* pending queue mutex is already held by timeout_checks */
}
/*
* Callback from IDM indicating that the task has been suspended or aborted.
*/
void
{
switch (status) {
case IDM_STATUS_SUSPENDED:
/*
* If the task is suspended, it may be aborted later,
* so we can ignore this notification.
*/
break;
case IDM_STATUS_ABORTED:
break;
default:
/*
* Unexpected status.
*/
ASSERT(0);
}
}
/*
* iscsi_handle_nop -
*
*/
static void
{
return;
}
}
/*
* iscsi_handle_reset - send reset request to the target
*
*/
{
return (ISCSI_STATUS_SUCCESS);
}
} else {
/*
* If the reset is in progress, it is unnecessary
* to send reset to the target redunantly.
*/
return (ISCSI_STATUS_SUCCESS);
}
}
/*
* If we received an IO and we are not in the
* LOGGED_IN state we are in the process of
* failing. Just respond that we are BUSY.
*/
/* We aren't connected to the target fake success */
} else {
}
return (ISCSI_STATUS_SUCCESS);
}
/* stall until completed */
}
/* copy rval */
if (rval == ISCSI_STATUS_SUCCESS) {
/*
* Reset was successful. We need to flush
* all active IOs.
*/
if (!(t_icmdp->cmd_misc_flags &
/*
* Although this command is in the
* active queue, it has not been sent.
* Skip it.
*/
continue;
}
continue;
}
}
/*
* This command may be replied with
* UA sense key later. So currently
* it is not a suitable time to flush
* it. Mark its flag with FLUSH. There
* is no harm to keep it for a while.
*/
}
/*
* This reset request must act on all
* the commnds from the same session
* having a CmdSN lower than the task
* mangement CmdSN. So flush these
* commands here.
*/
}
} else {
}
}
}
}
/* clean up */
} else {
}
return (rval);
}
/*
* iscsi_logout_start - task handler for deferred logout
* Acquire a hold before call, released in iscsi_handle_logout
*/
static void
{
(void) iscsi_handle_logout(icp);
}
/*
* iscsi_handle_logout - This function will issue a logout for
* the session from a specific connection.
* Acquire idm_conn_hold before call. Released internally.
*/
{
int rval;
/*
* If the connection has already gone down (e.g. if the transport
* failed between when this LOGOUT was generated and now) then we
* can and must skip sending the LOGOUT. Check the same condition
* we use below to determine that connection has "settled".
*/
return (0);
}
/*
* release connection state mutex to avoid a deadlock. This
* function is called from within the connection state
* machine with the lock held. When the logout response is
* received another call to the connection state machine
* occurs which causes the deadlock
*/
/* stall until completed */
}
/* copy rval */
/* clean up */
if (rval != 0) {
/* If the logout failed then drop the connection */
}
/* stall until connection settles */
/* wait for transition */
}
/*
* Return value reflects whether the logout command completed --
* regardless of the return value the connection is closed and
* ready for reconnection.
*/
return (rval);
}
/*
* iscsi_handle_text - main control function for iSCSI text requests. This
* function handles allocating the command, sending initial text request, and
* handling long response sequence.
* If a data overflow condition occurs, iscsi_handle_text continues to
* receive responses until the all data has been recieved. This allows
* the full data length to be returned to the caller.
*/
{
/*
* Ensure data for text request command is not greater
* than the negotiated maximum receive data seqment length.
*
* Although iSCSI allows for long text requests (multiple
* pdus), this function places a restriction on text
* requests to ensure it is handled by a single PDU.
*/
return (ISCSI_STATUS_CMD_FAILED);
}
return (ISCSI_STATUS_NO_CONN_LOGGED_IN);
}
/* stall until completed */
}
/*
* check if error occured. If data overflow occured, continue on
* to ensure we get all data so that the full data length can be
* returned to the user
*/
icmdp->cmd_result);
return (rval);
}
/* check if this was a partial text PDU */
/*
* If a paritial text rexponse received, send an empty
* text request. This follows the behaviour specified
* in RFC3720 regarding long text responses.
*/
goto long_text_response;
}
/*
* set total received data length. If data overflow this would be
* amount of data that would have been received if buffer large
* enough.
*/
/* copy rval */
/* clean up */
return (rval);
}
/*
* iscsi_handle_passthru - This function is used to send a uscsi_cmd
* to a specific target lun. This routine is used for internal purposes
* during enumeration and via the ISCSI_USCSICMD IOCTL. We restrict
* and READ_CAPACITY for security purposes.
*
* The logic here is broken into three phases.
* 3) cv_wait for completion
*/
{
int statuslen;
/*
* The caller provided sense buffer large enough for additional
* sense bytes. We need to allocate pkt_scbp to fit them there
* too.
*/
} else {
/* The default size of pkt_scbp */
statuslen = sizeof (struct scsi_arq_status);
}
/*
* Step 1. Setup structs - KM_SLEEP will always succeed
*/
/* setup bp structure */
/* setup scsi_pkt structure */
/* callback routine for passthru, will wake cv_wait */
/* setup iscsi_cmd structure */
/*
* Step 2. Push IO onto pending queue. If we aren't in
* FULL_FEATURE we need to fail the IO.
*/
return (ISCSI_STATUS_CMD_FAILED);
}
/*
* Step 3. Wait on cv_wait for completion routine
*/
}
/* copy rval */
/* update scsi status */
/* copy request sense buffers if caller gave space */
if ((ucmdp->uscsi_rqlen > 0) &&
}
/*
* Internal SCSI commands received status
*/
(void) iscsi_decode_sense(
}
/* clean up */
return (rval);
}
/*
* iscsi_handle_passthru_callback -
*
*/
static void
{
}
/*
* IDM callbacks
*/
void
{
if (opcode == ISCSI_OP_SCSI_DATA) {
data_sn++;
/* CRM: upate_flow_control */
"(ISCSI_OP_SCSI_DATA): task: %p icp: %p ic: %p itt: %x "
data_sn);
} else {
"header opcode: %x", opcode);
}
}
static void
{
switch (status) {
case IDM_STATUS_SUCCESS:
}
break;
break;
default:
break;
}
}
static void
}
void
{
}
void
{
case ISCSI_OP_LOGIN_RSP:
break;
case ISCSI_OP_LOGOUT_RSP:
break;
case ISCSI_OP_REJECT_MSG:
break;
break;
case ISCSI_OP_NOOP_IN:
break;
case ISCSI_OP_ASYNC_EVENT:
break;
case ISCSI_OP_TEXT_RSP:
break;
default:
"- received misc unsupported opcode 0x%02x",
break;
}
}
/*
* +--------------------------------------------------------------------+
* | Beginning of completion routines |
* +--------------------------------------------------------------------+
*/
/*
* iscsi_ic_thread -
*/
void
{
int ret;
for (;;) {
/*
* We wait till iodone or somebody else wakes us up.
*/
/*
* The value should never be negative since we never timeout.
*/
q.count = 0;
/*
* yet. If not, don't complete the command.
*/
(void) iscsi_dequeue_cmd(&isp->
icmdp);
} else {
}
icmdp = next_icmdp;
}
icmdp = next_icmdp;
}
if (ret > 0)
/* Somebody woke us up to work */
continue;
else
/*
* Somebody woke us up to kill ourselves. We will
* make sure, however that the completion queue is
* empty before leaving. After we've done that it
* is the originator of the signal that has to make
* sure no other SCSI command is posted.
*/
break;
}
}
/*
* iscsi_iodone -
*
*/
void
{
if (bp) {
} else {
}
}
}
} else {
/*
* Release mutex. As soon as callback is
* issued the caller may destroy the command.
*/
/*
* We can't just directly call the pk_comp routine. In
* many error cases the target driver will use the calling
* thread to re-drive error handling (reset, retries...)
* back into the hba driver (iscsi). If the target redrives
* a reset back into the iscsi driver off this thead we have
* a chance of deadlocking. So instead use the io completion
* thread.
*/
}
}
/*
* +--------------------------------------------------------------------+
* | End of completion routines |
* +--------------------------------------------------------------------+
*/
/*
* +--------------------------------------------------------------------+
* | Beginning of watchdog routines |
* +--------------------------------------------------------------------+
*/
/*
* iscsi_watchdog_thread -
*
*/
void
{
}
}
/*
* iscsi_timeout_checks -
*
*/
static void
{
/* PENDING */
/* Skip entries with no timeout */
if (icmdp->cmd_lbolt_timeout == 0)
continue;
/*
* Skip pending queue entries for cmd_type values that depend
* on having an open cmdsn window for successfull transition
* from pending to the active (i.e. ones that depend on
* sess_cmdsn .vs. sess_maxcmdsn). For them, the timer starts
* when they are successfully moved to the active queue by
* iscsi_cmd_state_pending() code.
*/
/*
* If the cmd is stuck, at least give it a chance
* to timeout
*/
continue;
/* Skip if timeout still in the future */
continue;
/* timeout */
}
/* ACTIVE */
}
/* Skip entries with no timeout */
if (icmdp->cmd_lbolt_timeout == 0)
continue;
/*
* Skip if command is not active or not needed
* to flush.
*/
continue;
/* Skip if timeout still in the future */
continue;
/*
* This command is left during target reset,
* we can flush it now.
*/
/* timeout */
}
}
}
/* timeout on this connect detected */
}
}
}
/*
* iscsi_nop_checks - sends a NOP on idle connections
*
* This function walks the connections on a session and
* issues NOPs on those connections that are in FULL
* FEATURE mode and have not received data for the
* time period specified by iscsi_nop_delay (global).
*/
static void
{
return;
}
/*
* We haven't received anything from the
* target is a defined period of time,
* send NOP to see if the target is alive.
*/
0, ISCSI_RSVD_TASK_TAG);
}
}
}
static boolean_t
{
return (B_TRUE);
} else {
return (B_FALSE);
}
}
return (B_FALSE);
}
/*
* +--------------------------------------------------------------------+
* | End of wd routines |
* +--------------------------------------------------------------------+
*/
/*
* iscsi_flush_cmd_after_reset - flush commands after reset
*
* Here we will flush all the commands for a specified LUN whose cmdsn is less
* than the one received with the Unit Attention.
*/
static void
{
/*
* We will flush the commands whose cmdsn is less than the one
* got Unit Attention.
* Here we will check for wrap by subtracting and compare to
* 1/2 of a 32 bit number, if greater then we wrapped.
*/
ISCSI_CMD_SN_WRAP))) {
/*
* Internally generated SCSI commands do not have
* t_icmdp->cmd_lun set, but the LUN can be retrieved
* from t_icmdp->cmd_un.scsi.lun.
*/
lun_num)) {
}
}
}
}
}
/*
* iscsi_decode_sense - decode the sense data in the cmd response
* and take proper actions
*/
static boolean_t
{
switch (sense_key) {
case KEY_UNIT_ATTENTION:
switch (asc) {
/*
* POWER ON, RESET, OR BUS_DEVICE RESET
* OCCURRED
*/
break;
if (ascq == ISCSI_SCSI_LUNCHANGED_ASCQ)
default:
break;
}
break;
default:
/*
* Currently we don't care
* about other sense key.
*/
break;
}
if (reconfig_lun == B_TRUE) {
}
}
return (flush_io);
}