Documented login parameters that we negotiate.
[gpxe.git] / src / net / tcp / iscsi.c
1 /*
2  * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #include <stddef.h>
20 #include <string.h>
21 #include <vsprintf.h>
22 #include <errno.h>
23 #include <assert.h>
24 #include <byteswap.h>
25 #include <gpxe/scsi.h>
26 #include <gpxe/process.h>
27 #include <gpxe/iscsi.h>
28
29 /** @file
30  *
31  * iSCSI protocol
32  *
33  */
34
35 static void iscsi_start_tx ( struct iscsi_session *iscsi );
36 static void iscsi_start_data_out ( struct iscsi_session *iscsi,
37                                    unsigned int datasn );
38
39 /****************************************************************************
40  *
41  * iSCSI SCSI command issuing
42  *
43  */
44
45 /**
46  * Build iSCSI SCSI command BHS
47  *
48  * @v iscsi             iSCSI session
49  *
50  * We don't currently support bidirectional commands (i.e. with both
51  * Data-In and Data-Out segments); these would require providing code
52  * to generate an AHS, and there doesn't seem to be any need for it at
53  * the moment.
54  */
55 static void iscsi_start_command ( struct iscsi_session *iscsi ) {
56         struct iscsi_bhs_scsi_command *command = &iscsi->tx_bhs.scsi_command;
57
58         assert ( ! ( iscsi->command->data_in && iscsi->command->data_out ) );
59
60         /* Construct BHS and initiate transmission */
61         iscsi_start_tx ( iscsi );
62         command->opcode = ISCSI_OPCODE_SCSI_COMMAND;
63         command->flags = ( ISCSI_FLAG_FINAL |
64                            ISCSI_COMMAND_ATTR_SIMPLE );
65         if ( iscsi->command->data_in )
66                 command->flags |= ISCSI_COMMAND_FLAG_READ;
67         if ( iscsi->command->data_out )
68                 command->flags |= ISCSI_COMMAND_FLAG_WRITE;
69         /* lengths left as zero */
70         command->lun = iscsi->lun;
71         command->itt = htonl ( ++iscsi->itt );
72         command->exp_len = htonl ( iscsi->command->data_in_len |
73                                    iscsi->command->data_out_len );
74         command->cmdsn = htonl ( iscsi->cmdsn );
75         command->expstatsn = htonl ( iscsi->statsn + 1 );
76         memcpy ( &command->cdb, &iscsi->command->cdb, sizeof ( command->cdb ));
77 }
78
79 /**
80  * Receive data segment of an iSCSI SCSI response PDU
81  *
82  * @v iscsi             iSCSI session
83  * @v data              Received data
84  * @v len               Length of received data
85  * @v remaining         Data remaining after this data
86  * 
87  */
88 static void iscsi_rx_scsi_response ( struct iscsi_session *iscsi, void *data,
89                                      size_t len, size_t remaining ) {
90         struct iscsi_bhs_scsi_response *response
91                 = &iscsi->rx_bhs.scsi_response;
92         int sense_offset;
93
94         /* Capture the sense response code as it floats past, if present */
95         sense_offset = ISCSI_SENSE_RESPONSE_CODE_OFFSET - iscsi->rx_offset;
96         if ( ( sense_offset >= 0 ) && len ) {
97                 iscsi->command->sense_response =
98                         * ( ( char * ) data + sense_offset );
99         }
100
101         /* Wait for whole SCSI response to arrive */
102         if ( remaining )
103                 return;
104         
105         /* Record SCSI status code */
106         iscsi->command->status = response->status;
107
108         /* Mark as completed, with error if applicable */
109         iscsi->status |= ISCSI_STATUS_DONE;
110         if ( response->response != ISCSI_RESPONSE_COMMAND_COMPLETE )
111                 iscsi->status |= ISCSI_STATUS_ERR;
112 }
113
114 /**
115  * Receive data segment of an iSCSI data-in PDU
116  *
117  * @v iscsi             iSCSI session
118  * @v data              Received data
119  * @v len               Length of received data
120  * @v remaining         Data remaining after this data
121  * 
122  */
123 static void iscsi_rx_data_in ( struct iscsi_session *iscsi, void *data,
124                                size_t len, size_t remaining __unused ) {
125         struct iscsi_bhs_data_in *data_in = &iscsi->rx_bhs.data_in;
126         unsigned long offset;
127
128         /* Copy data to data-in buffer */
129         offset = ntohl ( data_in->offset ) + iscsi->rx_offset;
130         assert ( iscsi->command != NULL );
131         assert ( iscsi->command->data_in != NULL );
132         assert ( ( offset + len ) <= iscsi->command->data_in_len );
133         memcpy ( ( iscsi->command->data_in + offset ), data, len );
134
135         /* Record SCSI status, if present */
136         if ( data_in->flags & ISCSI_DATA_FLAG_STATUS )
137                 iscsi->command->status = data_in->status;
138
139         /* If this is the end, flag as complete */
140         if ( ( offset + len ) == iscsi->command->data_in_len ) {
141                 assert ( data_in->flags & ISCSI_FLAG_FINAL );
142                 assert ( remaining == 0 );
143                 iscsi->status |= ISCSI_STATUS_DONE;
144         }
145 }
146
147 /**
148  * Receive data segment of an iSCSI R2T PDU
149  *
150  * @v iscsi             iSCSI session
151  * @v data              Received data
152  * @v len               Length of received data
153  * @v remaining         Data remaining after this data
154  * 
155  */
156 static void iscsi_rx_r2t ( struct iscsi_session *iscsi, void *data __unused,
157                            size_t len __unused, size_t remaining __unused ) {
158         struct iscsi_bhs_r2t *r2t = &iscsi->rx_bhs.r2t;
159
160         /* Record transfer parameters and trigger first data-out */
161         iscsi->ttt = ntohl ( r2t->ttt );
162         iscsi->transfer_offset = ntohl ( r2t->offset );
163         iscsi->transfer_len = ntohl ( r2t->len );
164         iscsi_start_data_out ( iscsi, 0 );
165 }
166
167 /**
168  * Build iSCSI data-out BHS
169  *
170  * @v iscsi             iSCSI session
171  * @v datasn            Data sequence number within the transfer
172  *
173  */
174 static void iscsi_start_data_out ( struct iscsi_session *iscsi,
175                                    unsigned int datasn ) {
176         struct iscsi_bhs_data_out *data_out = &iscsi->tx_bhs.data_out;
177         unsigned long offset;
178         unsigned long remaining;
179         unsigned long len;
180
181         /* We always send 512-byte Data-Out PDUs; this removes the
182          * need to worry about the target's MaxRecvDataSegmentLength.
183          */
184         offset = datasn * 512;
185         remaining = iscsi->transfer_len - offset;
186         len = remaining;
187         if ( len > 512 )
188                 len = 512;
189
190         /* Construct BHS and initiate transmission */
191         iscsi_start_tx ( iscsi );
192         data_out->opcode = ISCSI_OPCODE_DATA_OUT;
193         if ( len == remaining )
194                 data_out->flags = ( ISCSI_FLAG_FINAL );
195         ISCSI_SET_LENGTHS ( data_out->lengths, 0, len );
196         data_out->lun = iscsi->lun;
197         data_out->itt = htonl ( iscsi->itt );
198         data_out->ttt = htonl ( iscsi->ttt );
199         data_out->expstatsn = htonl ( iscsi->statsn + 1 );
200         data_out->datasn = htonl ( datasn );
201         data_out->offset = htonl ( iscsi->transfer_offset + offset );
202 }
203
204 /**
205  * Complete iSCSI data-out PDU transmission
206  *
207  * @v iscsi             iSCSI session
208  *
209  */
210 static void iscsi_data_out_done ( struct iscsi_session *iscsi ) {
211         struct iscsi_bhs_data_out *data_out = &iscsi->tx_bhs.data_out;
212
213         /* If we haven't reached the end of the sequence, start
214          * sending the next data-out PDU.
215          */
216         if ( ! ( data_out->flags & ISCSI_FLAG_FINAL ) )
217                 iscsi_start_data_out ( iscsi, ntohl ( data_out->datasn ) + 1 );
218 }
219
220 /**
221  * Send iSCSI data-out data segment
222  *
223  * @v iscsi             iSCSI session
224  */
225 static void iscsi_tx_data_out ( struct iscsi_session *iscsi ) {
226         struct iscsi_bhs_data_out *data_out = &iscsi->tx_bhs.data_out;
227         unsigned long offset;
228         unsigned long len;
229
230         offset = ( iscsi->transfer_offset + ntohl ( data_out->offset ) +
231                    iscsi->tx_offset );
232         len = ( ISCSI_DATA_LEN ( data_out->lengths ) - iscsi->tx_offset );
233         assert ( iscsi->command != NULL );
234         assert ( iscsi->command->data_out != NULL );
235         assert ( ( offset + len ) <= iscsi->command->data_out_len );
236         
237         tcp_send ( &iscsi->tcp, iscsi->command->data_out + offset, len );
238 }
239
240 /****************************************************************************
241  *
242  * iSCSI login
243  *
244  */
245
246 /**
247  * Build iSCSI login request strings
248  *
249  * @v iscsi             iSCSI session
250  *
251  * These are the initial set of strings sent in the first login
252  * request PDU.  We want the following settings:
253  *
254  *     HeaderDigest=None
255  *     DataDigest=None
256  *     MaxConnections is irrelevant; we make only one connection anyway
257  *     InitialR2T=Yes (default) [1]
258  *     ImmediateData is irrelevant; we never send immediate data
259  *     MaxRecvDataSegmentLength=8192 (default)
260  *     MaxBurstLength=262144 (default)
261  *     FirstBurstLength=262144 (default)
262  *     DefaultTime2Wait=0 [2]
263  *     DefaultTime2Retain=0 [2]
264  *     MaxOutstandingR2T=1 (default)
265  *     DataPDUInOrder=Yes (default)
266  *     DataSequenceInOrder=Yes (default)
267  *     ErrorRecoveryLevel=0 (default)
268  *
269  * [1] InitialR2T has an OR resolution function, so the target may
270  * force us to use it.  We therefore simplify our logic by always
271  * using it.
272  *
273  * [2] These ensure that we can safely start a new task once we have
274  * reconnected after a failure, without having to manually tidy up
275  * after the old one.
276  */
277 static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi,
278                                                void *data, size_t len ) {
279         return snprintf ( data, len,
280                           "InitiatorName=%s%c"
281                           "TargetName=%s%c"
282                           "SessionType=Normal%c"
283                           "DataDigest=None%c"
284                           "HeaderDigest=None%c"
285                           "DefaultTime2Wait=0%c"
286                           "DefaultTime2Retain=0%c",
287                           iscsi->initiator, 0, iscsi->target, 0,
288                           0, 0, 0, 0, 0 );
289 }
290
291 /**
292  * Build iSCSI login request BHS
293  *
294  * @v iscsi             iSCSI session
295  * @v first             Login request is the first in a sequence
296  */
297 static void iscsi_start_login ( struct iscsi_session *iscsi, int first ) {
298         struct iscsi_bhs_login_request *request = &iscsi->tx_bhs.login_request;
299         int len;
300
301         /* Construct BHS and initiate transmission */
302         iscsi_start_tx ( iscsi );
303         request->opcode = ( ISCSI_OPCODE_LOGIN_REQUEST |
304                             ISCSI_FLAG_IMMEDIATE );
305         request->flags = ( ISCSI_LOGIN_FLAG_TRANSITION |
306                            ISCSI_LOGIN_CSG_OPERATIONAL_NEGOTIATION |
307                            ISCSI_LOGIN_NSG_FULL_FEATURE_PHASE );
308         /* version_max and version_min left as zero */
309         if ( first ) {
310                 len = iscsi_build_login_request_strings ( iscsi, NULL, 0 );
311                 ISCSI_SET_LENGTHS ( request->lengths, 0, len );
312         }
313         request->isid_iana_en = htonl ( ISCSI_ISID_IANA |
314                                         IANA_EN_FEN_SYSTEMS );
315         /* isid_iana_qual left as zero */
316         request->tsih = htons ( iscsi->tsih );
317         if ( first )
318                 iscsi->itt++;
319         request->itt = htonl ( iscsi->itt );
320         /* cid left as zero */
321         request->cmdsn = htonl ( iscsi->cmdsn );
322         request->expstatsn = htonl ( iscsi->statsn + 1 );
323 }
324
325 /**
326  * Transmit data segment of an iSCSI login request PDU
327  *
328  * @v iscsi             iSCSI session
329  *
330  * For login requests, the data segment consists of the login strings.
331  */
332 static void iscsi_tx_login_request ( struct iscsi_session *iscsi ) {
333         int len;
334
335         len = iscsi_build_login_request_strings ( iscsi, tcp_buffer,
336                                                   tcp_buflen );
337         tcp_send ( &iscsi->tcp, tcp_buffer + iscsi->tx_offset,
338                    len - iscsi->tx_offset );
339 }
340
341 /**
342  * Receive data segment of an iSCSI login response PDU
343  *
344  * @v iscsi             iSCSI session
345  * @v data              Received data
346  * @v len               Length of received data
347  * @v remaining         Data remaining after this data
348  * 
349  */
350 static void iscsi_rx_login_response ( struct iscsi_session *iscsi,
351                                       void *data __unused,
352                                       size_t len __unused,
353                                       size_t remaining __unused ) {
354         struct iscsi_bhs_login_response *response
355                 = &iscsi->rx_bhs.login_response;
356
357         /* Check for fatal errors */
358         if ( response->status_class != 0 ) {
359                 printf ( "iSCSI login failure: class %02x detail %02x\n",
360                          response->status_class, response->status_detail );
361                 iscsi->status |= ( ISCSI_STATUS_DONE | ISCSI_STATUS_ERR );
362                 tcp_close ( &iscsi->tcp );
363                 return;
364         }
365
366         /* If server did not transition, send back another login
367          * request without any login strings.
368          */
369         if ( ! ( response->flags & ISCSI_LOGIN_FLAG_TRANSITION ) ) {
370                 iscsi_start_login ( iscsi, 0 );
371                 return;
372         }
373
374         /* Record TSIH for future reference */
375         iscsi->tsih = ntohl ( response->tsih );
376         
377         /* Send the SCSI command */
378         iscsi_start_command ( iscsi );
379 }
380
381 /****************************************************************************
382  *
383  * iSCSI to TCP interface
384  *
385  */
386
387 static inline struct iscsi_session *
388 tcp_to_iscsi ( struct tcp_connection *conn ) {
389         return container_of ( conn, struct iscsi_session, tcp );
390 }
391
392 /**
393  * Start up a new TX PDU
394  *
395  * @v iscsi             iSCSI session
396  *
397  * This initiates the process of sending a new PDU.  Only one PDU may
398  * be in transit at any one time.
399  */
400 static void iscsi_start_tx ( struct iscsi_session *iscsi ) {
401         assert ( iscsi->tx_state == ISCSI_TX_IDLE );
402         
403         /* Initialise TX BHS */
404         memset ( &iscsi->tx_bhs, 0, sizeof ( iscsi->tx_bhs ) );
405
406         /* Flag TX engine to start transmitting */
407         iscsi->tx_state = ISCSI_TX_BHS;
408         iscsi->tx_offset = 0;
409 }
410
411 /**
412  * Transmit data segment of an iSCSI PDU
413  *
414  * @v iscsi             iSCSI session
415  * 
416  * Handle transmission of part of a PDU data segment.  iscsi::tx_bhs
417  * will be valid when this is called.
418  */
419 static void iscsi_tx_data ( struct iscsi_session *iscsi ) {
420         struct iscsi_bhs_common *common = &iscsi->tx_bhs.common;
421
422         switch ( common->opcode & ISCSI_OPCODE_MASK ) {
423         case ISCSI_OPCODE_DATA_OUT:
424                 iscsi_tx_data_out ( iscsi );
425                 break;
426         case ISCSI_OPCODE_LOGIN_REQUEST:
427                 iscsi_tx_login_request ( iscsi );
428                 break;
429         default:
430                 assert ( 0 );
431                 break;
432         }
433 }
434
435 /**
436  * Complete iSCSI PDU transmission
437  *
438  * @v iscsi             iSCSI session
439  *
440  * Called when a PDU has been completely transmitted and the TX state
441  * machine is about to enter the idle state.  iscsi::tx_bhs will be
442  * valid for the just-completed PDU when this is called.
443  */
444 static void iscsi_tx_done ( struct iscsi_session *iscsi ) {
445         struct iscsi_bhs_common *common = &iscsi->tx_bhs.common;
446
447         switch ( common->opcode & ISCSI_OPCODE_MASK ) {
448         case ISCSI_OPCODE_DATA_OUT:
449                 iscsi_data_out_done ( iscsi );
450         default:
451                 /* No action */
452                 break;
453         }
454 }
455
456 /**
457  * Handle TCP ACKs
458  *
459  * @v iscsi             iSCSI session
460  * 
461  * Updates iscsi->tx_offset and, if applicable, transitions to the
462  * next TX state.
463  */
464 static void iscsi_acked ( struct tcp_connection *conn, size_t len ) {
465         struct iscsi_session *iscsi = tcp_to_iscsi ( conn );
466         struct iscsi_bhs_common *common = &iscsi->tx_bhs.common;
467         size_t max_tx_offset;
468         enum iscsi_tx_state next_state;
469         
470         iscsi->tx_offset += len;
471         while ( 1 ) {
472                 switch ( iscsi->tx_state ) {
473                 case ISCSI_TX_BHS:
474                         max_tx_offset = sizeof ( iscsi->tx_bhs );
475                         next_state = ISCSI_TX_AHS;
476                         break;
477                 case ISCSI_TX_AHS:
478                         max_tx_offset = 4 * ISCSI_AHS_LEN ( common->lengths );
479                         next_state = ISCSI_TX_DATA;
480                         break;
481                 case ISCSI_TX_DATA:
482                         max_tx_offset = ISCSI_DATA_LEN ( common->lengths );
483                         next_state = ISCSI_TX_DATA_PADDING;
484                         break;
485                 case ISCSI_TX_DATA_PADDING:
486                         max_tx_offset = ISCSI_DATA_PAD_LEN ( common->lengths );
487                         next_state = ISCSI_TX_IDLE;
488                         break;
489                 case ISCSI_TX_IDLE:
490                         return;
491                 default:
492                         assert ( 0 );
493                         return;
494                 }
495                 assert ( iscsi->tx_offset <= max_tx_offset );
496
497                 /* If the whole of the current portion has not yet
498                  * been acked, stay in this state for now.
499                  */
500                 if ( iscsi->tx_offset != max_tx_offset )
501                         return;
502
503                 /* Move to next state.  Call iscsi_tx_done() when PDU
504                  * transmission is complete.
505                  */
506                 iscsi->tx_state = next_state;
507                 iscsi->tx_offset = 0;
508                 if ( next_state == ISCSI_TX_IDLE )
509                         iscsi_tx_done ( iscsi );
510         }
511 }
512
513 /**
514  * Transmit iSCSI PDU
515  *
516  * @v iscsi             iSCSI session
517  * 
518  * Constructs data to be sent for the current TX state
519  */
520 static void iscsi_senddata ( struct tcp_connection *conn ) {
521         struct iscsi_session *iscsi = tcp_to_iscsi ( conn );
522         struct iscsi_bhs_common *common = &iscsi->tx_bhs.common;
523         static const char pad[] = { '\0', '\0', '\0' };
524
525         switch ( iscsi->tx_state ) {
526         case ISCSI_TX_IDLE:
527                 /* Nothing to send */
528                 break;
529         case ISCSI_TX_BHS:
530                 tcp_send ( conn, &iscsi->tx_bhs.bytes[iscsi->tx_offset],
531                            ( sizeof ( iscsi->tx_bhs ) - iscsi->tx_offset ) );
532                 break;
533         case ISCSI_TX_AHS:
534                 /* We don't yet have an AHS transmission mechanism */
535                 assert ( 0 );
536                 break;
537         case ISCSI_TX_DATA:
538                 iscsi_tx_data ( iscsi );
539                 break;
540         case ISCSI_TX_DATA_PADDING:
541                 tcp_send ( conn, pad, ( ISCSI_DATA_PAD_LEN ( common->lengths )
542                                         - iscsi->tx_offset ) );
543                 break;
544         default:
545                 assert ( 0 );
546                 break;
547         }
548 }
549
550 /**
551  * Receive data segment of an iSCSI PDU
552  *
553  * @v iscsi             iSCSI session
554  * @v data              Received data
555  * @v len               Length of received data
556  * @v remaining         Data remaining after this data
557  *
558  * Handle processing of part of a PDU data segment.  iscsi::rx_bhs
559  * will be valid when this is called.
560  */
561 static void iscsi_rx_data ( struct iscsi_session *iscsi, void *data,
562                             size_t len, size_t remaining ) {
563         struct iscsi_bhs_common_response *response
564                 = &iscsi->rx_bhs.common_response;
565
566         /* Update cmdsn and statsn */
567         iscsi->cmdsn = ntohl ( response->expcmdsn );
568         iscsi->statsn = ntohl ( response->statsn );
569
570         switch ( response->opcode & ISCSI_OPCODE_MASK ) {
571         case ISCSI_OPCODE_LOGIN_RESPONSE:
572                 iscsi_rx_login_response ( iscsi, data, len, remaining );
573                 break;
574         case ISCSI_OPCODE_SCSI_RESPONSE:
575                 iscsi_rx_scsi_response ( iscsi, data, len, remaining );
576                 break;
577         case ISCSI_OPCODE_DATA_IN:
578                 iscsi_rx_data_in ( iscsi, data, len, remaining );
579                 break;
580         case ISCSI_OPCODE_R2T:
581                 iscsi_rx_r2t ( iscsi, data, len, remaining );
582                 break;
583         default:
584                 printf ( "Unknown iSCSI opcode %02x\n", response->opcode );
585                 iscsi->status |= ( ISCSI_STATUS_DONE | ISCSI_STATUS_ERR );
586                 break;
587         }
588 }
589
590 /**
591  * Discard portion of an iSCSI PDU.
592  *
593  * @v iscsi             iSCSI session
594  * @v data              Received data
595  * @v len               Length of received data
596  * @v remaining         Data remaining after this data
597  *
598  * This discards data from a portion of a received PDU.
599  */
600 static void iscsi_rx_discard ( struct iscsi_session *iscsi __unused,
601                                void *data __unused, size_t len __unused,
602                                size_t remaining __unused ) {
603         /* Do nothing */
604 }
605
606 /**
607  * Receive basic header segment of an iSCSI PDU
608  *
609  * @v iscsi             iSCSI session
610  * @v data              Received data
611  * @v len               Length of received data
612  * @v remaining         Data remaining after this data
613  *
614  * This fills in iscsi::rx_bhs with the data from the BHS portion of
615  * the received PDU.
616  */
617 static void iscsi_rx_bhs ( struct iscsi_session *iscsi, void *data,
618                            size_t len, size_t remaining __unused ) {
619         memcpy ( &iscsi->rx_bhs.bytes[iscsi->rx_offset], data, len );
620 }
621
622 /**
623  * Receive new data
624  *
625  * @v tcp               TCP connection
626  * @v data              Received data
627  * @v len               Length of received data
628  *
629  * This handles received PDUs.  The receive strategy is to fill in
630  * iscsi::rx_bhs with the contents of the BHS portion of the PDU,
631  * throw away any AHS portion, and then process each part of the data
632  * portion as it arrives.  The data processing routine therefore
633  * always has a full copy of the BHS available, even for portions of
634  * the data in different packets to the BHS.
635  */
636 static void iscsi_newdata ( struct tcp_connection *conn, void *data,
637                             size_t len ) {
638         struct iscsi_session *iscsi = tcp_to_iscsi ( conn );
639         struct iscsi_bhs_common *common = &iscsi->rx_bhs.common;
640         void ( *process ) ( struct iscsi_session *iscsi, void *data,
641                             size_t len, size_t remaining );
642         size_t max_rx_offset;
643         enum iscsi_rx_state next_state;
644         size_t frag_len;
645         size_t remaining;
646
647         while ( 1 ) {
648                 switch ( iscsi->rx_state ) {
649                 case ISCSI_RX_BHS:
650                         process = iscsi_rx_bhs;
651                         max_rx_offset = sizeof ( iscsi->rx_bhs );
652                         next_state = ISCSI_RX_AHS;                      
653                         break;
654                 case ISCSI_RX_AHS:
655                         process = iscsi_rx_discard;
656                         max_rx_offset = 4 * ISCSI_AHS_LEN ( common->lengths );
657                         next_state = ISCSI_RX_DATA;
658                         break;
659                 case ISCSI_RX_DATA:
660                         process = iscsi_rx_data;
661                         max_rx_offset = ISCSI_DATA_LEN ( common->lengths );
662                         next_state = ISCSI_RX_DATA_PADDING;
663                         break;
664                 case ISCSI_RX_DATA_PADDING:
665                         process = iscsi_rx_discard;
666                         max_rx_offset = ISCSI_DATA_PAD_LEN ( common->lengths );
667                         next_state = ISCSI_RX_BHS;
668                         break;
669                 default:
670                         assert ( 0 );
671                         return;
672                 }
673
674                 frag_len = max_rx_offset - iscsi->rx_offset;
675                 if ( frag_len > len )
676                         frag_len = len;
677                 remaining = max_rx_offset - iscsi->rx_offset - frag_len;
678                 process ( iscsi, data, frag_len, remaining );
679
680                 iscsi->rx_offset += frag_len;
681                 data += frag_len;
682                 len -= frag_len;
683
684                 /* If all the data for this state has not yet been
685                  * received, stay in this state for now.
686                  */
687                 if ( iscsi->rx_offset != max_rx_offset )
688                         return;
689
690                 iscsi->rx_state = next_state;
691                 iscsi->rx_offset = 0;
692         }
693 }
694
695 /**
696  * Handle TCP connection closure
697  *
698  * @v conn              TCP connection
699  * @v status            Error code, if any
700  *
701  */
702 static void iscsi_closed ( struct tcp_connection *conn, int status __unused ) {
703         struct iscsi_session *iscsi = tcp_to_iscsi ( conn );
704
705         /* Clear connected flag */
706         iscsi->status &= ~ISCSI_STATUS_CONNECTED;
707
708         /* Retry connection if within the retry limit, otherwise fail */
709         if ( ++iscsi->retry_count <= ISCSI_MAX_RETRIES ) {
710                 tcp_connect ( conn );
711         } else {
712                 printf ( "iSCSI retry count exceeded\n" );
713                 iscsi->status |= ( ISCSI_STATUS_DONE | ISCSI_STATUS_ERR );
714         }
715 }
716
717 /**
718  * Handle TCP connection opening
719  *
720  * @v conn              TCP connection
721  *
722  */
723 static void iscsi_connected ( struct tcp_connection *conn ) {
724         struct iscsi_session *iscsi = tcp_to_iscsi ( conn );
725
726         /* Set connected flag and reset retry count */
727         iscsi->status |= ISCSI_STATUS_CONNECTED;
728         iscsi->retry_count = 0;
729
730         /* Prepare to receive PDUs. */
731         iscsi->rx_state = ISCSI_RX_BHS;
732         iscsi->rx_offset = 0;
733
734         /* Start logging in */
735         iscsi_start_login ( iscsi, 1 );
736 }
737
738 /** iSCSI TCP operations */
739 static struct tcp_operations iscsi_tcp_operations = {
740         .closed         = iscsi_closed,
741         .connected      = iscsi_connected,
742         .acked          = iscsi_acked,
743         .newdata        = iscsi_newdata,
744         .senddata       = iscsi_senddata,
745 };
746
747 /**
748  * Issue SCSI command via iSCSI session
749  *
750  * @v iscsi             iSCSI session
751  * @v command           SCSI command
752  * @ret rc              Return status code
753  */
754 int iscsi_issue ( struct iscsi_session *iscsi,
755                   struct scsi_command *command ) {
756         iscsi->command = command;
757         iscsi->status &= ~( ISCSI_STATUS_DONE | ISCSI_STATUS_ERR );
758
759         if ( iscsi->status & ISCSI_STATUS_CONNECTED ) {
760                 iscsi_start_command ( iscsi );
761         } else {
762                 iscsi->tcp.tcp_op = &iscsi_tcp_operations;
763                 tcp_connect ( &iscsi->tcp );
764         }
765
766         while ( ! ( iscsi->status & ISCSI_STATUS_DONE ) ) {
767                 step();
768         }
769
770         iscsi->command = NULL;
771
772         return ( ( iscsi->status & ISCSI_STATUS_ERR ) ? -EIO : 0 );     
773 }