c138d9b6967e791d1d8e841fc6b1c8f5e66ca857
[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.
253  */
254 static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi,
255                                                void *data, size_t len ) {
256         return snprintf ( data, len,
257                           "InitiatorName=%s%c"
258                           "TargetName=%s%c"
259                           "SessionType=Normal%c"
260                           "DataDigest=None%c"
261                           "HeaderDigest=None%c"
262                           "DefaultTime2Wait=0%c"
263                           "DefaultTime2Retain=0%c",
264                           iscsi->initiator, 0, iscsi->target, 0,
265                           0, 0, 0, 0, 0 );
266 }
267
268 /**
269  * Build iSCSI login request BHS
270  *
271  * @v iscsi             iSCSI session
272  * @v first             Login request is the first in a sequence
273  */
274 static void iscsi_start_login ( struct iscsi_session *iscsi, int first ) {
275         struct iscsi_bhs_login_request *request = &iscsi->tx_bhs.login_request;
276         int len;
277
278         /* Construct BHS and initiate transmission */
279         iscsi_start_tx ( iscsi );
280         request->opcode = ( ISCSI_OPCODE_LOGIN_REQUEST |
281                             ISCSI_FLAG_IMMEDIATE );
282         request->flags = ( ISCSI_LOGIN_FLAG_TRANSITION |
283                            ISCSI_LOGIN_CSG_OPERATIONAL_NEGOTIATION |
284                            ISCSI_LOGIN_NSG_FULL_FEATURE_PHASE );
285         /* version_max and version_min left as zero */
286         if ( first ) {
287                 len = iscsi_build_login_request_strings ( iscsi, NULL, 0 );
288                 ISCSI_SET_LENGTHS ( request->lengths, 0, len );
289         }
290         request->isid_iana_en = htonl ( ISCSI_ISID_IANA |
291                                         IANA_EN_FEN_SYSTEMS );
292         /* isid_iana_qual left as zero */
293         request->tsih = htons ( iscsi->tsih );
294         if ( first )
295                 iscsi->itt++;
296         request->itt = htonl ( iscsi->itt );
297         /* cid left as zero */
298         request->cmdsn = htonl ( iscsi->cmdsn );
299         request->expstatsn = htonl ( iscsi->statsn + 1 );
300 }
301
302 /**
303  * Transmit data segment of an iSCSI login request PDU
304  *
305  * @v iscsi             iSCSI session
306  *
307  * For login requests, the data segment consists of the login strings.
308  */
309 static void iscsi_tx_login_request ( struct iscsi_session *iscsi ) {
310         int len;
311
312         len = iscsi_build_login_request_strings ( iscsi, tcp_buffer,
313                                                   tcp_buflen );
314         tcp_send ( &iscsi->tcp, tcp_buffer + iscsi->tx_offset,
315                    len - iscsi->tx_offset );
316 }
317
318 /**
319  * Receive data segment of an iSCSI login response PDU
320  *
321  * @v iscsi             iSCSI session
322  * @v data              Received data
323  * @v len               Length of received data
324  * @v remaining         Data remaining after this data
325  * 
326  */
327 static void iscsi_rx_login_response ( struct iscsi_session *iscsi,
328                                       void *data __unused,
329                                       size_t len __unused,
330                                       size_t remaining __unused ) {
331         struct iscsi_bhs_login_response *response
332                 = &iscsi->rx_bhs.login_response;
333
334         /* Check for fatal errors */
335         if ( response->status_class != 0 ) {
336                 printf ( "iSCSI login failure: class %02x detail %02x\n",
337                          response->status_class, response->status_detail );
338                 iscsi->status |= ( ISCSI_STATUS_DONE | ISCSI_STATUS_ERR );
339                 tcp_close ( &iscsi->tcp );
340                 return;
341         }
342
343         /* If server did not transition, send back another login
344          * request without any login strings.
345          */
346         if ( ! ( response->flags & ISCSI_LOGIN_FLAG_TRANSITION ) ) {
347                 iscsi_start_login ( iscsi, 0 );
348                 return;
349         }
350
351         /* Record TSIH for future reference */
352         iscsi->tsih = ntohl ( response->tsih );
353         
354         /* Send the SCSI command */
355         iscsi_start_command ( iscsi );
356 }
357
358 /****************************************************************************
359  *
360  * iSCSI to TCP interface
361  *
362  */
363
364 static inline struct iscsi_session *
365 tcp_to_iscsi ( struct tcp_connection *conn ) {
366         return container_of ( conn, struct iscsi_session, tcp );
367 }
368
369 /**
370  * Start up a new TX PDU
371  *
372  * @v iscsi             iSCSI session
373  *
374  * This initiates the process of sending a new PDU.  Only one PDU may
375  * be in transit at any one time.
376  */
377 static void iscsi_start_tx ( struct iscsi_session *iscsi ) {
378         assert ( iscsi->tx_state == ISCSI_TX_IDLE );
379         
380         /* Initialise TX BHS */
381         memset ( &iscsi->tx_bhs, 0, sizeof ( iscsi->tx_bhs ) );
382
383         /* Flag TX engine to start transmitting */
384         iscsi->tx_state = ISCSI_TX_BHS;
385         iscsi->tx_offset = 0;
386 }
387
388 /**
389  * Transmit data segment of an iSCSI PDU
390  *
391  * @v iscsi             iSCSI session
392  * 
393  * Handle transmission of part of a PDU data segment.  iscsi::tx_bhs
394  * will be valid when this is called.
395  */
396 static void iscsi_tx_data ( struct iscsi_session *iscsi ) {
397         struct iscsi_bhs_common *common = &iscsi->tx_bhs.common;
398
399         switch ( common->opcode & ISCSI_OPCODE_MASK ) {
400         case ISCSI_OPCODE_DATA_OUT:
401                 iscsi_tx_data_out ( iscsi );
402                 break;
403         case ISCSI_OPCODE_LOGIN_REQUEST:
404                 iscsi_tx_login_request ( iscsi );
405                 break;
406         default:
407                 assert ( 0 );
408                 break;
409         }
410 }
411
412 /**
413  * Complete iSCSI PDU transmission
414  *
415  * @v iscsi             iSCSI session
416  *
417  * Called when a PDU has been completely transmitted and the TX state
418  * machine is about to enter the idle state.  iscsi::tx_bhs will be
419  * valid for the just-completed PDU when this is called.
420  */
421 static void iscsi_tx_done ( struct iscsi_session *iscsi ) {
422         struct iscsi_bhs_common *common = &iscsi->tx_bhs.common;
423
424         switch ( common->opcode & ISCSI_OPCODE_MASK ) {
425         case ISCSI_OPCODE_DATA_OUT:
426                 iscsi_data_out_done ( iscsi );
427         default:
428                 /* No action */
429                 break;
430         }
431 }
432
433 /**
434  * Handle TCP ACKs
435  *
436  * @v iscsi             iSCSI session
437  * 
438  * Updates iscsi->tx_offset and, if applicable, transitions to the
439  * next TX state.
440  */
441 static void iscsi_acked ( struct tcp_connection *conn, size_t len ) {
442         struct iscsi_session *iscsi = tcp_to_iscsi ( conn );
443         struct iscsi_bhs_common *common = &iscsi->tx_bhs.common;
444         size_t max_tx_offset;
445         enum iscsi_tx_state next_state;
446         
447         iscsi->tx_offset += len;
448         while ( 1 ) {
449                 switch ( iscsi->tx_state ) {
450                 case ISCSI_TX_BHS:
451                         max_tx_offset = sizeof ( iscsi->tx_bhs );
452                         next_state = ISCSI_TX_AHS;
453                         break;
454                 case ISCSI_TX_AHS:
455                         max_tx_offset = 4 * ISCSI_AHS_LEN ( common->lengths );
456                         next_state = ISCSI_TX_DATA;
457                         break;
458                 case ISCSI_TX_DATA:
459                         max_tx_offset = ISCSI_DATA_LEN ( common->lengths );
460                         next_state = ISCSI_TX_DATA_PADDING;
461                         break;
462                 case ISCSI_TX_DATA_PADDING:
463                         max_tx_offset = ISCSI_DATA_PAD_LEN ( common->lengths );
464                         next_state = ISCSI_TX_IDLE;
465                         break;
466                 case ISCSI_TX_IDLE:
467                         return;
468                 default:
469                         assert ( 0 );
470                         return;
471                 }
472                 assert ( iscsi->tx_offset <= max_tx_offset );
473
474                 /* If the whole of the current portion has not yet
475                  * been acked, stay in this state for now.
476                  */
477                 if ( iscsi->tx_offset != max_tx_offset )
478                         return;
479
480                 /* Move to next state.  Call iscsi_tx_done() when PDU
481                  * transmission is complete.
482                  */
483                 iscsi->tx_state = next_state;
484                 iscsi->tx_offset = 0;
485                 if ( next_state == ISCSI_TX_IDLE )
486                         iscsi_tx_done ( iscsi );
487         }
488 }
489
490 /**
491  * Transmit iSCSI PDU
492  *
493  * @v iscsi             iSCSI session
494  * 
495  * Constructs data to be sent for the current TX state
496  */
497 static void iscsi_senddata ( struct tcp_connection *conn ) {
498         struct iscsi_session *iscsi = tcp_to_iscsi ( conn );
499         struct iscsi_bhs_common *common = &iscsi->tx_bhs.common;
500         static const char pad[] = { '\0', '\0', '\0' };
501
502         switch ( iscsi->tx_state ) {
503         case ISCSI_TX_IDLE:
504                 /* Nothing to send */
505                 break;
506         case ISCSI_TX_BHS:
507                 tcp_send ( conn, &iscsi->tx_bhs.bytes[iscsi->tx_offset],
508                            ( sizeof ( iscsi->tx_bhs ) - iscsi->tx_offset ) );
509                 break;
510         case ISCSI_TX_AHS:
511                 /* We don't yet have an AHS transmission mechanism */
512                 assert ( 0 );
513                 break;
514         case ISCSI_TX_DATA:
515                 iscsi_tx_data ( iscsi );
516                 break;
517         case ISCSI_TX_DATA_PADDING:
518                 tcp_send ( conn, pad, ( ISCSI_DATA_PAD_LEN ( common->lengths )
519                                         - iscsi->tx_offset ) );
520                 break;
521         default:
522                 assert ( 0 );
523                 break;
524         }
525 }
526
527 /**
528  * Receive data segment of an iSCSI PDU
529  *
530  * @v iscsi             iSCSI session
531  * @v data              Received data
532  * @v len               Length of received data
533  * @v remaining         Data remaining after this data
534  *
535  * Handle processing of part of a PDU data segment.  iscsi::rx_bhs
536  * will be valid when this is called.
537  */
538 static void iscsi_rx_data ( struct iscsi_session *iscsi, void *data,
539                             size_t len, size_t remaining ) {
540         struct iscsi_bhs_common_response *response
541                 = &iscsi->rx_bhs.common_response;
542
543         /* Update cmdsn and statsn */
544         iscsi->cmdsn = ntohl ( response->expcmdsn );
545         iscsi->statsn = ntohl ( response->statsn );
546
547         switch ( response->opcode & ISCSI_OPCODE_MASK ) {
548         case ISCSI_OPCODE_LOGIN_RESPONSE:
549                 iscsi_rx_login_response ( iscsi, data, len, remaining );
550                 break;
551         case ISCSI_OPCODE_SCSI_RESPONSE:
552                 iscsi_rx_scsi_response ( iscsi, data, len, remaining );
553                 break;
554         case ISCSI_OPCODE_DATA_IN:
555                 iscsi_rx_data_in ( iscsi, data, len, remaining );
556                 break;
557         case ISCSI_OPCODE_R2T:
558                 iscsi_rx_r2t ( iscsi, data, len, remaining );
559                 break;
560         default:
561                 printf ( "Unknown iSCSI opcode %02x\n", response->opcode );
562                 iscsi->status |= ( ISCSI_STATUS_DONE | ISCSI_STATUS_ERR );
563                 break;
564         }
565 }
566
567 /**
568  * Discard portion of an iSCSI PDU.
569  *
570  * @v iscsi             iSCSI session
571  * @v data              Received data
572  * @v len               Length of received data
573  * @v remaining         Data remaining after this data
574  *
575  * This discards data from a portion of a received PDU.
576  */
577 static void iscsi_rx_discard ( struct iscsi_session *iscsi __unused,
578                                void *data __unused, size_t len __unused,
579                                size_t remaining __unused ) {
580         /* Do nothing */
581 }
582
583 /**
584  * Receive basic header segment of an iSCSI PDU
585  *
586  * @v iscsi             iSCSI session
587  * @v data              Received data
588  * @v len               Length of received data
589  * @v remaining         Data remaining after this data
590  *
591  * This fills in iscsi::rx_bhs with the data from the BHS portion of
592  * the received PDU.
593  */
594 static void iscsi_rx_bhs ( struct iscsi_session *iscsi, void *data,
595                            size_t len, size_t remaining __unused ) {
596         memcpy ( &iscsi->rx_bhs.bytes[iscsi->rx_offset], data, len );
597 }
598
599 /**
600  * Receive new data
601  *
602  * @v tcp               TCP connection
603  * @v data              Received data
604  * @v len               Length of received data
605  *
606  * This handles received PDUs.  The receive strategy is to fill in
607  * iscsi::rx_bhs with the contents of the BHS portion of the PDU,
608  * throw away any AHS portion, and then process each part of the data
609  * portion as it arrives.  The data processing routine therefore
610  * always has a full copy of the BHS available, even for portions of
611  * the data in different packets to the BHS.
612  */
613 static void iscsi_newdata ( struct tcp_connection *conn, void *data,
614                             size_t len ) {
615         struct iscsi_session *iscsi = tcp_to_iscsi ( conn );
616         struct iscsi_bhs_common *common = &iscsi->rx_bhs.common;
617         void ( *process ) ( struct iscsi_session *iscsi, void *data,
618                             size_t len, size_t remaining );
619         size_t max_rx_offset;
620         enum iscsi_rx_state next_state;
621         size_t frag_len;
622         size_t remaining;
623
624         while ( 1 ) {
625                 switch ( iscsi->rx_state ) {
626                 case ISCSI_RX_BHS:
627                         process = iscsi_rx_bhs;
628                         max_rx_offset = sizeof ( iscsi->rx_bhs );
629                         next_state = ISCSI_RX_AHS;                      
630                         break;
631                 case ISCSI_RX_AHS:
632                         process = iscsi_rx_discard;
633                         max_rx_offset = 4 * ISCSI_AHS_LEN ( common->lengths );
634                         next_state = ISCSI_RX_DATA;
635                         break;
636                 case ISCSI_RX_DATA:
637                         process = iscsi_rx_data;
638                         max_rx_offset = ISCSI_DATA_LEN ( common->lengths );
639                         next_state = ISCSI_RX_DATA_PADDING;
640                         break;
641                 case ISCSI_RX_DATA_PADDING:
642                         process = iscsi_rx_discard;
643                         max_rx_offset = ISCSI_DATA_PAD_LEN ( common->lengths );
644                         next_state = ISCSI_RX_BHS;
645                         break;
646                 default:
647                         assert ( 0 );
648                         return;
649                 }
650
651                 frag_len = max_rx_offset - iscsi->rx_offset;
652                 if ( frag_len > len )
653                         frag_len = len;
654                 remaining = max_rx_offset - iscsi->rx_offset - frag_len;
655                 process ( iscsi, data, frag_len, remaining );
656
657                 iscsi->rx_offset += frag_len;
658                 data += frag_len;
659                 len -= frag_len;
660
661                 /* If all the data for this state has not yet been
662                  * received, stay in this state for now.
663                  */
664                 if ( iscsi->rx_offset != max_rx_offset )
665                         return;
666
667                 iscsi->rx_state = next_state;
668                 iscsi->rx_offset = 0;
669         }
670 }
671
672 /**
673  * Handle TCP connection closure
674  *
675  * @v conn              TCP connection
676  * @v status            Error code, if any
677  *
678  */
679 static void iscsi_closed ( struct tcp_connection *conn, int status __unused ) {
680         struct iscsi_session *iscsi = tcp_to_iscsi ( conn );
681
682         /* Clear connected flag */
683         iscsi->status &= ~ISCSI_STATUS_CONNECTED;
684
685         /* Retry connection if within the retry limit, otherwise fail */
686         if ( ++iscsi->retry_count <= ISCSI_MAX_RETRIES ) {
687                 tcp_connect ( conn );
688         } else {
689                 printf ( "iSCSI retry count exceeded\n" );
690                 iscsi->status |= ( ISCSI_STATUS_DONE | ISCSI_STATUS_ERR );
691         }
692 }
693
694 /**
695  * Handle TCP connection opening
696  *
697  * @v conn              TCP connection
698  *
699  */
700 static void iscsi_connected ( struct tcp_connection *conn ) {
701         struct iscsi_session *iscsi = tcp_to_iscsi ( conn );
702
703         /* Set connected flag and reset retry count */
704         iscsi->status |= ISCSI_STATUS_CONNECTED;
705         iscsi->retry_count = 0;
706
707         /* Prepare to receive PDUs. */
708         iscsi->rx_state = ISCSI_RX_BHS;
709         iscsi->rx_offset = 0;
710
711         /* Start logging in */
712         iscsi_start_login ( iscsi, 1 );
713 }
714
715 /** iSCSI TCP operations */
716 static struct tcp_operations iscsi_tcp_operations = {
717         .closed         = iscsi_closed,
718         .connected      = iscsi_connected,
719         .acked          = iscsi_acked,
720         .newdata        = iscsi_newdata,
721         .senddata       = iscsi_senddata,
722 };
723
724 /**
725  * Issue SCSI command via iSCSI session
726  *
727  * @v iscsi             iSCSI session
728  * @v command           SCSI command
729  * @ret rc              Return status code
730  */
731 int iscsi_issue ( struct iscsi_session *iscsi,
732                   struct scsi_command *command ) {
733         iscsi->command = command;
734         iscsi->status &= ~( ISCSI_STATUS_DONE | ISCSI_STATUS_ERR );
735
736         if ( iscsi->status & ISCSI_STATUS_CONNECTED ) {
737                 iscsi_start_command ( iscsi );
738         } else {
739                 iscsi->tcp.tcp_op = &iscsi_tcp_operations;
740                 tcp_connect ( &iscsi->tcp );
741         }
742
743         while ( ! ( iscsi->status & ISCSI_STATUS_DONE ) ) {
744                 step();
745         }
746
747         iscsi->command = NULL;
748
749         return ( ( iscsi->status & ISCSI_STATUS_ERR ) ? -EIO : 0 );     
750 }