Provide a SCSI device interface to the iSCSI protocol
[people/sha0/gpxe.git] / src / net / tcp / iscsi.c
1 #include <stddef.h>
2 #include <string.h>
3 #include <vsprintf.h>
4 #include <errno.h>
5 #include <assert.h>
6 #include <byteswap.h>
7 #include <gpxe/scsi.h>
8 #include <gpxe/process.h>
9 #include <gpxe/iscsi.h>
10
11 /** @file
12  *
13  * iSCSI protocol
14  *
15  */
16
17 static void iscsi_start_tx ( struct iscsi_session *iscsi );
18
19 /****************************************************************************
20  *
21  * iSCSI SCSI command issuing
22  *
23  */
24
25 /**
26  * Build iSCSI SCSI command BHS
27  *
28  * @v iscsi             iSCSI session
29  */
30 static void iscsi_start_command ( struct iscsi_session *iscsi ) {
31         struct iscsi_bhs_scsi_command *command = &iscsi->tx_bhs.scsi_command;
32
33         assert ( ! ( iscsi->command->data_in && iscsi->command->data_out ) );
34
35         /* Construct BHS and initiate transmission */
36         iscsi_start_tx ( iscsi );
37         command->opcode = ISCSI_OPCODE_SCSI_COMMAND;
38         command->flags = ( ISCSI_FLAG_FINAL |
39                            ISCSI_COMMAND_ATTR_SIMPLE );
40         if ( iscsi->command->data_in )
41                 command->flags |= ISCSI_COMMAND_FLAG_READ;
42         if ( iscsi->command->data_out )
43                 command->flags |= ISCSI_COMMAND_FLAG_WRITE;
44         ISCSI_SET_LENGTHS ( command->lengths, 0, iscsi->command->data_out_len);
45         command->lun = iscsi->lun;
46         command->itt = htonl ( iscsi->itt );
47         command->exp_len = htonl ( iscsi->command->data_in_len );
48         memcpy ( &command->cdb, &iscsi->command->cdb, sizeof ( command->cdb ));
49 }
50
51 /**
52  * Send iSCSI SCSI command data
53  *
54  * @v iscsi             iSCSI session
55  */
56 static void iscsi_tx_command ( struct iscsi_session *iscsi ) {
57         tcp_send ( &iscsi->tcp, iscsi->command->data_out + iscsi->tx_offset,
58                    iscsi->command->data_out_len - iscsi->tx_offset );
59 }
60
61 /**
62  * Receive data segment of an iSCSI data-in PDU
63  *
64  * @v iscsi             iSCSI session
65  * @v data              Received data
66  * @v len               Length of received data
67  * @v remaining         Data remaining after this data
68  * 
69  */
70 static void iscsi_rx_data_in ( struct iscsi_session *iscsi, void *data,
71                                size_t len, size_t remaining ) {
72         struct iscsi_bhs_data_in *data_in = &iscsi->rx_bhs.data_in;
73         unsigned long offset;
74
75         /* Copy data to data-in buffer */
76         offset = ntohl ( data_in->offset ) + iscsi->rx_offset;
77         assert ( iscsi->command != NULL );
78         assert ( iscsi->command->data_in != NULL );
79         assert ( ( offset + len ) <= iscsi->command->data_in_len );
80         memcpy ( ( iscsi->command->data_in + offset ), data, len );
81
82         /* If this is the end, flag as complete */
83         if ( ( data_in->flags & ISCSI_FLAG_FINAL ) && ( remaining == 0 ) )
84                 iscsi->status |= ISCSI_STATUS_DONE;
85 }
86
87 /****************************************************************************
88  *
89  * iSCSI login
90  *
91  */
92
93 /**
94  * Build iSCSI login request strings
95  *
96  * @v iscsi             iSCSI session
97  *
98  * These are the initial set of strings sent in the first login
99  * request PDU.
100  */
101 static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi,
102                                                void *data, size_t len ) {
103         return snprintf ( data, len,
104                           "InitiatorName=%s%c"
105                           "TargetName=%s%c"
106                           "MaxRecvDataSegmentLength=512%c"
107                           "SessionType=Normal%c"
108                           "DataDigest=None%c"
109                           "HeaderDigest=None%c",
110                           iscsi->initiator, 0, iscsi->target, 0,
111                           0, 0, 0, 0 );
112 }
113
114 /**
115  * Build iSCSI login request BHS
116  *
117  * @v iscsi             iSCSI session
118  * @v first             Login request is the first request of a session
119  */
120 static void iscsi_start_login ( struct iscsi_session *iscsi, int first ) {
121         struct iscsi_bhs_login_request *request = &iscsi->tx_bhs.login_request;
122         int len;
123
124         /* Construct BHS and initiate transmission */
125         iscsi_start_tx ( iscsi );
126         request->opcode = ( ISCSI_OPCODE_LOGIN_REQUEST |
127                             ISCSI_FLAG_IMMEDIATE );
128         request->flags = ( ISCSI_LOGIN_FLAG_TRANSITION |
129                            ISCSI_LOGIN_CSG_OPERATIONAL_NEGOTIATION |
130                            ISCSI_LOGIN_NSG_FULL_FEATURE_PHASE );
131         /* version_max and version_min left as zero */
132         if ( first ) {
133                 len = iscsi_build_login_request_strings ( iscsi, NULL, 0 );
134                 ISCSI_SET_LENGTHS ( request->lengths, 0, len );
135         }
136         request->isid_iana_en = htonl ( ISCSI_ISID_IANA |
137                                         IANA_EN_FEN_SYSTEMS );
138         /* isid_iana_qual left as zero */
139         request->tsih = htons ( iscsi->tsih );
140         /* itt left as zero */
141         /* cid left as zero */
142 }
143
144 /**
145  * Transmit data segment of an iSCSI login request PDU
146  *
147  * @v iscsi             iSCSI session
148  *
149  * For login requests, the data segment consists of the login strings.
150  */
151 static void iscsi_tx_login_request ( struct iscsi_session *iscsi ) {
152         int len;
153
154         len = iscsi_build_login_request_strings ( iscsi, tcp_buffer,
155                                                   tcp_buflen );
156         tcp_send ( &iscsi->tcp, tcp_buffer + iscsi->tx_offset,
157                    len - iscsi->tx_offset );
158 }
159
160 /**
161  * Receive data segment of an iSCSI login response PDU
162  *
163  * @v iscsi             iSCSI session
164  * @v data              Received data
165  * @v len               Length of received data
166  * @v remaining         Data remaining after this data
167  * 
168  */
169 static void iscsi_rx_login_response ( struct iscsi_session *iscsi,
170                                       void *data __unused,
171                                       size_t len __unused,
172                                       size_t remaining __unused ) {
173         struct iscsi_bhs_login_response *response
174                 = &iscsi->rx_bhs.login_response;
175
176         /* Check for fatal errors */
177         if ( response->status_class != 0 ) {
178                 printf ( "iSCSI login failure: class %02x detail %02x\n",
179                          response->status_class, response->status_detail );
180                 iscsi->status |= ( ISCSI_STATUS_DONE | ISCSI_STATUS_ERR );
181                 tcp_close ( &iscsi->tcp );
182                 return;
183         }
184
185         /* If server did not transition, send back another login
186          * request without any login strings.
187          */
188         if ( ! ( response->flags & ISCSI_LOGIN_FLAG_TRANSITION ) ) {
189                 iscsi_start_login ( iscsi, 0 );
190                 return;
191         }
192
193         /* Record TSIH for future reference */
194         iscsi->tsih = ntohl ( response->tsih );
195         
196         /* Send the SCSI command */
197         iscsi_start_command ( iscsi );
198 }
199
200 /****************************************************************************
201  *
202  * iSCSI to TCP interface
203  *
204  */
205
206 static inline struct iscsi_session *
207 tcp_to_iscsi ( struct tcp_connection *conn ) {
208         return container_of ( conn, struct iscsi_session, tcp );
209 }
210
211 /**
212  * Start up a new TX PDU
213  *
214  * @v iscsi             iSCSI session
215  *
216  * This initiates the process of sending a new PDU.  Only one PDU may
217  * be in transit at any one time.
218  */
219 static void iscsi_start_tx ( struct iscsi_session *iscsi ) {
220         assert ( iscsi->tx_state == ISCSI_TX_IDLE );
221         
222         /* Initialise TX BHS */
223         memset ( &iscsi->tx_bhs, 0, sizeof ( iscsi->tx_bhs ) );
224         iscsi->tx_bhs.common_request.cmdsn = htonl ( iscsi->cmdsn );
225         iscsi->tx_bhs.common_request.expstatsn = htonl ( iscsi->statsn + 1 );
226
227         /* Flag TX engine to start transmitting */
228         iscsi->tx_state = ISCSI_TX_BHS;
229         iscsi->tx_offset = 0;
230 }
231
232 /**
233  * Transmit data segment of an iSCSI PDU
234  *
235  * @v iscsi             iSCSI session
236  * 
237  * Handle transmission of part of a PDU data segment.  iscsi::tx_bhs
238  * will be valid when this is called.
239  */
240 static void iscsi_tx_data ( struct iscsi_session *iscsi ) {
241         struct iscsi_bhs_common *common = &iscsi->tx_bhs.common;
242
243         switch ( common->opcode & ISCSI_OPCODE_MASK ) {
244         case ISCSI_OPCODE_SCSI_COMMAND:
245                 iscsi_tx_command ( iscsi );
246                 break;
247         case ISCSI_OPCODE_LOGIN_REQUEST:
248                 iscsi_tx_login_request ( iscsi );
249                 break;
250         default:
251                 assert ( 0 );
252                 break;
253         }
254 }
255
256 /**
257  * Handle TCP ACKs
258  *
259  * @v iscsi             iSCSI session
260  * 
261  * Updates iscsi->tx_offset and, if applicable, transitions to the
262  * next TX state.
263  */
264 static void iscsi_acked ( struct tcp_connection *conn, size_t len ) {
265         struct iscsi_session *iscsi = tcp_to_iscsi ( conn );
266         struct iscsi_bhs_common *common = &iscsi->tx_bhs.common;
267         size_t max_tx_offset;
268         enum iscsi_tx_state next_state;
269         
270         iscsi->tx_offset += len;
271         while ( 1 ) {
272                 switch ( iscsi->tx_state ) {
273                 case ISCSI_TX_BHS:
274                         max_tx_offset = sizeof ( iscsi->tx_bhs );
275                         next_state = ISCSI_TX_AHS;
276                         break;
277                 case ISCSI_TX_AHS:
278                         max_tx_offset = 4 * ISCSI_AHS_LEN ( common->lengths );
279                         next_state = ISCSI_TX_DATA;
280                         break;
281                 case ISCSI_TX_DATA:
282                         max_tx_offset = ISCSI_DATA_LEN ( common->lengths );
283                         next_state = ISCSI_TX_DATA_PADDING;
284                         break;
285                 case ISCSI_TX_DATA_PADDING:
286                         max_tx_offset = ISCSI_DATA_PAD_LEN ( common->lengths );
287                         next_state = ISCSI_TX_IDLE;
288                         break;
289                 case ISCSI_TX_IDLE:
290                         return;
291                 default:
292                         assert ( 0 );
293                         return;
294                 }
295                 assert ( iscsi->tx_offset <= max_tx_offset );
296
297                 /* If the whole of the current portion has not yet
298                  * been acked, stay in this state for now.
299                  */
300                 if ( iscsi->tx_offset != max_tx_offset )
301                         return;
302                 
303                 iscsi->tx_state = next_state;
304                 iscsi->tx_offset = 0;
305         }
306 }
307
308 /**
309  * Transmit iSCSI PDU
310  *
311  * @v iscsi             iSCSI session
312  * 
313  * Constructs data to be sent for the current TX state
314  */
315 static void iscsi_senddata ( struct tcp_connection *conn ) {
316         struct iscsi_session *iscsi = tcp_to_iscsi ( conn );
317         struct iscsi_bhs_common *common = &iscsi->tx_bhs.common;
318         static const char pad[] = { '\0', '\0', '\0' };
319
320         switch ( iscsi->tx_state ) {
321         case ISCSI_TX_IDLE:
322                 /* Nothing to send */
323                 break;
324         case ISCSI_TX_BHS:
325                 tcp_send ( conn, &iscsi->tx_bhs.bytes[iscsi->tx_offset],
326                            ( sizeof ( iscsi->tx_bhs ) - iscsi->tx_offset ) );
327                 break;
328         case ISCSI_TX_AHS:
329                 /* We don't yet have an AHS transmission mechanism */
330                 assert ( 0 );
331                 break;
332         case ISCSI_TX_DATA:
333                 iscsi_tx_data ( iscsi );
334                 break;
335         case ISCSI_TX_DATA_PADDING:
336                 tcp_send ( conn, pad, ( ISCSI_DATA_PAD_LEN ( common->lengths )
337                                         - iscsi->tx_offset ) );
338                 break;
339         default:
340                 assert ( 0 );
341                 break;
342         }
343 }
344
345 /**
346  * Receive data segment of an iSCSI PDU
347  *
348  * @v iscsi             iSCSI session
349  * @v data              Received data
350  * @v len               Length of received data
351  * @v remaining         Data remaining after this data
352  *
353  * Handle processing of part of a PDU data segment.  iscsi::rx_bhs
354  * will be valid when this is called.
355  */
356 static void iscsi_rx_data ( struct iscsi_session *iscsi, void *data,
357                             size_t len, size_t remaining ) {
358         struct iscsi_bhs_common_response *response
359                 = &iscsi->rx_bhs.common_response;
360
361         /* Update cmdsn and statsn */
362         iscsi->cmdsn = ntohl ( response->expcmdsn );
363         iscsi->statsn = ntohl ( response->statsn );
364
365         /* Increment itt when we receive a final response */
366         if ( response->flags & ISCSI_FLAG_FINAL )
367                 iscsi->itt++;
368
369         switch ( response->opcode & ISCSI_OPCODE_MASK ) {
370         case ISCSI_OPCODE_LOGIN_RESPONSE:
371                 iscsi_rx_login_response ( iscsi, data, len, remaining );
372                 break;
373         case ISCSI_OPCODE_DATA_IN:
374                 iscsi_rx_data_in ( iscsi, data, len, remaining );
375                 break;
376         default:
377                 printf ( "Unknown iSCSI opcode %02x\n", response->opcode );
378                 iscsi->status |= ( ISCSI_STATUS_DONE | ISCSI_STATUS_ERR );
379                 break;
380         }
381 }
382
383 /**
384  * Discard portion of an iSCSI PDU.
385  *
386  * @v iscsi             iSCSI session
387  * @v data              Received data
388  * @v len               Length of received data
389  * @v remaining         Data remaining after this data
390  *
391  * This discards data from a portion of a received PDU.
392  */
393 static void iscsi_rx_discard ( struct iscsi_session *iscsi __unused,
394                                void *data __unused, size_t len __unused,
395                                size_t remaining __unused ) {
396         /* Do nothing */
397 }
398
399 /**
400  * Receive basic header segment of an iSCSI PDU
401  *
402  * @v iscsi             iSCSI session
403  * @v data              Received data
404  * @v len               Length of received data
405  * @v remaining         Data remaining after this data
406  *
407  * This fills in iscsi::rx_bhs with the data from the BHS portion of
408  * the received PDU.
409  */
410 static void iscsi_rx_bhs ( struct iscsi_session *iscsi, void *data,
411                            size_t len, size_t remaining __unused ) {
412         memcpy ( &iscsi->rx_bhs.bytes[iscsi->rx_offset], data, len );
413 }
414
415 /**
416  * Receive new data
417  *
418  * @v tcp               TCP connection
419  * @v data              Received data
420  * @v len               Length of received data
421  *
422  * This handles received PDUs.  The receive strategy is to fill in
423  * iscsi::rx_bhs with the contents of the BHS portion of the PDU,
424  * throw away any AHS portion, and then process each part of the data
425  * portion as it arrives.  The data processing routine therefore
426  * always has a full copy of the BHS available, even for portions of
427  * the data in different packets to the BHS.
428  */
429 static void iscsi_newdata ( struct tcp_connection *conn, void *data,
430                             size_t len ) {
431         struct iscsi_session *iscsi = tcp_to_iscsi ( conn );
432         struct iscsi_bhs_common *common = &iscsi->rx_bhs.common;
433         void ( *process ) ( struct iscsi_session *iscsi, void *data,
434                             size_t len, size_t remaining );
435         size_t max_rx_offset;
436         enum iscsi_rx_state next_state;
437         size_t frag_len;
438         size_t remaining;
439
440         while ( 1 ) {
441                 switch ( iscsi->rx_state ) {
442                 case ISCSI_RX_BHS:
443                         process = iscsi_rx_bhs;
444                         max_rx_offset = sizeof ( iscsi->rx_bhs );
445                         next_state = ISCSI_RX_AHS;                      
446                         break;
447                 case ISCSI_RX_AHS:
448                         process = iscsi_rx_discard;
449                         max_rx_offset = 4 * ISCSI_AHS_LEN ( common->lengths );
450                         next_state = ISCSI_RX_DATA;
451                         break;
452                 case ISCSI_RX_DATA:
453                         process = iscsi_rx_data;
454                         max_rx_offset = ISCSI_DATA_LEN ( common->lengths );
455                         next_state = ISCSI_RX_DATA_PADDING;
456                         break;
457                 case ISCSI_RX_DATA_PADDING:
458                         process = iscsi_rx_discard;
459                         max_rx_offset = ISCSI_DATA_PAD_LEN ( common->lengths );
460                         next_state = ISCSI_RX_BHS;
461                         break;
462                 default:
463                         assert ( 0 );
464                         return;
465                 }
466
467                 frag_len = max_rx_offset - iscsi->rx_offset;
468                 if ( frag_len > len )
469                         frag_len = len;
470                 remaining = max_rx_offset - iscsi->rx_offset - frag_len;
471                 process ( iscsi, data, frag_len, remaining );
472
473                 iscsi->rx_offset += frag_len;
474                 data += frag_len;
475                 len -= frag_len;
476
477                 /* If all the data for this state has not yet been
478                  * received, stay in this state for now.
479                  */
480                 if ( iscsi->rx_offset != max_rx_offset )
481                         return;
482
483                 iscsi->rx_state = next_state;
484                 iscsi->rx_offset = 0;
485         }
486 }
487
488 /**
489  * Handle TCP connection closure
490  *
491  * @v conn              TCP connection
492  * @v status            Error code, if any
493  *
494  */
495 static void iscsi_closed ( struct tcp_connection *conn, int status __unused ) {
496         struct iscsi_session *iscsi = tcp_to_iscsi ( conn );
497
498         /* Clear connected flag */
499         iscsi->status &= ~ISCSI_STATUS_CONNECTED;
500
501         /* Retry connection if within the retry limit, otherwise fail */
502         if ( ++iscsi->retry_count <= ISCSI_MAX_RETRIES ) {
503                 tcp_connect ( conn );
504         } else {
505                 iscsi->status |= ( ISCSI_STATUS_DONE | ISCSI_STATUS_ERR );
506         }
507 }
508
509 /**
510  * Handle TCP connection opening
511  *
512  * @v conn              TCP connection
513  *
514  */
515 static void iscsi_connected ( struct tcp_connection *conn ) {
516         struct iscsi_session *iscsi = tcp_to_iscsi ( conn );
517
518         /* Set connected flag and reset retry count */
519         iscsi->status |= ISCSI_STATUS_CONNECTED;
520         iscsi->retry_count = 0;
521
522         /* Prepare to receive PDUs. */
523         iscsi->rx_state = ISCSI_RX_BHS;
524         iscsi->rx_offset = 0;
525
526         /* Start logging in */
527         iscsi_start_login ( iscsi, 1 );
528 }
529
530 /** iSCSI TCP operations */
531 static struct tcp_operations iscsi_tcp_operations = {
532         .closed         = iscsi_closed,
533         .connected      = iscsi_connected,
534         .acked          = iscsi_acked,
535         .newdata        = iscsi_newdata,
536         .senddata       = iscsi_senddata,
537 };
538
539 /**
540  * Issue SCSI command via iSCSI session
541  *
542  * @v iscsi             iSCSI session
543  * @v command           SCSI command
544  * @ret rc              Return status code
545  */
546 static int iscsi_command ( struct iscsi_session *iscsi,
547                            struct scsi_command *command ) {
548         iscsi->command = command;
549         iscsi->status &= ~( ISCSI_STATUS_DONE | ISCSI_STATUS_ERR );
550
551         if ( iscsi->status & ISCSI_STATUS_CONNECTED ) {
552                 iscsi_start_command ( iscsi );
553         } else {
554                 iscsi->tcp.tcp_op = &iscsi_tcp_operations;
555                 tcp_connect ( &iscsi->tcp );
556         }
557
558         while ( ! ( iscsi->status & ISCSI_STATUS_DONE ) ) {
559                 step();
560         }
561
562         iscsi->command = NULL;
563
564         return ( ( iscsi->status & ISCSI_STATUS_ERR ) ? -EIO : 0 );     
565 }
566
567 /**
568  * Issue SCSI command via iSCSI device
569  *
570  * @v scsi              SCSI device
571  * @v command           SCSI command
572  * @ret rc              Return status code
573  */
574 static int iscsi_scsi_command ( struct scsi_device *scsi,
575                                 struct scsi_command *command ) {
576         struct iscsi_device *iscsidev
577                 = container_of ( scsi, struct iscsi_device, scsi );
578
579         return iscsi_command ( &iscsidev->iscsi, command );
580 }
581
582 /**
583  * Initialise iSCSI device
584  *
585  * @v iscsidev          iSCSI device
586  */
587 int init_iscsidev ( struct iscsi_device *iscsidev ) {
588         iscsidev->scsi.command = iscsi_scsi_command;
589         iscsidev->scsi.lun = iscsidev->iscsi.lun;
590         return init_scsidev ( &iscsidev->scsi );
591 }