📄 overview-ffp
字号:
2. the "cmnd" field which contains all the information needed by the target to perform the SCSI operation. (This is usually called the "CDB", for "Command Description Block".) This field contains the information needed by the SCSI system on the target to perform the SCSI operation. It includes an "opcode" value of 0x28 for READ (10), a "lun" value (which duplicates the lun field mentioned next), the "logical block address" value (i.e., the number of the logical block on the disk where this read starts), the "length" value, which is the "request_bufflen" field (mentioned next) divided by 512 (i.e., the number of logical blocks to read, each logical block being 512 bytes), and finally a "control" value. 3. the "lun" field which contains the "Logical Unit Number" used by SCSI to identify devices. This value duplicates a field in the CDB. 4. the "request_bufflen" field which contains the total number of bytes of data to be read (i.e., transfered from the target) by this command. This value is 512 times the value of a field in the CDB. 5. the "request_buffer" field, which indicates where in memory SCSI expects the data to be put as it is read from the target. 6. the "use_sg" field, which is 0 if the "request_buffer" field is the address in memory of the data buffer itself, and which is greater than 0 if the "request_buffer" field is the address in memory of a scatter-gather list containing "use_sg" data buffers. 7. the "sc_data_direction" field, which is 1 if the command is generally categorized as a "write" command (i.e., if any data flows during this command, it will be from initiator to target), and which is 2 if the command is generally categorized as a "read" command (i.e., if any data flows during this command, it will be from target to initiator). 8. the "result" field, which is filled in by the initiator when it has finished processing this command completely. The values that can be stored here are defined by SAM2. The value 0 means "GOOD". The others are various error codes which are defined as DID_XXX symbols in the file /usr/src/linux/drivers/scsi/scsi.h. (DID_OK is defined as 0.) The initiator will fill this field by opaquely copying the value in the Status field of a SCSIResponse PDU. See section 3.4.2 of draft 9 for a partial listing of these values.5.1.3.2 struct command The "iscsi_initiator_queuecommand" function creates a new "struct command" data structure, initializes the fields, and adds it to the "pending_commands" list for the current connection. The "struct command" contains a number fields that are initialized at this time. Since we are considering a READ operation, only those fields relevant to a READ are discussed here. 1. the "SCpnt" field, which is initialized with a pointer to the "Scsi_Cmnd" structure. 2. the "init_task_tag" field, which is initialized with value of the session-wide "init_task_tag". This value will be put into all PDUs sent/received during the processing of this command, as discussed above. 3. the "recvd_length" field, which is initialized to 0 and will be used by the initiator to keep track of the number of bytes of data received in one burst or sequence of DataIn PDUs. 4. the "data_offset" field, which is initialized to 0 and will be used by the initiator to keep track of where in its memory buffer to put the bytes of data received from the next DataIn PDU. 5. the "data_in_sn" field, which is initialized to 0 and will be used by the initiator to check the DataSN field of all DataIn PDUs sent by the target, as discussed above.5.1.3.3 SCSI Command PDU "iscsi_initiator_queuecommand" then sets up the header of the "SCSI Command" PDU in the "iscsi_cmd" field of the "struct command". This header contains the following fields: opcode 0x01, I bit 0 F bit 1 R bit 1 (for read) W bit 0 (no write) DSL 0 (no data in this PDU) the LUN copied from the "lun" field in the "Scsi_Cmnd" (see above), the ITT for this task (see above), the EDTL copied from the "request_bufflen" field in the "Scsi_Cmnd" (see above), the CmdSN for this command (see above), the ExpStatSN for this PDU (see above), the CDB copied from the "cmnd" field in the "Scsi_Cmnd" (see above), "iscsi_initiator_queuecommand" then attaches this newly initialized "struct command" to the end of the "pending_commands" list for this connection, and then does an "up" operation on the "tx_sem" semaphore for the session in order to signal the "tx_thread" that a PDU is ready for transmission to the target.5.1.4 Tx_Thread Each iSCSI session contains one kernel thread that performs all transmissions of PDUs to the target. This thread consists of an infinite loop that: 1. blocks on a "down" operation on the "tx_sem" semaphore for this session until some other process or thread does a corresponding "up" on this semaphore to indicate that a PDU is ready for transmission to the target. 2. for each connection open in this session, search its pending command list to see if there are any PDUs ready to be sent to the target. 3. for each PDU ready to be sent to the target, call "sock_sendmsg" to send it out over an open TCP connection to the target. 4. after a PDU has been sent, set its "tx_size" field to 0 to indicate that it has been sent. 5. after all PDUs for all connections in the session have been sent, return to step 1. Note that when a PDU is sent, tx_thread does not remove it from the "pending_commands" list for a connection because the information about the command must be retained until the initiator receives a response from the target.5.1.5 Rx_Thread Each iSCSI connection contains one kernel thread that performs all receptions of PDUs from the target. This thread consists of an infinite loop that: 1. Blocks on a call to sock_recvmsg waiting for the target to send a PDU to the initiator. This call reads only the header of this PDU into an rx_buf allocated to this connection. 2. Uses the opcode field in the PDU header to call the appropriate function to deal with PDUs of this type. If the opcode is 0x25 (DataIn) then the function rx_data is called. If the opcode is 0x21 (SCSIResponse) then the function rx_rsp is called. 3. When the PDU has been processed by the appropriate function, return to step 1.5.1.5.1 rx_data This function is called whenever the header for a DataIn PDU has been read in the rx_thread loop. This function does the following: 1. Search the list of pending commands associated with this connection to find one whose init_task_tag field matches the ITT field in the DataIn PDU. It is an error if no such command is found. 2. Extract the values from the BufferOffset and DSL fields in the DataIn PDU and check that the BufferOffset value in the DataIn PDU matches the data_offset value for this command, that the DSL field in the DataIn PDU does not exceed the value in the MaxRecvPDULength key sent by the initiator to the target, and that the total amount of data received by this command, including the data in this PDU, does not exceed the request_bufflen for this command. 3. Check that the DataSN value in the DataIn PDU matches the data_in_sn field of the command. 4. If all the checks are ok, increment the data_in_sn value for this command by 1, and the data_offset value for this command by the DSL value. 5. Finally, call sock_recvmsg to read the data attached to the DataIn PDU directly into the memory location indicated by the request_buffer field of the SCSI command (no extra copy required).5.1.5.2 rx_rsp This function is called whenever the header for a SCSIResponse PDU has been read in the rx_thread loop. This function does the following: 1. Search the list of pending commands associated with this connection to find one whose init_task_tag field matches the ITT field in the SCSIResponse PDU. It is an error if no such command is found. 2. Check that the F-bit is set to 1 (required for a SCSIResponse PDU). 3. Check that the StatSN in the PDU matches the exp_stat_sn field for this connection and then increment the exp_stat_sn field by 1. 4. Check that the ExpCmdSn in the PDU matches the cmd_sn field for this connection. 5. Check that the O-bit (Overflow), U-bit (Underflow) and Residual Count fields agree with the request_bufflen of the original command and the data_offset (which gives the amount of data actually transfered). 6. Store the Status value from the PDU into the result field of the SCSI command, then call the SCSI Mid-level "done" function for this command. 7. Remove this command from the list of pending commands for this connection because it has now been completely processed.5.1.6 Using MaxBurstSize The above discussion ignored the value of the key MaxBurstSize negotiated during login. However, this cannot be ignored, because DataIn PDUs must be sent by the target in bursts or sequences having no more than MaxBurstSize total bytes. Therefore, one READ command requiring more data than can be transfered in a single burst will be handled slightly differently that the "simple" READ involving only one burst that was discussed above. This large READ command will require the transfer of several bursts or sequences, each of which will contain several DataIn PDUs as follows: 1. one "SCSI Command" PDU sent by the initiator to deliver the READ command information to the target, 2. some number of "DataIn" PDUs sent back by the target to deliver the data in the first burst to the initiator, 3. some number of "DataIn" PDUs sent back by the target to deliver the data in the second burst to the initiator, ... 4. some number of "DataIn" PDUs sent back by the target to deliver the data in the last burst to the initiator, 5. one final "SCSI Response" PDU sent back by the target to deliver the final status to the initiator. Notice that there is no pause between bursts, and no extra PDU sent from either initiator or target to indicate the end of one burst or the beginning of another burst. It is all based on the F-bit in the DataIn PDUs. If we assume a burst contains N DataIn PDUs, then the F-bit in the first N-1 DataIn PDUs of the burst will have the F-bit set to 0, and the last (Nth) DataIn PDU of the burst will have the F-bit set to 1. Furthermore, the DataSN field in the DataIn PDUs is a counter that starts with 0 for the first DataIn PDU in the first burst, and increments by 1 for each successive DataIn PDU in this command, regardless of which burst the DataIn PDU belongs to! In other words, the DataSN field does not get reset to 0 for the first DataIn PDU of each burst -- it continues incrementing across burst boundaries, as if these boundaries did not exist.5.1.6.1 PDU trace The following gives an abbreviated trace of the PDUs sent during a READ command that requires the transfer of 102400 bytes (200 blocks of 512 bytes each) from the target to the initiator. (This is taken directly from the file "ty4" starting at line 1190.) Assumptions: 1. The command is a READ of 102400 bytes of data (200 blocks each containing 512 bytes). 2. The key MaxBurstSize=32768 was negotiated during login. 3. As above, the key MaxRecvPDULength=12288 was negotiated during login. Since each burst or sequence can contain at most MaxBurstSize=32768 bytes (64 logical blocks), the total READ of 102400 bytes (200 logical blocks) will require at least 4 bursts -- the first 3 bursts will each contain 32768 bytes (64 logical blocks), for a total of 98304 bytes (192 logical blocks); the fourth burst will contain the remaining 4096 bytes (8 logical blocks) to give the required total of 102400 bytes (200 logical blocks). Because a single DataIn PDU can contain at most MaxRecvPDULength=12288 bytes (24 logical blocks), the first 3 bursts will each contain 3 DataIn PDUs -- the first 2 DataIn PDUs will each have the F-bit set to 0 and will contain 12288 bytes (24 logical blocks), for a total of 24576 bytes (48 logical blocks); the third DataIn PDU will have the F-bit set to 1 and will contain the remaining 8192 bytes (16 logical blocks) in the burst, giving a total of 32768 bytes (64 logical blocks) for the burst. The last burst will consist of a single DataIn PDU containing 4096 bytes (8 logical blocks) of data. This is a grand total of 10 DataIn PDUs, and the DataSN field in these PDUs will increase linearly from 0 to 9, without regard to the boundaries between bursts (which is marked by the F-bit set to 1). The complete sequence of PDUs for this operation follows (note that all these PDUs carry the same ITT = 88905, and all the PDUs sent by the target contain ExpCmdSN=22239): Initiator->Target Opcode = 0x01; F = 1; R = 1; DSL = 0; EDTL = 102400; CmdSN = 22238; ExpStatSN = 41 Target -> Initiator Opcode = 0x25; F = 0; DataSN = 0; DSL = 12288; Buffer Offset = 0 First DataIn PDU of first burst (12288 bytes total so far) Opcode = 0x25; F = 0; DataSN = 1; DSL = 12288; Buffer Offset = 12288 Second DataIn PDU of first burst (24576 bytes total so far) Opcode = 0x25; F = 1; DataSN = 2; DSL = 8192; Buffer Offset = 24576 Third (last) DataIn PDU of first burst (32768 bytes total so far) Opcode = 0x25; F = 0; DataSN = 3; DSL = 12288; Buffer Offset = 32768 First DataIn PDU of second burst (45056 bytes total so far) Opcode = 0x25; F = 0; DataSN = 4; DSL = 12288; Buffer Offset = 45056 Second DataIn PDU of second burst (57344 bytes total so far) Opcode = 0x25; F = 1; DataSN = 5; DSL = 8192; Buffer Offset = 57344 Third (last) DataIn PDU of second burst (65536 bytes total so far) Opcode = 0x25; F = 0; DataSN = 6; DSL = 12288; Buffer Offset = 65536
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -