📄 initiator_rx.c
字号:
/* initiator/initiator_rx.c * * vi: set autoindent tabstop=8 shiftwidth=8 : * * This file contains the functions for iscsi initiator code that are * responsible for reading and processing PDUs sent by the target. * * Copyright (C) 2001-2004 InterOperability Lab (IOL) * University of New Hampshire (UNH) * Durham, NH 03824 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * The name IOL and/or UNH may not be used to endorse or promote * products derived from this software without specific prior * written permission.*/#include "iscsi_initiator.h"#include "initiator_utilities.h"#include "initiator_error_rec.h"#include "../common/lun_packing.h"/* attributes for each opcode that can be received from target */#define ITT_OPTIONAL 0x0000#define ITT_REQUIRED 0x0100#define ITT_RESERVED 0x0200#define ITT_MASK 0x0300#define F_MUST_BE_1 0x0400#define ILLEGAL_REPLY 0x0800#define DSL_MUST_BE_0 0x1000#define LUN_MUST_BE_0 0x2000/* table of attributes (in byte 1) and related opcode (in byte 0) * for each opcode that can be received from target */static __u16 opcode_attributes[32] = {/* 0x20 NopIn ==> NopOut */ F_MUST_BE_1 | ITT_OPTIONAL | ISCSI_INIT_NOP_OUT,/* 0x21 SCSI Response ==> SCSI Command */ LUN_MUST_BE_0 | F_MUST_BE_1 | ITT_REQUIRED | ISCSI_INIT_SCSI_CMND,/* 0x22 Task Management Response ==> Task Management Request */ LUN_MUST_BE_0 | DSL_MUST_BE_0 | F_MUST_BE_1 | ITT_REQUIRED | ISCSI_INIT_TASK_MGMT_CMND,/* 0x23 Login Response ==>Login Request */ ITT_REQUIRED | ISCSI_INIT_LOGIN_CMND,/* 0x24 Text Response ==>Text Request */ ITT_REQUIRED | ISCSI_INIT_TEXT_CMND,/* 0x25 DataIn ==> SCSI Command */ ITT_REQUIRED | ISCSI_INIT_SCSI_CMND,/* 0x26 Logout Response ==> Logout Request */ LUN_MUST_BE_0 | DSL_MUST_BE_0 | F_MUST_BE_1 | ITT_REQUIRED | ISCSI_INIT_LOGOUT_CMND,/* 0x27 */ ILLEGAL_REPLY,/* 0x28 */ ILLEGAL_REPLY,/* 0x29 */ ILLEGAL_REPLY,/* 0x2a */ ILLEGAL_REPLY,/* 0x2b */ ILLEGAL_REPLY,/* 0x2c */ ILLEGAL_REPLY,/* 0x2d */ ILLEGAL_REPLY,/* 0x2e */ ILLEGAL_REPLY,/* 0x2f */ ILLEGAL_REPLY,/* 0x30 */ ILLEGAL_REPLY,/* 0x31 R2T ==> SCSI Command */ DSL_MUST_BE_0 | F_MUST_BE_1 | ITT_REQUIRED | ISCSI_INIT_SCSI_CMND,/* 0x32 Asynch ==> -none- */ F_MUST_BE_1 | ITT_RESERVED,/* 0x33 */ ILLEGAL_REPLY,/* 0x34 */ ILLEGAL_REPLY,/* 0x35 */ ILLEGAL_REPLY,/* 0x36 */ ILLEGAL_REPLY,/* 0x37 */ ILLEGAL_REPLY,/* 0x38 */ ILLEGAL_REPLY,/* 0x39 */ ILLEGAL_REPLY,/* 0x3a */ ILLEGAL_REPLY,/* 0x3b */ ILLEGAL_REPLY,/* 0x3c */ ILLEGAL_REPLY,/* 0x3d */ ILLEGAL_REPLY,/* 0x3e */ ILLEGAL_REPLY,/* 0x3f Reject ==> -none- */ LUN_MUST_BE_0 | F_MUST_BE_1 | ITT_RESERVED};#ifdef ISCSI_CHECK_PDU_FORMAT/* utility function called by all rx___() functions when they * determine that their reserved fields are not all 0 */static void __attribute__ ((no_instrument_function))reserved_not_0(char *pdu_name){ if (TRACE_TEST(TRACE_ISCSI)) { TRACE_WARNING("reserved fields in %s header not all 0\n", pdu_name); }}#endifstatic void __attribute__ ((no_instrument_function))debug_print_ip(struct connection *conn, char *message){ char ip_string[INET6_ADDRSTRLEN+2], port_string[8]; if (cnv_inet_to_string(conn->ip_address, ip_string, port_string) > 0) { TRACE(TRACE_DEBUG, "%s from %s:%s\n", message, ip_string, port_string); }}/* * searches conn's pending_commands list for a command with init_task_tag that * matches this_itt. * returns pointer to that command if found, * NULL if not found */static inline struct command * __attribute__ ((no_instrument_function))find_command_by_itt(__u32 this_itt, struct connection *conn){ struct command *cmd; list_for_each_entry(cmd, &conn->pending_commands, link) { if (cmd->init_task_tag == this_itt) { return cmd; } } return NULL;}/**************************************************************************** * executed only by rx thread. * the session->sess_lock MUST be held by the calling process/thread. * Called only by iscsi_initiator_rx_thread(). * This function processes the DataIn payload of a DataIn PDU. It fills the * scatter-gather buffers provided by the SCSI Mid-level. The data digest * is also checked (if such checking has been enabled). * * Parameters: - current_connection which is a ptr to the struct where the SCSI * DataIn Header was received. * - related_command which is a ptr to the struct for the SCSI READ * command to which this DataIn is related. * Return Value: >0 success, session lock is locked * =0 failure, session lock is locked * <0 failure, session lock is NOT locked * * While reading, the following variables in the related_command maintain state: * r2t_range_list.limit sum of the size of all finally accepted DataIn * pdus in command -- incremented by size of * every good recvd data-in pdu that will not be * retransmitted later. * r2t_range_list.offset offset to first DataIN pdu in current sequence. * set at start of an out-of-order seq if * in-order pdus. set at end of sequence * of out-of-order pdus * data_offset expected offset in next in-order DataIn pdu * incremented by size of every recvd DataIn pdu. * set at start of an out-of-order sequence * recvd_length sum of size of recvd data-in pdus in this * sequence incremented by size of every recvd * data-in pdu reset to 0 at end of each sequence . * used to test for start of a new sequence when 0. * pdu_range_list.limit largest limit of any out-of-order pdu * in current seq . * pdu_range_list.offset smallest offset of any out-of-order pdu in * cur seq used to set r2t_range_list.offset at * end of seq. ****************************************************************/static intrx_data(struct connection *current_connection, __u32 local_itt, struct command *related_command, __u32 size, char *string){ struct iscsi_targ_scsi_data_in *data_in_header; int received = 1, delta; int over_flow_data_size = 0; struct session *related_session; struct init_error_rec err_rec; char *over_flow_buffer = NULL; __u32 lun; __u32 sg_size; TRACE(TRACE_ENTER_LEAVE, "Enter rx_data, current_connection %p\n", current_connection); data_in_header = (struct iscsi_targ_scsi_data_in *) current_connection->rx_buf; sg_size = 0; if (likely(related_command != NULL && related_command->SCpnt != NULL)) sg_size = related_command->SCpnt->use_sg; related_session = current_connection->my_session;#ifdef ISCSI_CHECK_PDU_FORMAT /* Check if reserved fields are zero or not */ if (unlikely(!((data_in_header->rsvd1 == 0) && ((data_in_header->flags & (W_BIT|BRO_BIT|BRU_BIT)) == 0)))) { reserved_not_0("SCSI DataIn"); }#endif if (unlikely(data_in_header->flags & A_BIT)) { /* A bit is 1 -- MUST have ErrorRecoveryLevel > 0, and TTT and LUN must be valid */ /* Draft 20, Section 10.7.2 A (Acknowledge) bit * "The Target MUST NOT set to 1 the A bit for sessions with * ErrorRecoveryLevel=0. The initiator MUST ignore the A bit * set to 1 for sessions with ErrorRecoveryLevel=0. */ if (related_session->oper_param->ErrorRecoveryLevel == 0) { TRACE_WARNING("Ignoring A_Bit=1 in DataIn, ITT %u, with" " ErrorRecoveryLevel=0\n", local_itt); } /* Draft 20, Section 10.7.4 Target Transfer Tag and LUN * "On incoming data, the Target Transfer Tag and LUN MUST be * provided by the target if the A bit is set to 1; otherwise * they are reserved. ... * * The Target Transfer Tag values are not specified by this * protocol except that the value 0xffffffff is reserved and * means that the Target Transfer Tag is not supplied. If the * Target Transfer Tag is provided, then the LUN field MUST hold * a valid value and be consistent with whatever was specified * with the command; otherwise, the LUN field is reserved." */ if (data_in_header->target_xfer_tag == ALL_ONES) { /* TTT is reserved, this is an error */ TRACE_ERROR ("DataIn with A bit = 1 has reserved TTT 0x%08x, " "expected valid value\n", ALL_ONES); } else if (related_command != NULL && related_command->SCpnt != NULL) { /* TTT is valid, LUN must be same as in related Cmnd */ lun = unpack_lun((__u8 *) & data_in_header->lun);/* Ming Zhang, mingz@ele.uri.edu */#ifdef K26 if (lun != related_command->SCpnt->device->lun) { TRACE_ERROR( "DataIn for ITT %u has LUN %u, expected %u\n", local_itt, lun, related_command->SCpnt->device->lun); }#else if (lun != related_command->SCpnt->lun) { TRACE_ERROR( "DataIn for ITT %u has LUN %u, expected %u\n", local_itt, lun, related_command->SCpnt->lun); } /* else LUN matches the command's LUN, all ok */#endif } } else { /* A bit is 0 -- TTT can be reserved, doesn't have to be */ if (data_in_header->target_xfer_tag == ALL_ONES) { /* TTT is reserved */ if (unlikely(data_in_header->lun != 0LL)) { /* TTT is reserved, LUN is not, that's bad */ TRACE_ERROR("DataIn with reserved TTT 0x%08x, " "non-reserved LUN %s\n", ALL_ONES, string_llx(data_in_header->lun, string)); } /* else LUN is also reserved, all ok */ } else if (related_command != NULL && related_command->SCpnt != NULL) { /* TTT has valid value, LUN must be same as in related Cmnd */ lun = unpack_lun((__u8 *) & data_in_header->lun);/* Ming Zhang, mingz@ele.uri.edu */#ifdef K26 if (unlikely(lun != related_command ->SCpnt->device->lun)) { TRACE_ERROR ("DataIn with ITT %u has invalid LUN %u, " "expected %u\n", local_itt, lun, related_command->SCpnt->device->lun); }#else if (unlikely(lun != related_command->SCpnt->lun)) { TRACE_ERROR ("DataIn with ITT %u has invalid LUN %u, " "expected %u\n", local_itt, lun, related_command->SCpnt->lun); }#endif } }#ifdef ISCSI_CHECK_PDU_FORMAT if (data_in_header->flags & S_BIT) { /* this command is a "phase collapse" with "good" status */ if (unlikely(!(data_in_header->flags & F_BIT))) { /* F bit not set, but it should be when S bit is set */ /* Draft 20, Section 10.7.3 Flags (byte 1) * "bit 7 S (status) - set to indicate that the Command * Status field contains status. If this bit is set * to 1, the F bit MUST also be set to 1." */ TRACE_ERROR( "DataIn with ITT %u, S bit = 1 has F Bit = 0, " "expected 1\n", local_itt); } }#endif data_in_header->offset = ntohl(data_in_header->offset); data_in_header->data_sn = ntohl(data_in_header->data_sn); if (unlikely(related_command==NULL || related_command->SCpnt==NULL)) { /* We lost the ptr to the command or to the SCSI Midlevel struct, can't do anything */ TRACE(TRACE_ERROR_RECOVERY, "No related command/SCpnt: ITT %u, size %u, offset %u, " "DataSN %u\n", local_itt, size, data_in_header->offset, data_in_header->data_sn); if (size > 0) { /* read in and ignore this PDU's data payload */ received = recv_ignore_data(current_connection, size); } } else { /* have matched the DataIn to a legitimate SCSI READ request */ /* Check if DataSN field in header is what we expected */ delta = data_in_header->data_sn - related_command->data_in_sn; if (unlikely(delta != 0)) { TRACE_ERROR("DataIn with ITT %u, got DataSN %u, " "expected %u\n", local_itt, data_in_header->data_sn, related_command->data_in_sn); if (delta < 0) { /* duplicate DataSn, just silently ignore this PDU */ if (size > 0) { /* read in and ignore this PDU's data payload */ received = recv_ignore_data( current_connection, size); } goto out; } else { /* DataIn PDU Sequence Error - SAI */ err_rec.curr_conn = current_connection; err_rec.related_cmd = related_command; err_rec.err_type = SEQUENCE_ERR; if ((received = init_do_error_recovery(&err_rec)) <= 0) goto out; /* Fall thru and use this out-of-order data. * Force the expected data offset to the * one received in order to avoid a * cascade of error messages. */ related_command->data_offset = data_in_header->offset; /* for the same reason, force the start of a new sequence */ related_command->recvd_length = 0; } } /* Draft 20, Section 10.7.1 F (Final) Bit * "Splitting the data stream into sequences does not affect * DataSN counting on Data-In PDUs." * * Draft 20, Section 10.7.5 DataSN * "For input (read) or bidirectional Data-IN PDUs, the DataSN * is the input PDU number within the data transfer for the * command identified by the Initiator Task Tag." * * Therefore, we do not reset data_in_sn at the end of a data-in * sequence -- it continues incrementing over all data-in * sequence boundaries. */ related_command->data_in_sn++; /* Check if we are overflowing the SCSI Mid-level buffers */ if (unlikely((over_flow_data_size = data_in_header->offset + size - related_command->SCpnt->request_bufflen) > 0)){ TRACE_ERROR("DataIn with ITT %u, Received buffer size " "%u bytes will overflow SCSI buffer by %d\n", local_itt, size, over_flow_data_size); if (related_command->SCpnt->request_bufflen > data_in_header->offset)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -