📄 target_notes
字号:
NOTES: Notes on the target implementationscsi_target_process_thread() - one for the SCSI MidLevel prints as "target_thread" big loop 1. blocks on down_interruptible(&target_data.target_sem) 2. goes through the target_data.msgq_start list looking at each message. Removes each list item in turn under control of target_data.msg_lock spinlock. 3. For each item, removes it under control of target_data.msg_lock spinlock and then does switch(msg->message) ABORT_TASK ?? LUN_RESET scsi_release() TARGET_RESET scsi_release() <others> error 4. goes through the target_data.cmd_queue_start list looking at each command. 5. For each item, does big if (cmd_curr->state == ST_NEW_CMND scsi_allocate_request() handle_cmd() ST_PENDING hand_to_front_end() ST_TO_PROCESS handle_cmd() ST_PROCESSED read() ST_DONE hand_to_front_end() ST_DEQUEUE <remove from target_data.cmd_queue> under control of target_data.add_delete spinlock scsi_release_request() <others> ignoreiscsi_server_thread() - one per TCP listening post big loop (no locks obtained or held) 1. allocates a new socket 2. blocks on accept() 3. clean_bad_session() 4. create and initialize new struct iscsi_conn for new connection 5. create iscsi_tx_thread and iscsi_rx_thread for new connectioniscsi_tx_thread() - one per connection prints as "iscsi_tx_0" big loop (no locks obtained or held) 1. blocks on down_interruptible(&conn->tx_sem) 2. goes through session->cmnd_list looking at each struct iscsi_cmnd on it for this conn NOTE: list is NOT locked while doing this! 3. does switch(cmnd->state) for each command belonging to this conn ISCSI_DISCOVERY handle_discovery_rsp() ISCSI_LOGOUT handle_logout_rsp() ISCSI_PING handle_nopin() ISCSI_DONE handle_iscsi_done() set by iscsi_xmit_response() which is called from midlevel ISCSI_MGT_FN_DONE handle_iscsi_mgt_fn_done() ISCSI_BUFFER_RDY iscsi_tx_r2t() ISCSI_DEQUEUE iscsi_dequeue() ISCSI_QUEUE_CMND_RDY send_unsolicited_data() <others> <do nothing>iscsi_rx_thread() - one per connection big loop (no locks obtained or held) 1. calls iscsi_rx_data() to read a header 2. if Header Digests in use, does crc on header, then calls iscsi_rx_data() to read crc then checks 3. does switch(opcode) to various handle_xxx() routines LOGIN_CMND handle_login() TEXT_CMND handle_discovery() SCSI_CMND handle_cmnd() SCSI_DATA_OUT handle_data() TASK_MGMT_CMND handle_task_mgt_command() LOGOUT_CMND handle_logout() NOP_OUT handle_nopout() SNACK handle_snack() <others> iscsi_tx_rjt()Semaphores: devdata->session_sem created UNLOCKED in iscsi_detect() mutex around devdata->session_list devdata->server_sem created LOCKED in iscsi_detect() wait by ??? in bring_up_portal() after starting up iscsi_server_thread wait by ??? in bring_down_portals() after sending ISCSI_SHUTDOWN_SIGNAL to iscsi_server_thread wakeup by iscsi_server_thread() after it has started up ok wakeup by iscsi_server_thread() when it exits session->cmnd_sem mutex around the list session->cmnd_list and around all calls to scsi_rx_data() and around all calls to rx_cmnd() and around all calls to scsi_target_done and around all calls to check_cmd_sn() and update to session->cmnd_id and update to session->exp_cmd_sn and updates to session->max_cmd_sn session->conn_sem mutex around the list session->conn_list cmnd->unsolicited_data_sem created LOCKED in handle_cmnd() wait by rx thread when WRITE is received in order to give midlevel chance to set up the receive buffers wakeup by midlevel when it calls back to rdy_to_xfer which does it if immediate or unsolicited data is present conn->tx_sem created LOCKED in iscsi_server_thread wait by tx thread at top of big loop wakeup conn->kill_rx_sem created LOCKED in iscsi_server_thread wait in iscsi_release_connection() after sending ISCSI_SHUTDOWN_SIGNAL to rx thread wakeup when rx thread exits conn->kill_tx_sem created LOCKED in iscsi_server_thread wait in iscsi_release_connection() after sending ISCSI_SHUTDOWN_SIGNAL to tx thread wakeup when tx thread exitsIn the iscsi layer, each command is represented by a struct iscsi_cmnd.This struct is kept in session->cmnd_list during processing This list is protected by session->cmnd_sem Values in cmnd->state for commands in this list ISCSI_QUEUE_CMND set in handle_cmnd() when new command is first received out of order means command is out-of-order by CmdSN ISCSI_QUEUE_CMND_RDY set in iscsi_rdy_to_xfer() (called by midlevel) when prior state was ISCSI_QUEUE_CMND and when cmd->data_length > 0 and midlevel has set up buffers to accept input for WRITE command ISCSI_NEW_CMND set in handle_cmnd() when new command is first received in order means command is in-order by CmdSN ISCSI_BUFFER_RDY set in iscsi_rdy_to_xfer (called by midlevel) when cmd->data_length > 0 and midlevel has set up buffers to accept input for WRITE command ISCSI_ALL_R2TS_SENT set in iscsi_tx_r2t (called by tx thread) when this command is a SCSI WRITE and ALL R2Ts have been sent on its behalf (ITT = command's ITT) ISCSI_DATA_IN set in handle_cmnd() and send_unsolicited_data() means have finished reading the full amount of data to be sent in a WRITE command ISCSI_DONE set in iscsi_rdy_to_xfer (called by midlevel) when cmd->data_length goes to 0 ISCSI_SENT means text rsp, logout rsp, nopin, scsi rsp, or a DataIn with S=1 have been sent to initiator set in handle_iscsi_done() and other places checked in (too) many places ISCSI_DEQUEUE set in handle_cmnd() and other places when state is ISCSI_SENT means command's StatSN has been acked by initiator's ExpStatSN, tx thread needs to call midlevel's scsi_target_done() before freeing it ISCSI_MGT_FN_DONE set in iscsi_task_mgt_fn_done (called by midlevel) means this command is a TMF and midlevel has finished with itFlow of a READ_10 command1. iscsi_rx_thread() reads 48-byte header into local buffer, reads and checks 4-byte header digest (if needed), identifies opcode == ISCSI_INIT_SCSI_CMND and passes conn and header to handle_cmnd().2. handle_cmnd() converts byte order in header fields, checks length, kmalloc's a struct iscsi_cmnd and initializes it from header, sets cmnd->state = ISCSI_NEW_CMND, and checks command order: a. If out of range, silently ignore it and return 0. b. If in range but out of order, sets cmnd->state = ISCSI_QUEUE_CMND, sets order = 1, and locks session->cmnd_sem. c. If in range and in order, locks session->cmnd_sem, calls rx_cmnd() and store result in cmnd->cmnd, and then calls check_queued_cmnd(). Then finishes initializing new cmnd and then adds it to end of cmnd->session->cmnd_list, changing cmnd->state of other commands on that list to ISCSI_DEQUEUE if their state was ISCSI_SENT and their StatSN is acknowledged by new command's ExpStatSN. Then unlocks session->cmnd_sem. Then if order == 1 call save_unsolicited_data() else if immediate data present in command ... else if unsolicited data present in command ...3. rx_cmnd in Midlevel kmallocs new struct Target_Scsi_Cmnd, initializes it (with copy of cdb and lun from pdu header), sets state = ST_NEW_CMND, spinlocks target_data.add_delete, sets id = ++targt_data.command_id, adds new struct to end of target_data.cmd_queue list, unlocks spinlock, does up on target_data.target_sem (which awakens scsi_target_process_thread), and returns pointer to new struct.4. scsi_target_process_thread in Midlevel finds struct Target_Scsi_Cmnd on target_data.cmd_queue list, finds state = ST_NEW_CMND, kmallocs the req field in this command, zeros the new request structure, copies the cmd data (i.e., the cdb) into the req, and calls handle_cmd().5. handle_cmd in Midlevel does switch on cdb opcode: READ_6, READ_10: calls get_allocation_length() to get number of bytes to_read from cdb, calls get_space() to allocate buffer for that number of bytes, set cmnd->state = ST_DONE.6. on return from handle_cmd() (back to scsi_target_process_thread), detects state == ST_DONE and calls hand_to_front_end()7. hand_to_front_end in Midlevel searches target_data.st_device_list for item with id = command->dev_id, finds that command->state == ST_DONE, calls curr_device->template->xmit_response, passing pointer to the command, sets command->state = ST_HANDED.8. iscsi_xmit_response calls search_iscsi_cmnd to get back struct iscsi_cmnd *cmd to item whose cmnd field equals the cmnd passed into xmit_response, sets cmd->state = ISCSI_DONE, does up on cmd->cnn->tx_sem to wake up tx thread.9. iscsi_tx_thread searches session->cmnd_list for cmnd with this conn, finds cmnd->state == ISCSI_DONE, calls handle_iscsi_done(cmnd, 0, 0).10.handle_iscsi_done finds req->sr_data_direction == SCSI_DATA_READ and req->sr_result == DID_OK << 16, so it generates all DataIn PDU sequences data_length_left keeps track of bytes of data remaining to be sent (initializes this from req->sr_bufflen) while (data_length_left > 0) /* once around for each sequence/burst */ data_send_length = max possible to send in this burst (sequence) last_seq = 1 if this is last burst needed by this command offset = offset into req->sr of first byte to be sent in this burst seq_offset = offset int req->sr of first byte to be sent in next PDU reduce data_length_left by data_send_length data_send = bytes sent so far in this burst (initially 0) while (data_send_length > 0) /* once around for each DataIn PDU */ initialize local iscsi_hdr with ISCSI_TARG_SCSI_DATA_IN data_payload_length = max possible to send in next pdu hdr->flags == F_BIT if this is last pdu in this burst hdr->flags == S_BIT if this is last pdu in last burst increase data_send by data_payload_length st_list = req->sr_buffer + cmnd->scatter_list_count niov = find_iovec_needed() + header + hdr_crc + padding + data_crc iov = kmalloc niov's cmnd->scatter_list_count += fill_iov() pad_bytes = kmalloc padding call iscsi_tx_data() kfree(iov) kfree(pad_bytes) decrease data_send_length by data_payload_length increase offset by data_payload_length set cmnd->state = ISCSI_SENTTarget Transfer Tag at offset 20 in all reply PDUs the field name in the PDU is: target_xfer_tag the value of this field is set to ALL_ONES in: handle_nopin() handle_iscsi_done() handle_discovery_rsp() it is set to ++cmnd->session->cmnd_id in: handle_cmnd() it is copied from the iscsi_cmnd in: iscsi_tx_r2t() It is used in the following replies sent by target: ISCSI_TARG_SCSI_DATA_IN 10.7 DataIn handle_iscsi_done() ISCSI_TARG_R2T 10.8 R2T iscsi_tx_r2t() ISCSI_TARG_TEXT_RSP 10.11 Text Response handle_discovery_rsp() ISCSI_TARG_NOP_IN 10.19 NopIn handle_nopin() It is NOT used in the following replies sent by target: ISCSI_TARG_SCSI_RSP 10.4 SCSI Response ISCSI_TARG_TASK_MGMT_RSP 10.6 Task Managment Function Response ISCSI_TARG_ASYNC_MSG 10.9 Asynchronous Message ISCSI_TARG_LOGIN_RSP 10.13 Login Response ISCSI_TARG_LOGOUT_RSP 10.15 Logout Response ISCSI_TARG_RJT 10.17 Reject It is used in the following requests sent by initiator: ISCSI_INIT_SCSI_DATA_OUT 10.7 DataOut handle_data() ISCSI_INIT_TEXT_CMND 10.10 Text Request handle_discovery() ISCSI_INIT_SNACK 10.16 SNACK Request handle_snack() ISCSI_INIT_NOP_OUT 10.18 NopOut handle_nopout() It is NOT used in the following requests sent by initiator: ISCSI_INIT_SCSI_CMND 10.3 SCSI Command handle_cmnd() ISCSI_INIT_TASK_MGMT_CMND10.5 Task Management handle_task_mgt_command ISCSI_INIT_LOGIN_CMND 10.12 Login Request handle_login() ISCSI_INIT_LOGOUT_CMND 10.14 Logout Request handle_logout()during a WRITE command processingcmnd->data_length is total number of bytes expected from inicmnd->data_done is total number of bytes read from initiator so far (which is also the offset when things are in order) this includes immediate, unsolicited, solicited bytes are done when cmnd->data_done >= cmnd->data_lengthcmnd->next_burst_len is number of (solicited) bytes read for this R2T so far so it should never exceed MaxBurstLengthcmnd->first_burst_len is total number of unsolicited bytes read so far so it should never exceed FirstBurstLengthcmnd->r2t_data is number of bytes that need to be solicited but that have not yet been asked for by any R2Ts initialized in handle_cmnd() when cmnd first set up tested in handle_data() to see if need to send more R2Ts tested and decremented in iscsi_tx_r2t()as R2Ts are sent done sending R2Ts when this is no longer positive
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -