Hi Michael and List,

We realized that sending the padding bytes to align the data segment to 4byte 
boundaries as a separate segment causes an issue.

When the full featured login phase starts the BHS segment is sent followed by 
the data segment. Once the data segment is received EQL targets send login 
success but are still waiting for the padding bytes as ipxe sends the padding 
in a separate segment.

Note that since the data segment length field does not include padding bytes 
the target can't make a decision on if the padding bytes will arrive at all.
So, it bases its decision on receiving the next PDU. If the next PDU is 
received on a non-aligned boundary it will close the connection.
The reason the padding bytes are never received by the target is because the 
initiator changes login state on receiving login SUCCESS from the target and 
starts SCSI initiatialization and since it doesn't allow concurrent tx threads 
the padding segment is never sent.

The target is not expecting the next set of SCSI initialization TCP segments 
because it has not yet received the padding bytes and hence closes connection.

Older EQL firmware would respond with login SUCCESS a little slower and that 
allowed the initiator to send the padding bytes in a new TCP segment before the 
LOGIN SUCCESS response was sent by the target and thus avoid this situation. 
Newer EQL firmwares respond quickly with login SUCCESS before the padding bytes 
are received and hence this issue.

Looking further  seems like this could be an issue for all PDUs as padding 
should be done such that each segment is 4byte aligned and sending them 
separately when we don't allow concurrent tx threads may become a problem in 
other situations.

rfc 3720 Section 10.2.  PDU Template, Header, and Opcodes states-
“

   All PDU segments and digests are padded to the closest integer number

   of four byte words.  For example, all PDU segments and digests start

   at a four byte word boundary and the padding ranges from 0 to 3

   bytes.  The padding bytes SHOULD be sent as 0.

"
The following changes were done in the fix.
1) Removed fns  iscsi_tx_data_padding and the corresponding data_padding state 
machine in fn iscsi_tx_step.
2) Added padding bytes before performing tx of the data segment.
3) Removed ISCSI_TX_DATA_PADDING enum.

Thanks,
Shyam Iyer
---
 src/include/ipxe/iscsi.h |    2 -
 src/net/tcp/iscsi.c      |   55 ++++++++++++++++++++--------------------------
 2 files changed, 24 insertions(+), 33 deletions(-)

diff --git a/src/include/ipxe/iscsi.h b/src/include/ipxe/iscsi.h
index 5d3d73b..b4de793 100644
--- a/src/include/ipxe/iscsi.h
+++ b/src/include/ipxe/iscsi.h
@@ -515,8 +515,6 @@ enum iscsi_tx_state {
        ISCSI_TX_AHS,
        /** Sending the data segment */
        ISCSI_TX_DATA,
-       /** Sending the data segment padding */
-       ISCSI_TX_DATA_PADDING,
 };
 
 /** State of an iSCSI RX engine */
