📄 overview-ini
字号:
OVERVIEW.txt------------ vi: set autoindent tabstop=4 shiftwidth=4 : this document is written in ordinary ASCII text with tabs set to a width of 4.The UNH iscsi reference initiator is implemented as a loadable modulefor the Linux kernel. This document is the beginnings of a documentto explain the internals of this module.The login phase will be described in a separate document. Thisdocument explains the general structure and the internal organizationof the Full Feature PhaseThreads-------Once the login phase is complete and the connection to a target has beenestablished, there are the following threads running: 1 tx_thread for each session 1 rx_thread for each connectionMultiple connections per session, and multiple sessions per initiatorare both supported.The tx_thread does all the sending on all connections belonging to a session.The rx_thread does all the receiving on one connection.Most of the time the rx_thread is blocked on a call to sock_recvmsg,waiting for a PDU to arrive from the target. Also most of the timethe tx_thread is blocked on a semaphore waiting for an "up" signal thata new PDU has been enqueued for transmission. There are 3 sourcesfor a new PDU: 1. the SCSI midlevel calls the iscsi_initiator_queuecommand() function with a SCSI CDB to be delivered to the target. This function will create a SCSI Command PDU and enqueue it for the tx_thread. 2. the SCSI midlevel calls the iscsi_initiator_abort() function in order to abort a previously issued command. This function will create a Task Management PDU and enqueue it for the tx_thread. 3. the rx_thread can generate a reply to a PDU it has received. At present there are two types of PDUs that can be generated in this manner -- a DataOut PDU is generated as a reply to an R2T sent by the target, and a NopOut PDU is generated as a reply to a NopIn sent by the target (that was not a reply to a previously issued NopOut).Session-specific signaling semaphores-------------------------------------Each session has three signaling semaphores defined in the session structure: 1. tx_sem 2. tx_done_sem 3. task_mgt_semtx_sem is initialized to locked when a session is created, and the tx_threadnormally blocks on a call to down_interruptible() on this semaphore. Wheneverthe rx_thread or the functions called by the SCSI midlevel enqueue a new PDUto be transmitted to the target associated with this session, they do anup() on the session's tx_sem to wake up the tx_thread (which will causethe tx_thread to actually transmit the PDU on the proper connection inthe proper order).tx_done_sem is also initialized to locked when a session is created. A newsession is created via the /proc interface, so the thread that creates asession is the one that is processing the write to the /proc interface, whichis handled by our iscsi_initiator_proc_info() function. After all thedata structures for the new session have been correctly created andinitialized, and the login phase has successfully concluded, the tx_threadis started. The /proc thread does a down() on tx_done_sem to wait untilthe tx_thread has actually started and initialized itself, at which timeit does the corresponding up() on tx_done_sem.Once going, the tx_thread does not do another up() on the tx_done_sem untilit terminates itself for any reason. The function close_session() is calledto terminate a session, which is triggered by two different sources (eachrunning in its own thread created by the user-level command): if the moduleis unloaded using /sbin/rmmod, at which time our iscsi_initiator_release()function is called; and if the session is terminated by a write to the/proc interface, as handled by our iscsi_initiator_proc_info() function.The close_session() function will send a kill signal to the tx_thread and thenblock on a down_interruptible() on the tx_done_sem to wait until the tx_threadshuts itself down cleanly.task_mgt_sem is also initialized to locked when a session is created.When the iscsi_initiator_abort() function is called by the SCSI midlevel,a task management command is queued for the tx_thread, a local timer isactivated to wait for at most 3 seconds for a response from the target,and then iscsi_initiator_abort() does a down() on the task_mgt_sem for thesession. If the timer expires before a response is received, the interruptservice routine for this timer, abort_timer_function(), will do an up()on the task_mgt_sem. If a response is received, it will be processed byrx_task_mgt_rsp(), which also does an up() on the task_mgt_sem for thesession.Connection-specific signaling semaphores----------------------------------------Each connection has one signaling sempahore defined in the connectionstructure: 1. rx_done_semrx_done_sem is initialized to locked when a connection is created, and therx_thread does an up() on it when it terminates for any reason. The functionclose_connection() will send a kill signal to the rx_thread and then block ona down_interruptible() on the rx_done_sem to wait until the rx_thread shutsitself down cleanly.rx_done_sem is initialized to locked when a connection is created. A newconnection is created via the /proc interface, so the thread that creates aconnection is the one that is processing the write to the /proc interface,which is handled by our iscsi_initiator_proc_info() function. After all thedata structures for the new connection have been correctly created andinitialized, and the login phase has successfully concluded, the rx_threadis started. The /proc thread does a down() on rx_done_sem to wait untilthe rx_thread has actually started and initialized itself, at which timeit does the corresponding up() on rx_done_sem.Once going, the rx_thread does not do another up() on the rx_done_sem untilit terminates itself for any reason. The function close_connection() iscalled to terminate a connection, which is triggered by two different sources(each running in its own thread created by the user-level command): if themodule is unloaded using /sbin/rmmod, at which time ouriscsi_initiator_release() function is called; and if the connection isterminated by a write to the /proc interface, as handled by ouriscsi_initiator_proc_info() function. The close_connection() function willsend a kill signal to the rx_thread and then block on a down_interruptible()on the rx_done_sem to wait until the rx_thread shuts itself down cleanly.Timers------There are is one local timer created and destroyed for each task managementcommand, as explained above under the description of the task_mgt_sem.There is also one session-specific timer called tx_timer. This is createdand initialized in init_session(), which is called by create_session()whenever a new session is created. When it starts up, the tx_threadcalls restart_tx_timer() with the "nop_period" converted to jiffies.The nop_period is a session-specific variable that can be "forced" bythe iscsi_manage program to a non-zero value which is the nop period inseconds. What this means is if there is no activity received on aconnection during one nop period, the initiator will send a NopOut PDUto the target to "ping" it to see if it is still alive. This NopOut PDUhas data attached, and a NopIn response from the target with the samedata attached is expected.restart_tx_timer() activates the session-specific tx_timer with theperiod supplied as a parameter, assuming certain other sanity conditionsare met. When this timer expires, the kernel calls the deal_with_tx_timer()function, which does an up() on the session-specific tx_sem (therebywaking the tx_thread if it is blocked) and then calls restart_tx_timer()again to keep the timer running. If the tx_thread itself receives anEAGAIN error from a call to sock_sendmsg(), meaning that data could notbe written to a socket because it was backed up, then the tx_threadalso calls restart_tx_timer() with a value of TX_RESTART_PERIOD (10)and then blocks again on the tx_sem for that session. This gives theTCP/IP sublayers 10 jiffies (100 milliseconds) to clear the buffersbefore the tx_thread tries again to send data.recv_a_message()----------------After login has been completed, there is only a single place wheresock_recvmsg() is called by the isci_initiator, and this call is in themain input routine called recv_iovector(). There are 3 parameters torecv_iovector(): 1. A pointer to the connection structure on which to read. 2. The number of bytes to read, which MUST be positive. 3. The number of io vectors which have been set up in the connection's rx_iov array.recv_iovector() uses the rx_msg area of the connection structureto set up the message header required by sock_recvmsg(), but the i/o vectormust have been previously set up in the connection's rx_iov array by thecaller to sock_recvmsg().The caller to recv_iovector() MUST hold the session->sess_lock spinlock priorto the call, with the flags saved in the lock_flags field for the currentsession. Just before calling sock_recvmsg(), recv_iovector() releasesthis lock, thereby releasing the global exclusive access in the(highly likely) case that the sock_recvmsg() call will block until dataarrives from the target. When sock_recvmsg() returns, successfully or not,recv_iovector() gets the session->sess_lock spinlock again to regain globalexclusive access to all the session structures. If sock_recvmsg() fails or isinterrupted by a signal, recv_iovector() releases the lock and returns anegative error code. If sock_recvmsg() succeeds, recv_iovector() returns thenumber of bytes received (which will always be equal to the number of bytesexpected, because recv_iovector() will loop until this number has been receivedor an error occurs). It is for this reason that the parameter specifyingthe number of bytes to read MUST be positive when recv_iovector() is called.To summarize, there are 2 possible return values from recv_iovector(): 1. > 0 Success, io_request_lock is locked 2. < 0 Failure, io_request_lock is NOT lockedThere are only 2 calls to recv_iovector() in the iscsi initiator: 1. From recv_pdu_header() to read the next PDU header. 2. From recv_data_in_data() to read data into a SCSI scatter-gather list.recv_pdu_header()---------------This routine is called in order to set up the connection's rx_iovto read a 48-byte or 52-byte header (if header digests are in use)into the connections rx_buf area.recv_data_in_data()-----------------This routine is called in order to set up the connection's rx_iovto read data into scsi buffers. This routine maps the scsi midlevel'sscatter-gather list into the necessary iovector slots, and takes careof adding padding and/or the data digest and/or overflow data.Key Parameters--------------1. The number of key parameters recognized in this implementation of Draft 20 is given in the file "common/text_param.h" as: #define MAX_CONFIG_PARAMS 28
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -