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