📄 overview-ini-scsi
字号:
OVERVIEW.scsi-------------This file describes the way the iscsi_initiator utilizes theinterface provided by the Linux SCSI mid-level subsystem.Error handling:--------------use_new_eh_code should be set to 1 in the Scsi_Host_Template.Addressing:----------SCSI has a 4-level addressing scheme: Linux SCSI /proc/scsi/scsi name name name ---- ---- ---- host Adaptor Number Host bus Channel Number Channel target Id Number Id lun Logical Unit Number LunThe "host" is a number assigned by the Linux SCSI subsystem to Host BusAdaptors as they register by calling scsi_register(). These numbersare sequential, starting with 0. If iSCSI is the only SCSI adaptoron this system, then it will register as number 0.The "bus" is always 0 for us -- it is supposed to be the number ofhardware buses an HBA can access.The "target" is the number of the target device on the SCSI bus.This corresponds to a single session in iSCSI, so that the firstsession is target number 0, the second 1, etc.The "lun" is always 0 in Linux.queuecommand handling:---------------------The SCSI mid-level always does a: spin_lock_irqsave(&io_request_lock, flags);prior to every call to the driver's queuecommand function in theScsi_Host_Template, and a spin_unlock_irqrestore(&io_request_lock, flags);when that function returns back to the mid-level. Therefore, while inthe iscsi_initiator_queuecommand() function, the "io_request_lock" is heldby the running process.The value returned by the driver's queuecommand should be: ==0 Success !=0 FailureThe SCSI mid-level always passes the function scsi_done() as the secondparameter on every call to the driver's queuecommand function in theScsi_Host_Template.According to comments in "/usr/src/linux/drivers/scsi/hosts.h": The done() function must only be called after QueueCommand() has returned!!However, a review of many drivers show that they often call done() beforereturning from the QueueCommand() function when the command can becompleted immediately (usually due to an error).The done() function must be called with the "io_request_lock" held by thecaller.The result passed in the result field of the command when the driver callsthe mid-level's done() function is encoded as: byte 0 SCSI status code byte 1 SCSI 1-byte message byte 2 host error byte (i.e., DID_xxx << 16) byte 3 mid-level error returnDID_xxx host error codes are defined in "/usr/src/linux/drivers/scsi/scsi.h".These are always shifted left by 16 bits to get them into byte 2!!Some common codes are: DID_OK 0x00 No error DID_NO_CONNECT 0x01 No connect before timeout DID_BAD_TARGET 0x03 Bad target DID_ABORT 0x05 Command aborted by mid-level DID_ERROR 0x07 Internal error DID_RESET 0x08 Command reset by mid-levelFor purposes of synchronization, we depend on the fundamental premise of theLinux kernel, which is that a process or thread cannot be preempted once itis running in kernel mode.This means once it gets the cpu in kernel mode, a process or thread runs untilit itself gives up the cpu, and while running no other process or thread willrun in kernel mode on that cpu. In an SMP system, there can be other processesor threads in kernel mode on other cpus, which is why we need spinlocks.Since the scsi mid-level always locks/unlocks the "io_request_lock" aroundcalls to the queuecommand(), eh_abort_handler(), eh_device_reset_handler(),etc., and because the done() call-back function expects this same lock tobe held around a call to it, we will also use this same lock for ourfunctions in order to prevent race conditions in SMP systems.Beware -- these spinlocks have not been tested yet on a multiprocessor system.Processing PDUs sent by the target----------------------------------The headers for all PDUs sent by the target will be received by the rx_threadfor a connection (each connection has its own rx_thread).The only opcodes that should be received from a target are: ITT related command 0x20 Nop In F=1 optional -none- or NopOut 0x21 SCSI Response F=1 required SCSI Command 0x22 Task Management Response DSL=0 F=1 required T.M. Request 0x23 Login Response required Login Request 0x24 Text Response required Text Request 0x25 Data In required SCSI Command 0x26 Logout Response DSL=0 F=1 required Logout Request 0x31 R2T DSL=0 F=1 required SCSI Command 0x32 Asynchronous Message F=1 reserved -none- 0x3f Reject F=1 reserved -none-The rx_thread does some standard processing on all received PDUsbefore dispatching to a pdu-specific routine based on the opcodefield of the received PDU. This standard processing includes:1. A check is made that the TotalAHSLength field (byte at offset 4 in each pdu) is zero (because we do not yet support Additional Header Segments of any kind). This error is fatal.2. If Header Digests are in use, the digest is computed for the 48-byte pdu header. If the computed CRC does not equal the CRC contained at the end of the header (4 bytes at offset 48 in each pdu) then an error message is printed. At this point the PDU should be discarded. However, for now, we just keep going as if the CRC had been correct. This will change when we implement error recovery properly.3. A check is made that the old X bit (bit 7 in the byte at offset 0 in each pdu) is 0. A warning is given if it is not.4. For drafts 9 and earlier, a check is made that the I bit (bit 6 in the byte at offset 0 in each pdu) is set to 1. For drafts later than 9, the opposite check is made -- that the I bit (bit 6 in the byte at offset 0 in each pdu) is set to 0 (reserved).5. A check is made that the opcode (the low-order 6 bits in the byte at offset 0 in each pdu) is legal (see the table above). This error is fatal.6. A check is made that the F bit (bit 7 in the byte at offset 1 in each pdu) is 1. If it is 0, a further check is made that the attributes of the reply opcode allow F = 0 and if not, a warning messages is printed.7. A check is made that the ITT (the 4 bytes at offset 16 in each pdu) is legal (see the table above). There are 3 cases: 1. ITT is reserved - for drafts 9 and earlier, its value should be 0. - for drafts after 9, its value should be 0xffffffff. 2. ITT is optional - if value is 0xfffffff then it is reserved, else search pending commands list for related command (which must be found and match the expected opcode). 3. ITT is required - its value should NOT be 0xffffffff. search pending commands list for related command (which must be found and match the expected opcode).8. A check is made that if the LUN (the 8 bytes at offset 8 in each pdu) is not 0 then the opcode allows this. A warning is given if not.9. A check is made that if the Data Segment Length (the 3 bytes at offset 5 in each pdu) is not 0 then the opcode allows this. This error is fatal.10. When the DSL is legally > 0, it is checked against MaxRecvPDULength (for drafts 9 and later) or DataPDULength (for drafts before 9).11. For all response PDUs except DataIn, if the DSL is > 0 then read the attached data into a newly allocated buffer, and if Data Digests are in use, compute the digest and check it against the received digest value. This is all done in the function recv_locally(). Note that this is not done for DataIn PDUs because the data sent in them will be read directly into the buffers reserved for the related SCSI READ operation by the SCSI Mid-Level. This read is done in the DataIn specific routine rx_data().12. For all response PDUs except DataIn with the S-bit NOT set to 1, the StatSN field received in the response PDU's header is checked against the exp_stat_sn value for the connection on which the PDU was received. If they are not equal, an error message is given. For all those PDUs in which the StatSN value is checked, except for R2T or NopIn with a valid Target Transfer Tag (i.e., TTT != 0xffffffff), the exp_stat_sn value for the connection is then incremented by 1.13. For all response PDUs, the ExpCmdSN field received in the response PDU's header is checked against the cur_cmd_sn value for the session. If they are not equal, an error messages is given.After all this common processing on a response PDU received from a targethas been completed, the rx_thread dispatches (via a switch statement on theresponse PDU's opcode) to an opcode-specific routine with a name of the formrx_xxx, where xxx indicates the specific opcode (in abbreviated form).Once the opcode-specific rx_xxx routine returns, any data buffer allocatedin step 11 above is freed unless the opcode-specific routine indicated thatit is still in use. At present, the only time it would be still in use isif it is being transmitted back to the target in a NopOut ping operation.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -