iscsi_io.c revision fcf3ce441efd61da9bb2884968af01cb7c1452cc
/*
* 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 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* iSCSI Pseudo HBA Driver
*/
#include "iscsi.h" /* iscsi driver */
/* generic io helpers */
/* receivers */
iscsi_hdr_t *ihp);
/* senders */
/* helpers */
#define ISCSI_CONN_TO_NET_DIGEST(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
*/
static void
{
}
isp->sess_maxcmdsn)) {
/*
* the window is open again - schedule
* to send any held tasks soon
*/
}
}
}
}
/*
* +--------------------------------------------------------------------+
* | io receive and processing routines |
* +--------------------------------------------------------------------+
*/
/*
* iscsi_rx_thread - The connection creates a thread of this
* function during login. After which point this thread is
* used to receive and process all iSCSI PDUs on this connection.
* The PDUs received on this connection are used to drive the
* commands through their state machine. This thread will
* continue processing while the connection is on a LOGGED_IN
* or IN_LOGOUT state. Once the connection moves out of this
* state the thread will die.
*/
void
{
int hdr_size = 0;
int data_size = 0;
/* pre-alloc recv header buffer for common actions */
/* pre-alloc max_recv_size buffer for common actions */
do {
/* Wait for the next iSCSI header */
ISCSI_NET_HEADER_DIGEST : 0));
if (ISCSI_SUCCESS(rval)) {
isp->sess_rx_lbolt =
icp->conn_rx_lbolt =
/* Perform specific hdr handling */
}
/*
* handle failures
*/
switch (rval) {
case ISCSI_STATUS_SUCCESS:
/*
* If we successfully completed a receive
* and we are in an IN_FLUSH state then
* check the active queue count to see
* if its empty. If its empty then force
* a disconnect event on the connection.
* This will move the session from IN_FLUSH
* to FLUSHED and complete the login
* parameter update.
*/
(void) iscsi_conn_state_machine(icp,
}
break;
/* connection had an error */
(void) iscsi_conn_state_machine(icp,
break;
/*
* If we encounter a digest error we have to restart
* all the connections on this session. per iSCSI
* Level 0 Recovery.
*/
(void) iscsi_conn_state_machine(icp,
break;
/*
* We can continue with a data digest error. The
* icmdp was flaged as having a crc problem. It
* will be aborted when all data is received. This
* saves us from restarting the session when we
* might be able to keep it going. If the data
* digest issue was really bad we will hit a
* status protocol error on the next pdu, which
* will force a connection retstart.
*/
break;
/*
* A protocol problem was encountered. Reset
* session to try and repair issue.
*/
(void) iscsi_conn_state_machine(icp,
break;
/*
* These should have all been handled before now.
*/
break;
default:
rval);
}
(iscsi_thread_wait(thread, 0) != 0));
}
/*
* iscsi_rx_process_hdr - This function collects data for all PDUs
* that do not have data that will be mapped to a specific scsi_pkt.
* Then for each hdr type fan out the processing.
*/
static iscsi_status_t
{
/* If this is not a SCSI_DATA_RSP we can go ahead and get the data */
ISCSI_NET_DATA_DIGEST : 0);
if (!ISCSI_SUCCESS(rval)) {
return (rval);
}
}
/* fan out the hdr processing */
case ISCSI_OP_SCSI_DATA_RSP:
break;
case ISCSI_OP_SCSI_RSP:
break;
case ISCSI_OP_RTT_RSP:
break;
case ISCSI_OP_NOOP_IN:
break;
case ISCSI_OP_REJECT_MSG:
break;
break;
case ISCSI_OP_LOGOUT_RSP:
break;
case ISCSI_OP_ASYNC_EVENT:
break;
case ISCSI_OP_TEXT_RSP:
break;
default:
"received an unsupported opcode 0x%02x",
}
return (rval);
}
/*
* iscsi_rx_process_data_rsp - Processed received data header. Once
* header is processed we read data off the connection directly into
* the scsi_pkt to avoid duplicate bcopy of a large amount of data.
* If this is 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. In this case complete the command. If This bit isn't
* set we wait for more data or a scsi command response.
*/
static iscsi_status_t
{
/* make sure we got status in order */
icp->conn_expstatsn++;
} else {
"- received status out of order itt:0x%x "
return (ISCSI_STATUS_PROTOCOL_ERROR);
}
}
/* match itt in the session's command table */
return (ISCSI_STATUS_PROTOCOL_ERROR);
}
/*
* 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.
*/
/* update expcmdsn and maxcmdsn */
/* 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 (ISCSI_STATUS_PROTOCOL_ERROR);
}
/*
* We can't tolerate the target sending too much
* data for our buffer
*/
if ((dlength >
"iscsi connection(%u) protocol error - "
"received too much data itt:0x%x",
return (ISCSI_STATUS_PROTOCOL_ERROR);
}
/*
* Get the rest of the data and copy it directly into
* the scsi_pkt.
*/
ISCSI_NET_DATA_DIGEST : 0));
if (ISCSI_SUCCESS(rval)) {
} else {
/* If digest error flag icmdp with a crc error */
if (rval == ISCSI_STATUS_DATA_DIGEST_ERROR) {
}
return (rval);
}
/* update icmdp statistics */
}
/*
* We got status. This should only happen if we have
* received all the data with no errors. 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 */
if (bp &&
/*
* 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 somehw */
}
}
}
/*
* Not supposed to get exception status here!
* We have no request sense data so just do the
* best we can
*/
struct scsi_arq_status *arqstat =
sizeof (struct scsi_extended_sense);
/* just pass along the status we got */
}
} else {
}
return (ISCSI_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 iscsi_status_t
{
/* make sure we get status in order */
icp->conn_expstatsn++;
} else {
"received status out of order itt:0x%x statsn:0x%x "
return (ISCSI_STATUS_PROTOCOL_ERROR);
}
return (ISCSI_STATUS_PROTOCOL_ERROR);
}
/* update expcmdsn and maxcmdsn */
/* The target failed the command. */
} else {
}
} else {
/* success */
/* 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
*/
}
}
}
/* 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 {
}
}
/*
* 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;
/* 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 */
sizeof (struct scsi_extended_sense);
} else if (senselen <
sizeof (struct scsi_extended_sense)) {
/* auto request sense short */
sizeof (struct scsi_extended_sense)
- senselen;
} else {
/* auto request sense complete */
arqstat->sts_rqpkt_resid = 0;
}
arqstat->sts_rqpkt_statistics = 0;
/* copy auto request sense */
sizeof (struct scsi_extended_sense));
if (dlength) {
}
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)
*/
/* pass SCSI status up stack */
}
}
}
return (ISCSI_STATUS_SUCCESS);
}
/*
* iscsi_rx_process_rtt_rsp - Process received RTT. This means the target is
* requesting data.
*/
/* ARGSUSED */
static iscsi_status_t
{
return (status);
}
/* update expcmdsn and maxcmdsn */
/*
* Perform boundary-checks per RFC 3720 (section 10.8.4).
* The Desired Data Transfer Length must satisfy this relation:
*
* 0 < Desired Data Transfer Length <= MaxBurstLength
*/
"is larger than MaxBurstLength itt:0x%x len:0x%x - "
"protocol error",
} else {
}
return (status);
}
/*
* 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 iscsi_status_t
{
/* ASSERT(data != NULL) data is allowed to be NULL */
"received status out of order itt:0x%x statsn:0x%x "
return (ISCSI_STATUS_PROTOCOL_ERROR);
}
return (ISCSI_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 (rval);
}
/*
* iscsi_rx_process_reject_rsp - The server rejected a PDU
*/
static iscsi_status_t
{
/* make sure we only Ack Status numbers that we've actually received. */
icp->conn_expstatsn++;
} else {
"received status out of order itt:0x%x statsn:0x%x "
return (ISCSI_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 (ISCSI_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 (ISCSI_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;
(void) iscsi_rx_process_rejected_tsk_mgt(icp,
old_ihp);
break;
default:
"- received a reject for a command(0x%02x) not "
return (ISCSI_STATUS_PROTOCOL_ERROR);
}
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 itt:0x%x reason:0x%x",
return (ISCSI_STATUS_PROTOCOL_ERROR);
}
return (ISCSI_STATUS_SUCCESS);
}
/*
* iscsi_rx_process_rejected_tsk_mgt -
*/
static iscsi_status_t
{
return (ISCSI_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 (ISCSI_STATUS_SUCCESS);
}
/*
* iscsi_rx_process_task_mgt_rsp -
*/
/* ARGSUSED */
static iscsi_status_t
{
icp->conn_expstatsn++;
} else {
"received status out of order itt:0x%x statsn:0x%x "
return (ISCSI_STATUS_PROTOCOL_ERROR);
}
/* make sure we only Ack Status numbers that we've actually received. */
return (ISCSI_STATUS_PROTOCOL_ERROR);
}
/* update expcmdsn and maxcmdn */
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 */
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.
*/
return (ISCSI_STATUS_PROTOCOL_ERROR);
}
break;
default:
"received a task mgt response for a non-task mgt "
return (ISCSI_STATUS_PROTOCOL_ERROR);
}
return (ISCSI_STATUS_SUCCESS);
}
/*
* iscsi_rx_process_logout -
*
*/
/* ARGSUSED */
static iscsi_status_t
{
"received status out of order itt:0x%x statsn:0x%x "
return (ISCSI_STATUS_PROTOCOL_ERROR);
}
return (ISCSI_STATUS_PROTOCOL_ERROR);
}
}
/* update expcmdsn and maxcmdsn */
/*
* 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:
/* logout completed successfully notify the conn */
break;
default:
}
return (rval);
}
/*
* iscsi_rx_process_logout -
*
*/
/* ARGSUSED */
static iscsi_status_t
{
"received status out of order itt:0x%x statsn:0x%x "
return (ISCSI_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
* action to these events of dis/reconnecting.
* Once reconnected we perform a reenumeration.
*/
break;
/* Target has requested this connection to logout. */
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?
*/
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.
* Test 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 iscsi_status_t
{
icp->conn_expstatsn++;
} else {
"received status out of order itt:0x%x statsn:0x%x "
return (ISCSI_STATUS_PROTOCOL_ERROR);
}
return (ISCSI_STATUS_PROTOCOL_ERROR);
}
/* update expcmdsn and maxcmdsn */
/* 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 (ISCSI_STATUS_PROTOCOL_ERROR);
}
/* TTT should have matched reserved value */
"error - received text response with invalid "
return (ISCSI_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 (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);
}
/* 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 sendpdu()
* directly they should be funneled through iscsi_tx_thread.
*/
void
{
int ret = 1;
/*
* 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 */
}
/*
* 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_R2T:
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
*/
#define DEF_CDB_LEN 200
/*
* 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)
*/
#define ADDLHDRSZ(x) (sizeof (iscsi_addl_hdr_t) + (x) - \
16 - 4)
/*
* iscsi_tx_scsi -
*
*/
static iscsi_status_t
{
union {
} hdr_un;
int cdblen = 0;
/* 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
*/
}
isp->sess_cmdsn++;
/*
* Sestion 12.11 of the iSCSI specification has a good table
* should be sent.
*/
/*
* 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
*/
(imdata == first_burst_length)) {
}
}
/* total data transfer length */
}
} else {
buflen = 0;
}
/* 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 sendpdu()
* will fail is a on a connection disconnect
* in that case the command will be flushed.
*/
/*
* Check if there is additional data to transfer beyond what
* will be sent as part of the initial command. If InitialR2T
* is disabled then we should fake up a R2T so all the data,
* up to first burst length, is sent in an unsolicited
* fashion. We have already sent as much immediate data
* as possible.
*/
if ((buflen > 0) &&
/* data will be chunked at tx */
}
/* release pending queue mutex across the network call */
/* Transfer Cmd PDU */
if (imdata) {
if (ISCSI_SUCCESS(rval)) {
}
} else {
}
if (cdblen) {
}
return (rval);
}
/*
* iscsi_tx_r2t -
*
*/
static iscsi_status_t
{
/* validate the offset and length against the buffer size */
"for icmd itt:0x%x offset:0x%x length:0x%x bufsize:0x%lx",
b_bcount);
return (ISCSI_STATUS_INTERNAL_ERROR);
}
/*
* we're finished with this r2t; there could be another r2t
* waiting on us to finish, so signal it.
*/
/*
* the parent command may be waiting for us to finish; if so,
* wake the _ic_ thread
*/
return (rval);
}
/*
* iscsi_tx_data -
*/
static iscsi_status_t
{
/* verify there is data to send */
return (ISCSI_STATUS_INTERNAL_ERROR);
}
/*
* update the LUN with the amount of data we will
* transfer. If there is a failure it's because of
* a network fault and the command will get flushed.
*/
/* release pending queue mutex across the network call */
while (remainder) {
/* Check so see if we need to chunk the data */
} else {
}
/* setup iscsi data hdr */
data_sn++;
}
/* setup data */
/*
* Keep track of how much data we have
* transfer so far and how much is remaining.
*/
if (ISCSI_SUCCESS(rval)) {
} else {
break;
}
}
return (rval);
}
/*
* iscsi_tx_nop -
*
*/
static iscsi_status_t
{
/* release pending queue mutex across the network call */
return (rval);
}
/*
* iscsi_tx_abort -
*
*/
static iscsi_status_t
{
/* release pending queue mutex across the network call */
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;
}
/* release pending queue mutex across the network call */
return (rval);
}
/*
* iscsi_tx_logout -
*
*/
static iscsi_status_t
{
/* release pending queue mutex across the network call */
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
{
isp->sess_cmdsn++;
/* release pending queue mutex across the network call */
return (rval);
}
/*
* +--------------------------------------------------------------------+
* | End of protocol send routines |
* +--------------------------------------------------------------------+
*/
/*
* iscsi_handle_r2t -
*/
static void
{
/*
* the sosendmsg from a previous r2t can be slow to return;
* the array may have sent another r2t at this point, so
* wait until the first one finishes and signals us.
*/
}
/*
* try to create an R2T task to send it later. If we can't,
* we're screwed, and the command will eventually time out
* and be retried by the SCSI layer.
*/
/*
* pending queue mutex is already held by the
* tx_thread or rtt_rsp function.
*/
}
/*
* iscsi_handle_abort -
*
*/
void
iscsi_handle_abort(void *arg)
{
/* there should only be one abort */
/* pending queue mutex is already held by timeout_checks */
}
/*
* iscsi_handle_nop -
*
*/
static void
{
return;
}
}
/*
* iscsi_handle_reset -
*
*/
{
/*
* 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 */
return (ISCSI_STATUS_SUCCESS);
}
/* stall until completed */
}
/* copy rval */
if (rval == ISCSI_STATUS_SUCCESS) {
/*
* Reset was successful. We need to flush
* all active IOs.
*/
}
}
}
/* clean up */
return (rval);
}
/*
* iscsi_handle_logout - This function will issue a logout for
* the session from a specific connection.
*/
{
int rval;
/*
* 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 */
/*
* another way to do this would be to send t17 unconditionally,
* but then the _rx_ thread would get bumped out with a receive
* error, and send another t17.
*/
if (rval != ISCSI_STATUS_SUCCESS) {
}
/* clean up */
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 rqlen = SENSE_LENGTH;
/*
* If the caller didn't provide a sense buffer we need
* to allocation one to get the scsi 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) &&
}
/* clean up */
return (rval);
}
/*
* iscsi_handle_passthru_callback -
*
*/
static void
{
}
/*
* +--------------------------------------------------------------------+
* | 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
{
int rc = 1;
}
}
/*
* 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.
*/
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 */
continue;
/* Skip if timeout still in the future */
continue;
/* timeout */
}
}
}
/*
* 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);
}
}
}
/*
* +--------------------------------------------------------------------+
* | End of wd routines |
* +--------------------------------------------------------------------+
*/