Walking thru a TEXT exchange on the UNH target.

Conceptual Overview
-------------------

The purpose of a text exchange in iSCSI is to declare and negotiate
values for iSCSI text keys between an initiator and a target.  A text
exchange is always started by the initiator.  There can be at most
one text exchange in progress on a connection at any given time.

Text exchange in iSCSI is not simple.  In principle, it is a single task
consisting of a sequence of pairs of pdus, all carrying the same ITT.
Each pair consists of a Text Request pdu sent by the initiator followed by
a Text Response pdu sent by the target.  Each Text Request pdu consumes a
CmdSN on the initiator, and each Text Response pdu consumes a StatSN on
the target.  The task (and the exchange) ends with a Text Response pdu
that has its F bit set to 1.  All Text Request pdus in the exchange must
have the same value for the I bit (i.e., all 0 or all 1).

However, the sequence of pairs should actually be viewed as consisting of
consecutive subsequences of pairs, where a subsequence is determined by
which end point, initiator or target, is driving the exchange during the
subsequence.  The exchange always begins with a subsequence driven by the
initiator and ends with a subsequence driven by the target.

A subsequence driven by the initiator begins and ends with Text Request
pdus.  If the first pdu in the subsequence has its C bit set to 1 (and its
F bit reset to 0), the target can only respond with an empty Text Response
pdu having both C = 0 and F = 0.  This subsequence continues as long as the
initiator sends Text Request pdus with C = 1 (and F = 0).  The subsequence
ends when the initiator sends a Text Request pdu having its C bit reset to
0.  During such a subsequence, the target must simply concatenate all the
text carried in Text Request pdus into one big internal buffer (called the
Logical-Text-Data-Segment, or LTDS), and cannot process any of it until the
last Text Request pdu in the subsequence (i.e., the one with C = 0) has
been received and its text concatenated at the end of the LTDS.  At that
point the target is expected to begin driving the next subsequence.  Note
that a degenerate subsequence driven by the initiator will begin and end
with a single Text Request pdu having its C bit reset to 0.

A subsequence driven by the target begins and ends with Text Response pdus.
If the first pdu in the subsequence has its C bit set to 1 (and its F bit
reset to 0), the initiator can only respond with an empty Text Request pdu
having both C = 0 and F = 0.  This subsequence continues as long as the
target sends Text Response pdus with C = 1 (and F = 0).  The subsequence
ends when the target sends a Text Response pdu having its C bit reset to 0.
During such a subsequence, the initiator must simply concatenate all the
text carried in Text Response pdus into one LTDS, and cannot process any of
it until the last Text Response pdu in the subsequence (i.e., the one with
C = 0) has been received and its text concatenated at the end of the LTDS.
Note that a degenerate subsequence driven by the target will begin and end
with a single Text Response pdu having its C bit reset to 0.

The last Text Request pdu in a subsequence driven by the initiator will
have its F bit set to 1 only if the initiator believes that it will not
want to send any more text data to the target after this Text Request pdu
as part of this task.  The last Text Response pdu in a subsequence driven
by the target will have its F bit set to 1 only if the previous Text
Request pdu from the initiator had its F = 1 and if the target has nothing
more to send to the initiator after this Text Response pdu as part of this
task.  A Text Response pdu with F = 1 must also have TTT = 0xffffffff.
Note that this Text Response ends the entire exchange.

When a target sends a Text Response pdu with F = 0 to the initiator, it
must also set the TTT to a unique value different from 0xffffffff.  When an
initiator sends a Text Request pdu other than the first pdu in the task, it
should set the TTT to the TTT received in the preceding Text Response pdu
if it wants to continue with the current negotiation.  If it wants to
restart the current negotiation, the initiator sends a Text Request pdu
containing TTT = 0xffffffff.  This causes both the initiator and target to
completely forget about any information previously negotiated as part of
this task, and to restart a fresh negotiation, considering the Text Request
pdu with TTT = 0xffffffff to be the first pdu in the new negotiation.  A
target resets a negotiation by sending a Reject PDU with reason 0x0b
"Negotiation Reject" that contains the header of the received Text Request
pdu.  This Reject also terminates the task, discarding any partial results
previously negotiated in this task.


Implementation Overview
-----------------------
Processing of a Text exchange in the target bounces back and forth between
the rx-thread and the tx-thread.  While an exchange is in progress, the
connection's text_in_progress field points to a struct iscsi_cmnd that
holds the information relative to the in-progress exchange.

An exchange always starts when the rx-thread receives a new Text Request
pdu from the initiator.  This is processed in the function
handle_text_request(), which creates a new struct iscsi_cmnd and points
conn->text_in_progress at it.  If the command arrives in-order, as
determined by its CmdSN, do_text_request() is called to do all the
processing.  The command's state will be left by the rx-thread at
ISCSI_ASK_FOR_MORE_TEXT if the initiator will continue to drive this
subsequence, or ISCSI_SEND_TEXT_RESPONSE if the target will start to drive
the next subsequence.  If the command arrives out-of-order, its state is
left at ISCSI_QUEUE_OTHER until it becomes the next in-order command, at
which point the function deliver_queue_other() will call do_text_request().

In either case, the tx-thread is awakened to build the next Text Response
pdu and send it to the initiator.  This is done by handle_discovery_rsp()
when the state is ISCSI_ASK_FOR_MORE_TEXT or ISCSI_SEND_TEXT_RESPONSE.  The
state of this command will be left by the tx-thread at ISCSI_SENT if the
exchange is finished, ISCSI_BLOCKED_SENDING_TEXT if the target will
continue to drive this subsequence, or ISCSI_AWAIT_MORE_TEXT if the
initiator will continue to drive this subsequence (or start driving the
next subsequence, as appropriate).

Of course, the rx-thread must always call handle_text_request() when it
receives a Text Request pdu, because without some processing it is
impossible to know whether this is the first pdu in a new text exchange, or
the next pdu in an on-going text exchange.  This is all sorted out in the
function do_text_request().


States
------
To keep track of the subsequences during a text exchange, the target uses
the following states in the cmnd->state field of a command structure:
    ISCSI_NEW_CMND
	The initial (transient) state for the new struct iscsi_cmnd created
	when the rx-thread receives the first Text Request pdu for a task.
	Set by the rx-thread in get_new_cmnd() which is called from
	handle_text_request().  Exchange is being driven by the initiator.
    ISCSI_QUEUE_OTHER
	The state when a Text Request pdu arrives out of order, as
	determined by its CmdSN.  Set by the rx-thread in
	handle_text_request().  Processed by either rx-thread or tx-thread
	in the function deliver_queue_other().  Exchange is being driven by
	the initiator.
    ISCSI_SEND_TEXT_RESPONSE
	The state when a reply Text Response pdu has to be generated and
	sent to the initiator by the tx-thread.  Set by the rx-thread.
	Processed by the tx-thread in handle_discovery_rsp().  Exchange is
	being driven by the target.
    ISCSI_ASK_FOR_MORE_TEXT
	The state when an empty Text Response pdu has to be generated and
	sent to the initiator by the tx-thread.  Set by the rx-thread.
	Processed by the tx-thread in handle_discovery_rsp().
	Exchange is being driven by the initiator.
    ISCSI_BLOCKED_SENDING_TEXT
	The state when the target is driving the exchange, and must wait
	for an empty Text Request pdu from the initiator in order to
	continue. Set by the tx-thread.  Processed by the rx-thread when
	it receives the next Text Request from the initiator (which should
	contain no text).  Exchange is being driven by the target.
    ISCSI_AWAIT_MORE_TEXT
	The state when the target must wait for more text in a Text Request
	pdu from the initiator.  Set by the tx-thread.  Processed by the
	rx-thread when it recieves the next Text Request from the initiator.
	Exchange is being driven by the initiator.
    ISCSI_SENT
	The state after the target sends its last Text Response pdu (with F
	= 1) for a complete text exchange task.  Set by the tx-thread.
	Exchange is now finished.
    ISCSI_DEQUEUE
	The state after the Text Response pdu with F = 1 has been
	acknowledged by the initiator (i.e., the initiator has send some
	other pdu with ExpStatSN greater than the StatSN in the Text
	Response pdu).  Also the state after the target has received a Text
	Request pdu with TTT = 0xffffffff, indicating a negotiation reset.
	Set by rx-thread. Processed by tx-thread in iscsi_dequeue().
	The task is now finished.


Relevant variables and fields in structures
-------------------------------------------
    conn->text_in_progress_sem
	lock used when processing a text request or response.
    conn->text_in_progress
	pointer to the struct iscsi_cmnd of the Text exchange currently in
	progress on this connection, NULL if none.  The standard limits the
	number of in-progress Text exchanges to one per connection.

    cmnd->opcode_byte
	the opcode byte from the first Text Request pdu, never changed.
    cmnd->command_flags
	the flags byte initialized from the most recently received Text
	Request pdu.  Holds the current C bit and F bit during processing.
    cmnd->init_task_tag
	the task's ITT, set by first Text Request pdu, never changed.
    cmnd->target_xfer_tag
	the TTT generated by the target, returned by the initiator.
    cmnd->cmd_sn
	in an in-progress command, the CmdSN from the most recently
	received Text Request pdu.
    cmnd->stat_sn
	in an in-progress command, the StatSN sent in the last Text
	Response pdu.
    cmnd->first_burst_len
	the DataSegmentLength of the received Text Request pdu.
    cmnd->ping_data
	in a newly received Text Request pdu, the buffer holding the text
	data.  in an in-progress command driven by the target, the LTDS.
    cmnd->in_progress_buffer
	in an in-progress command being driven by the initiator, the LTDS.
	(allocated in accumulate_text_input() with size MAX_TEXT_LEN)
    cmnd->data_length
	the total number of bytes in the LTDS.
	in an in-progress command being driven by the initiator, this is
	incremented by the DSL of each newly received Text Request pdu.
	in an in-progress commmand being driven by the target, this
	remains constant.
    cmnd->data_done
	unused in an in-progress command being driven by the initiator.
	in an in-progress command being driven by the target, the number of
	bytes from the LTDS sent to the initiator so far.
    cmnd->reply_hdr
	in an in-progress command, the header of the last Text Response pdu
	sent to the initiator (saved for retransmission if needed).
    cmnd->reply_data
	in an in-progress command, a pointer to the data sent in the last
	Text Response pdu.  This points within the LTDS of the command.
	(saved for retransmission if needed).
    cmnd->reply_size
	in an in-progress command, the number of bytes of data sent in the
	last Text Response pdu.  (save for retransmission if needed).


Details
-------
1. rx-thread in iscsi_target.c
	-> main loop in iscsi_rx_thread()
		read Text Request Header (opcode 0x04)
		read and check header crc if header digest is on
		dispatch on opcode to case ISCSI_INIT_TEXT_CMND
		which calls handle_text_request()
	-> handle_text_request()
		convert big-endian to host pdu fields IN PLACE
		call get_new_cmnd() to allocate clean struct iscsi_cmnd *cmnd
		cmnd->state = ISCSI_NEW_CMND
		fill in fields in cmnd from pdu
		if pdu's DataSegmentLength (pdu->length) is positive
			call read_single_data_seg() to read the text data into
			a newly allocated buffer stored in cmnd->ping_data
			(free cmnd and leave on fatal error during reading)
		else give warning about text request with no data attached
		call check_cmd_sn() to check CmdSN of pdu for in-order cmnd
		if cmnd is out of range
			print error, ack_sent_cmnds(), free cmnd and ignore it
		if cmnd is out of order
			cmnd->state = ISCSI_QUEUE_OTHER
			call ack_send_cmnds() to add to session->cmnd_list
		if cmnd is in order (or is immediate)
			call do_text_request()
			call ack_send_cmnds() to add to session->cmnd_list
	---------> do_text_request() in scsi_target.c
			lock connection's text_in_progress_sem
			give error if cmnd's ITT = 0xffffffff (i.e., reserved)
			give error if both C=1 and F=1
			if text exchange is already in progress on this conn
				give error if cmnd's ITT not the inprogress ITT
				give error if both I bits are not the same
				if cmnd's TTT is 0xffffffff, reset the exchange
					in_progress->state = ISCSI_DEQUEUE
					connection's text_in_progress = NULL
				else
					error if cmnd's TTT not in progress TTT
					if in progress state is blocked
						error if cmnd has data attached
						error if cmnd has C=1
						set in progress state to send
						call copy_in_progress_stuff()
					error if in progress state not awaitmore
			else
				error if cmnd's TTT not 0xffffffff (reserved)
			if nothing in progress, make this command be in progress
			call accumulate_text_input() to add text from this
				command to the in progress text
			if this is the in progress command
				save this as the connection's in progress cmnd
			else
				call copy_in_progress_stuff()
			if cmnd's C bit is set
				set in progress state = ask for more
			else
				move in progress buffer to in progress ping_data
				call generate_text_response
	------------------> generate_text_request() in scsi_target.c
				call parse_text_buffer() to classify type of
					text buffer and, if have only
					SendTargets=, get its value
				allocate a "big" buffer to hold response
				for each target in use that is part of what is
					being asked for
					add it as TargetName=
					and add all the portal groups as
					TargetAddress=
				free existing ping_data buffer (holding request)
				save new buffer as ping_data (holding response)
				cmnd->data_length = size of buffer
				cmnd->data_done = 0
				cmnd->state = ISCSI_SEND_TEXT_RESPONSE
				wake up tx-thread if needed

2. tx-thread in iscsi_target.c
	-> main loop in iscsi_tx_thread()
		wake up after blocking on conn->tx_sem
		look at each command in session->cmnd_list
			dispatch on state to case ISCSI_SEND_TEXT_RESPONSE
			which calls handle_discovery_rsp()
			repeat loop
	-> handle_discovery_rsp()
		lock connection's text_in_progress_sem
		check that cmnd's state is still valid and is still in progress
		build text response pdu, header in local iscsi_hdr array
		opcode = ISCSI_TARG_TEXT_RSP
		if sending only one key per text response
			size = length of that key from buffer
			add size to cmnd's data_done
			set state to ISCSI_BLOCKED_SENDING_TEXT
			save cmnd as connection's text_in_progress
		else if amount of text to send exceeds max send length
			size = only max amount
			add size to cmnd's data_done
			set C bit in header
			set state to ISCSI_BLOCKED_SENDING_TEXT
			save cmnd as connection's text_in_progress
		else if initiator's request had F bit set
			set F bit in header
			set TTT to 0xffffffff
			set state to ISCSI_SENT
			do not save cmnd (NULL in connection's text_in_progress)
		else (initiator's request had F = 0)
			reset cmnd's data_length to 0
			move cmnd's ping_data to cmnd's in_progress_buffer
			set state to ISCSI_AWAIT_MORE_TEXT
			save cmnd as connection's text_in_progress
		generate TTT if we expect initiator to send another text request
		fill in header fields under session->cmnd_sem lock
		call send_hdr_plus_1_data() to send the text response pdu
		wake up tx-thread if necessary
		unlock connection's text_in_progress_sem
