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