6 #include <gpxe/iscsi.h>
14 /****************************************************************************
21 * Start up a new TX PDU
23 * @v iscsi iSCSI session
25 * This initiates the process of sending a new PDU. Only one PDU may
26 * be in transit at any one time.
28 static void iscsi_start_tx ( struct iscsi_session *iscsi ) {
29 assert ( iscsi->tx_state == ISCSI_TX_IDLE );
30 iscsi->tx_state = ISCSI_TX_BHS;
35 * Mark session as failed
37 * @v iscsi iSCSI session
39 * This marks the session as permanently failed. The session will not
40 * be automatically logged back in.
42 static void iscsi_fail ( struct iscsi_session *iscsi ) {
43 iscsi->state = ISCSI_STATE_FAILED;
44 tcp_close ( &iscsi->tcp );
47 /****************************************************************************
49 * iSCSI SCSI command issuing
54 * Start up a block read
56 * @v iscsi iSCSI session
59 static void iscsi_start_read_block ( struct iscsi_session *iscsi ) {
60 struct iscsi_bhs_scsi_command *command = &iscsi->tx_bhs.scsi_command;
61 struct scsi_cdb_read_10 *read = &command->cdb.read_10;
63 assert ( iscsi->block_size != 0 );
64 assert ( iscsi->block_count != 0 );
65 assert ( iscsi->block_read_callback != NULL );
68 memset ( command, 0, sizeof ( *command ) );
69 command->opcode = ISCSI_OPCODE_SCSI_COMMAND;
70 command->flags = ( ISCSI_FLAG_FINAL |
71 ISCSI_COMMAND_FLAG_READ |
72 ISCSI_COMMAND_ATTR_SIMPLE );
73 /* lengths left as zero */
74 /* lun left as zero, on the assumption that no-one uses LUNs > 0 */
75 command->itt = htonl ( iscsi->itt );
76 command->exp_len = htonl ( iscsi->block_count * iscsi->block_size );
77 command->cmdsn = htonl ( iscsi->cmdsn );
78 command->expstatsn = htonl ( iscsi->statsn + 1 );
79 read->opcode = SCSI_OPCODE_READ_10;
80 read->lba = htonl ( iscsi->block_start );
81 read->len = htons ( iscsi->block_count );
83 iscsi->state = ISCSI_STATE_READING_DATA;
84 iscsi_start_tx ( iscsi );
88 * Receive data segment of an iSCSI data-in PDU
90 * @v iscsi iSCSI session
91 * @v data Received data
92 * @v len Length of received data
93 * @v remaining Data remaining after this data
96 static void iscsi_rx_data_in ( struct iscsi_session *iscsi, void *data,
97 size_t len, size_t remaining ) {
98 struct iscsi_bhs_data_in *data_in = &iscsi->rx_bhs.data_in;
101 /* Update cmdsn and statsn */
102 iscsi->cmdsn = ntohl ( data_in->expcmdsn );
103 iscsi->statsn = ntohl ( data_in->statsn );
105 /* Process data via callback */
106 offset = ntohl ( data_in->offset ) + iscsi->rx_offset;
107 iscsi->block_read_callback ( iscsi->block_read_private,
110 /* If this is the end, mark state as idle */
111 if ( ( data_in->flags & ISCSI_FLAG_FINAL ) && ( remaining == 0 ) )
112 iscsi->state = ISCSI_STATE_IDLE;
115 /****************************************************************************
122 * Build iSCSI login request strings
124 * @v iscsi iSCSI session
126 * These are the initial set of strings sent in the first login
129 static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi,
130 void *data, size_t len ) {
131 return snprintf ( data, len,
132 "InitiatorName=%s:initiator%c"
134 "MaxRecvDataSegmentLength=512%c"
135 "SessionType=Normal%c"
137 "HeaderDigest=None%c",
138 iscsi->initiator, 0, iscsi->target, 0,
143 * Transmit data segment of an iSCSI login request PDU
145 * @v iscsi iSCSI session
147 * For login requests, the data segment consists of the login strings.
149 static void iscsi_tx_login_request ( struct iscsi_session *iscsi ) {
152 len = iscsi_build_login_request_strings ( iscsi, tcp_buffer,
154 tcp_send ( &iscsi->tcp, tcp_buffer + iscsi->tx_offset,
155 len - iscsi->tx_offset );
159 * Start up a login request
161 * @v iscsi iSCSI session
164 static void iscsi_start_login ( struct iscsi_session *iscsi ) {
165 struct iscsi_bhs_login_request *request = &iscsi->tx_bhs.login_request;
168 /* Construct login request BHS */
169 memset ( request, 0, sizeof ( *request ) );
170 request->opcode = ( ISCSI_OPCODE_LOGIN_REQUEST |
171 ISCSI_FLAG_IMMEDIATE );
172 request->flags = ( ISCSI_LOGIN_FLAG_TRANSITION |
173 ISCSI_LOGIN_CSG_OPERATIONAL_NEGOTIATION |
174 ISCSI_LOGIN_NSG_FULL_FEATURE_PHASE );
175 /* version_max and version_min left as zero */
176 len = iscsi_build_login_request_strings ( iscsi, NULL, 0 );
177 ISCSI_SET_LENGTHS ( request->lengths, 0, len );
178 request->isid_iana_en = htonl ( ISCSI_ISID_IANA |
179 IANA_EN_FEN_SYSTEMS );
180 /* isid_iana_qual left as zero */
181 request->tsih = htons ( iscsi->tsih );
182 /* itt left as zero */
183 /* cid left as zero */
184 request->cmdsn = htonl ( iscsi->cmdsn );
185 request->expstatsn = htonl ( iscsi->statsn + 1 );
187 iscsi->state = ISCSI_STATE_LOGGING_IN;
188 iscsi_start_tx ( iscsi );
192 * Receive data segment of an iSCSI login response PDU
194 * @v iscsi iSCSI session
195 * @v data Received data
196 * @v len Length of received data
197 * @v remaining Data remaining after this data
200 static void iscsi_rx_login_response ( struct iscsi_session *iscsi,
203 size_t remaining __unused ) {
204 struct iscsi_bhs_login_request *request = &iscsi->tx_bhs.login_request;
205 struct iscsi_bhs_login_response *response
206 = &iscsi->rx_bhs.login_response;
209 if ( iscsi->state != ISCSI_STATE_LOGGING_IN ) {
210 printf ( "Spurious iSCSI login response\n" );
211 iscsi_fail ( iscsi );
215 /* Check for fatal errors */
216 if ( response->status_class != 0 ) {
217 printf ( "iSCSI login failure: class %02x detail %02x\n",
218 response->status_class, response->status_detail );
219 iscsi_fail ( iscsi );
223 /* Update cmdsn and statsn */
224 iscsi->cmdsn = ntohl ( response->expcmdsn );
225 iscsi->statsn = ntohl ( response->statsn );
227 /* If server did not transition, we send it another login
228 * request with empty strings.
230 if ( ! ( response->flags & ISCSI_LOGIN_FLAG_TRANSITION ) ) {
231 ISCSI_SET_LENGTHS ( request->lengths, 0, 0 );
232 iscsi_start_tx ( iscsi );
236 /* Record TSIH for future reference */
237 iscsi->tsih = ntohl ( response->tsih );
239 /* Start reading data */
240 iscsi_start_read_block ( iscsi );
243 /****************************************************************************
245 * iSCSI to TCP interface
249 static inline struct iscsi_session *
250 tcp_to_iscsi ( struct tcp_connection *conn ) {
251 return container_of ( conn, struct iscsi_session, tcp );
254 static void iscsi_aborted ( struct tcp_connection *conn ) {
255 struct iscsi_session *iscsi = tcp_to_iscsi ( conn );
259 static void iscsi_timedout ( struct tcp_connection *conn ) {
260 struct iscsi_session *iscsi = tcp_to_iscsi ( conn );
264 static void iscsi_closed ( struct tcp_connection *conn ) {
265 struct iscsi_session *iscsi = tcp_to_iscsi ( conn );
269 static void iscsi_connected ( struct tcp_connection *conn ) {
270 struct iscsi_session *iscsi = tcp_to_iscsi ( conn );
272 /* Prepare to receive PDUs. */
273 iscsi->rx_state = ISCSI_RX_BHS;
274 iscsi->rx_offset = 0;
276 /* TX state should already have been set up */
277 assert ( iscsi->tx_state != ISCSI_TX_IDLE );
278 assert ( iscsi->tx_offset == 0 );
282 * Transmit data segment of an iSCSI PDU
284 * @v iscsi iSCSI session
286 * Handle transmission of part of a PDU data segment. iscsi::tx_bhs
287 * will be valid when this is called.
289 static void iscsi_tx_data ( struct iscsi_session *iscsi ) {
290 struct iscsi_bhs_common *common = &iscsi->tx_bhs.common;
292 switch ( common->opcode & ISCSI_OPCODE_MASK ) {
293 case ISCSI_OPCODE_LOGIN_REQUEST:
294 iscsi_tx_login_request ( iscsi );
305 * @v iscsi iSCSI session
307 * Updates iscsi->tx_offset and, if applicable, transitions to the
310 static void iscsi_acked ( struct tcp_connection *conn, size_t len ) {
311 struct iscsi_session *iscsi = tcp_to_iscsi ( conn );
312 struct iscsi_bhs_common *common = &iscsi->tx_bhs.common;
313 size_t max_tx_offset;
314 enum iscsi_tx_state next_state;
316 iscsi->tx_offset += len;
318 switch ( iscsi->tx_state ) {
320 max_tx_offset = sizeof ( iscsi->tx_bhs );
321 next_state = ISCSI_TX_AHS;
324 max_tx_offset = 4 * ISCSI_AHS_LEN ( common->lengths );
325 next_state = ISCSI_TX_DATA;
328 max_tx_offset = ISCSI_DATA_LEN ( common->lengths );
329 next_state = ISCSI_TX_DATA_PADDING;
331 case ISCSI_TX_DATA_PADDING:
332 max_tx_offset = ISCSI_DATA_PAD_LEN ( common->lengths );
333 next_state = ISCSI_TX_IDLE;
341 assert ( iscsi->tx_offset <= max_tx_offset );
343 /* If the whole of the current portion has not yet
344 * been acked, stay in this state for now.
346 if ( iscsi->tx_offset != max_tx_offset )
349 iscsi->tx_state = next_state;
350 iscsi->tx_offset = 0;
357 * @v iscsi iSCSI session
359 * Constructs data to be sent for the current TX state
361 static void iscsi_senddata ( struct tcp_connection *conn ) {
362 struct iscsi_session *iscsi = tcp_to_iscsi ( conn );
363 struct iscsi_bhs_common *common = &iscsi->tx_bhs.common;
364 static const char pad[] = { '\0', '\0', '\0' };
366 switch ( iscsi->tx_state ) {
371 tcp_send ( conn, &iscsi->tx_bhs.bytes[iscsi->tx_offset],
372 ( sizeof ( iscsi->tx_bhs ) - iscsi->tx_offset ) );
375 /* We don't yet have an AHS transmission mechanism */
379 iscsi_tx_data ( iscsi );
381 case ISCSI_TX_DATA_PADDING:
382 tcp_send ( conn, pad, ( ISCSI_DATA_PAD_LEN ( common->lengths )
383 - iscsi->tx_offset ) );
392 * Receive data segment of an iSCSI PDU
394 * @v iscsi iSCSI session
395 * @v data Received data
396 * @v len Length of received data
397 * @v remaining Data remaining after this data
399 * Handle processing of part of a PDU data segment. iscsi::rx_bhs
400 * will be valid when this is called.
402 static void iscsi_rx_data ( struct iscsi_session *iscsi, void *data,
403 size_t len, size_t remaining ) {
404 struct iscsi_bhs_common *common = &iscsi->rx_bhs.common;
406 switch ( common->opcode & ISCSI_OPCODE_MASK ) {
407 case ISCSI_OPCODE_LOGIN_RESPONSE:
408 iscsi_rx_login_response ( iscsi, data, len, remaining );
410 case ISCSI_OPCODE_DATA_IN:
411 iscsi_rx_data_in ( iscsi, data, len, remaining );
414 printf ( "Unknown iSCSI opcode %02x\n", common->opcode );
420 * Discard portion of an iSCSI PDU.
422 * @v iscsi iSCSI session
423 * @v data Received data
424 * @v len Length of received data
425 * @v remaining Data remaining after this data
427 * This discards data from a portion of a received PDU.
429 static void iscsi_rx_discard ( struct iscsi_session *iscsi __unused,
430 void *data __unused, size_t len __unused,
431 size_t remaining __unused ) {
436 * Receive basic header segment of an iSCSI PDU
438 * @v iscsi iSCSI session
439 * @v data Received data
440 * @v len Length of received data
441 * @v remaining Data remaining after this data
443 * This fills in iscsi::rx_bhs with the data from the BHS portion of
446 static void iscsi_rx_bhs ( struct iscsi_session *iscsi, void *data,
447 size_t len, size_t remaining __unused ) {
448 memcpy ( &iscsi->rx_bhs.bytes[iscsi->rx_offset], data, len );
454 * @v tcp TCP connection
455 * @v data Received data
456 * @v len Length of received data
458 * This handles received PDUs. The receive strategy is to fill in
459 * iscsi::rx_bhs with the contents of the BHS portion of the PDU,
460 * throw away any AHS portion, and then process each part of the data
461 * portion as it arrives. The data processing routine therefore
462 * always has a full copy of the BHS available, even for portions of
463 * the data in different packets to the BHS.
465 static void iscsi_newdata ( struct tcp_connection *conn, void *data,
467 struct iscsi_session *iscsi = tcp_to_iscsi ( conn );
468 struct iscsi_bhs_common *common = &iscsi->rx_bhs.common;
469 void ( *process ) ( struct iscsi_session *iscsi, void *data,
470 size_t len, size_t remaining );
471 size_t max_rx_offset;
472 enum iscsi_rx_state next_state;
477 switch ( iscsi->rx_state ) {
479 process = iscsi_rx_bhs;
480 max_rx_offset = sizeof ( iscsi->rx_bhs );
481 next_state = ISCSI_RX_AHS;
484 process = iscsi_rx_discard;
485 max_rx_offset = 4 * ISCSI_AHS_LEN ( common->lengths );
486 next_state = ISCSI_RX_DATA;
489 process = iscsi_rx_data;
490 max_rx_offset = ISCSI_DATA_LEN ( common->lengths );
491 next_state = ISCSI_RX_DATA_PADDING;
493 case ISCSI_RX_DATA_PADDING:
494 process = iscsi_rx_discard;
495 max_rx_offset = ISCSI_DATA_PAD_LEN ( common->lengths );
496 next_state = ISCSI_RX_BHS;
503 frag_len = max_rx_offset - iscsi->rx_offset;
504 if ( frag_len > len )
506 remaining = max_rx_offset - iscsi->rx_offset - frag_len;
507 process ( iscsi, data, frag_len, remaining );
509 iscsi->rx_offset += frag_len;
513 /* If all the data for this state has not yet been
514 * received, stay in this state for now.
516 if ( iscsi->rx_offset != max_rx_offset )
519 iscsi->rx_state = next_state;
520 iscsi->rx_offset = 0;
524 /** iSCSI TCP operations */
525 static struct tcp_operations iscsi_tcp_operations = {
526 .aborted = iscsi_aborted,
527 .timedout = iscsi_timedout,
528 .closed = iscsi_closed,
529 .connected = iscsi_connected,
530 .acked = iscsi_acked,
531 .newdata = iscsi_newdata,
532 .senddata = iscsi_senddata,
538 * @v iscsi iSCSI session
541 void iscsi_wakeup ( struct iscsi_session *iscsi ) {
542 iscsi->tcp.tcp_op = &iscsi_tcp_operations;
544 switch ( iscsi->state ) {
545 case ISCSI_STATE_NOT_CONNECTED:
546 case ISCSI_STATE_FAILED:
547 if ( tcp_connect ( &iscsi->tcp ) != 0 )
548 iscsi_fail ( iscsi );
549 iscsi_start_login ( iscsi );
551 case ISCSI_STATE_IDLE:
552 iscsi_start_read_block ( iscsi );
555 /* Stay in same state */