diff --git a/src/net/tcp/iscsi.c b/src/net/tcp/iscsi.c
index fa1bb39..1ae4da7 100644
--- a/src/net/tcp/iscsi.c
+++ b/src/net/tcp/iscsi.c
@@ -568,23 +568,32 @@ static void iscsi_data_out_done ( struct iscsi_session 
*iscsi ) {
 static int iscsi_tx_data_out ( struct iscsi_session *iscsi ) {
        struct iscsi_bhs_data_out *data_out = &iscsi->tx_bhs.data_out;
        struct io_buffer *iobuf;
+       static const char pad[] = { '\0', '\0', '\0' };
        unsigned long offset;
-       size_t len;
+       size_t len, pad_len;
 
        offset = ntohl ( data_out->offset );
        len = ISCSI_DATA_LEN ( data_out->lengths );
+       pad_len = ISCSI_DATA_PAD_LEN ( data_out->lengths );
 
        assert ( iscsi->command != NULL );
        assert ( iscsi->command->data_out );
        assert ( ( offset + len ) <= iscsi->command->data_out_len );
 
-       iobuf = xfer_alloc_iob ( &iscsi->socket, len );
+       iobuf = xfer_alloc_iob ( &iscsi->socket, len + pad_len );
        if ( ! iobuf )
                return -ENOMEM;
        
        copy_from_user ( iob_put ( iobuf, len ),
                         iscsi->command->data_out, offset, len );
 
+       /*Send padding bytes along*/
+       if ( pad_len ) {
+               memcpy( iob_put( iobuf, pad_len ), pad, pad_len );
+               DBGC ( iscsi, "iSCSI %p Adding padding %d bytes \n",
+                      iscsi, pad_len );
+       }
+
        return xfer_deliver_iob ( &iscsi->socket, iobuf );
 }
 
@@ -799,15 +808,25 @@ static void iscsi_login_request_done ( struct 
iscsi_session *iscsi ) {
  */
 static int iscsi_tx_login_request ( struct iscsi_session *iscsi ) {
        struct iscsi_bhs_login_request *request = &iscsi->tx_bhs.login_request;
+       static const char pad[] = { '\0', '\0', '\0' };
        struct io_buffer *iobuf;
-       size_t len;
+       size_t len, pad_len;
 
        len = ISCSI_DATA_LEN ( request->lengths );
-       iobuf = xfer_alloc_iob ( &iscsi->socket, len );
+       pad_len = ISCSI_DATA_PAD_LEN ( request->lengths );
+       iobuf = xfer_alloc_iob ( &iscsi->socket, len + pad_len );
        if ( ! iobuf )
                return -ENOMEM;
        iob_put ( iobuf, len );
        iscsi_build_login_request_strings ( iscsi, iobuf->data, len );
+       
+       /*Send padding bytes along*/
+       if ( pad_len ) {
+               memcpy( iob_put( iobuf, pad_len ), pad, pad_len );
+               DBGC ( iscsi, "iSCSI %p Adding padding %d bytes \n",
+                      iscsi, pad_len );
+       }
+
        return xfer_deliver_iob ( &iscsi->socket, iobuf );
 }
 
@@ -1416,27 +1435,6 @@ static int iscsi_tx_data ( struct iscsi_session *iscsi ) 
{
 }
 
 /**
- * Transmit data padding of an iSCSI PDU
- *
- * @v iscsi            iSCSI session
- * @ret rc             Return status code
- * 
- * Handle transmission of any data padding in a PDU data segment.
- * iscsi::tx_bhs will be valid when this is called.
- */
-static int iscsi_tx_data_padding ( struct iscsi_session *iscsi ) {
-       static const char pad[] = { '\0', '\0', '\0' };
-       struct iscsi_bhs_common *common = &iscsi->tx_bhs.common;
-       size_t pad_len;
-       
-       pad_len = ISCSI_DATA_PAD_LEN ( common->lengths );
-       if ( ! pad_len )
-               return 0;
-
-       return xfer_deliver_raw ( &iscsi->socket, pad, pad_len );
-}
-
-/**
  * Complete iSCSI PDU transmission
  *
  * @v iscsi            iSCSI session
@@ -1494,12 +1492,7 @@ static void iscsi_tx_step ( struct iscsi_session *iscsi 
) {
                case ISCSI_TX_DATA:
                        tx = iscsi_tx_data;
                        tx_len = ISCSI_DATA_LEN ( common->lengths );
-                       next_state = ISCSI_TX_DATA_PADDING;
-                       break;
-               case ISCSI_TX_DATA_PADDING:
-                       tx = iscsi_tx_data_padding;
-                       tx_len = ISCSI_DATA_PAD_LEN ( common->lengths );
-                       next_state = ISCSI_TX_IDLE;
+                       next_state= ISCSI_TX_IDLE;
                        break;
                case ISCSI_TX_IDLE:
                        /* Nothing to do; pause processing */
-- 
1.7.6.5

_______________________________________________
ipxe-devel mailing list
[email protected]
https://lists.ipxe.org/mailman/listinfo/ipxe-devel

Reply via email to