|
@@ -33,6 +33,8 @@
|
33
|
33
|
*/
|
34
|
34
|
|
35
|
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 );
|
36
|
38
|
|
37
|
39
|
/****************************************************************************
|
38
|
40
|
*
|
|
@@ -44,6 +46,11 @@ static void iscsi_start_tx ( struct iscsi_session *iscsi );
|
44
|
46
|
* Build iSCSI SCSI command BHS
|
45
|
47
|
*
|
46
|
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.
|
47
|
54
|
*/
|
48
|
55
|
static void iscsi_start_command ( struct iscsi_session *iscsi ) {
|
49
|
56
|
struct iscsi_bhs_scsi_command *command = &iscsi->tx_bhs.scsi_command;
|
|
@@ -59,21 +66,49 @@ static void iscsi_start_command ( struct iscsi_session *iscsi ) {
|
59
|
66
|
command->flags |= ISCSI_COMMAND_FLAG_READ;
|
60
|
67
|
if ( iscsi->command->data_out )
|
61
|
68
|
command->flags |= ISCSI_COMMAND_FLAG_WRITE;
|
62
|
|
- ISCSI_SET_LENGTHS ( command->lengths, 0, iscsi->command->data_out_len);
|
|
69
|
+ /* lengths left as zero */
|
63
|
70
|
command->lun = iscsi->lun;
|
64
|
|
- command->itt = htonl ( iscsi->itt );
|
65
|
|
- command->exp_len = htonl ( iscsi->command->data_in_len );
|
|
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 );
|
66
|
76
|
memcpy ( &command->cdb, &iscsi->command->cdb, sizeof ( command->cdb ));
|
67
|
77
|
}
|
68
|
78
|
|
69
|
79
|
/**
|
70
|
|
- * Send iSCSI SCSI command data
|
|
80
|
+ * Receive data segment of an iSCSI SCSI response PDU
|
71
|
81
|
*
|
72
|
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
|
+ *
|
73
|
87
|
*/
|
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 );
|
|
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;
|
77
|
112
|
}
|
78
|
113
|
|
79
|
114
|
/**
|
|
@@ -86,7 +121,7 @@ static void iscsi_tx_command ( struct iscsi_session *iscsi ) {
|
86
|
121
|
*
|
87
|
122
|
*/
|
88
|
123
|
static void iscsi_rx_data_in ( struct iscsi_session *iscsi, void *data,
|
89
|
|
- size_t len, size_t remaining ) {
|
|
124
|
+ size_t len, size_t remaining __unused ) {
|
90
|
125
|
struct iscsi_bhs_data_in *data_in = &iscsi->rx_bhs.data_in;
|
91
|
126
|
unsigned long offset;
|
92
|
127
|
|
|
@@ -97,9 +132,109 @@ static void iscsi_rx_data_in ( struct iscsi_session *iscsi, void *data,
|
97
|
132
|
assert ( ( offset + len ) <= iscsi->command->data_in_len );
|
98
|
133
|
memcpy ( ( iscsi->command->data_in + offset ), data, len );
|
99
|
134
|
|
|
135
|
+ /* Record SCSI status, if present */
|
|
136
|
+ if ( data_in->flags & ISCSI_DATA_FLAG_STATUS )
|
|
137
|
+ iscsi->command->status = data_in->status;
|
|
138
|
+
|
100
|
139
|
/* If this is the end, flag as complete */
|
101
|
|
- if ( ( data_in->flags & ISCSI_FLAG_FINAL ) && ( remaining == 0 ) )
|
|
140
|
+ if ( ( offset + len ) == iscsi->command->data_in_len ) {
|
|
141
|
+ assert ( data_in->flags & ISCSI_FLAG_FINAL );
|
|
142
|
+ assert ( remaining == 0 );
|
102
|
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 );
|
103
|
238
|
}
|
104
|
239
|
|
105
|
240
|
/****************************************************************************
|
|
@@ -121,11 +256,11 @@ static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi,
|
121
|
256
|
return snprintf ( data, len,
|
122
|
257
|
"InitiatorName=%s%c"
|
123
|
258
|
"TargetName=%s%c"
|
124
|
|
- "MaxRecvDataSegmentLength=512%c"
|
125
|
259
|
"SessionType=Normal%c"
|
126
|
260
|
"DataDigest=None%c"
|
127
|
261
|
"HeaderDigest=None%c"
|
128
|
|
- "ErrorRecoveryLevel=0%c",
|
|
262
|
+ "DefaultTime2Wait=0%c"
|
|
263
|
+ "DefaultTime2Retain=0%c",
|
129
|
264
|
iscsi->initiator, 0, iscsi->target, 0,
|
130
|
265
|
0, 0, 0, 0, 0 );
|
131
|
266
|
}
|
|
@@ -134,7 +269,7 @@ static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi,
|
134
|
269
|
* Build iSCSI login request BHS
|
135
|
270
|
*
|
136
|
271
|
* @v iscsi iSCSI session
|
137
|
|
- * @v first Login request is the first request of a session
|
|
272
|
+ * @v first Login request is the first in a sequence
|
138
|
273
|
*/
|
139
|
274
|
static void iscsi_start_login ( struct iscsi_session *iscsi, int first ) {
|
140
|
275
|
struct iscsi_bhs_login_request *request = &iscsi->tx_bhs.login_request;
|
|
@@ -156,8 +291,12 @@ static void iscsi_start_login ( struct iscsi_session *iscsi, int first ) {
|
156
|
291
|
IANA_EN_FEN_SYSTEMS );
|
157
|
292
|
/* isid_iana_qual left as zero */
|
158
|
293
|
request->tsih = htons ( iscsi->tsih );
|
159
|
|
- /* itt left as zero */
|
|
294
|
+ if ( first )
|
|
295
|
+ iscsi->itt++;
|
|
296
|
+ request->itt = htonl ( iscsi->itt );
|
160
|
297
|
/* cid left as zero */
|
|
298
|
+ request->cmdsn = htonl ( iscsi->cmdsn );
|
|
299
|
+ request->expstatsn = htonl ( iscsi->statsn + 1 );
|
161
|
300
|
}
|
162
|
301
|
|
163
|
302
|
/**
|
|
@@ -240,8 +379,6 @@ static void iscsi_start_tx ( struct iscsi_session *iscsi ) {
|
240
|
379
|
|
241
|
380
|
/* Initialise TX BHS */
|
242
|
381
|
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
|
382
|
|
246
|
383
|
/* Flag TX engine to start transmitting */
|
247
|
384
|
iscsi->tx_state = ISCSI_TX_BHS;
|
|
@@ -260,8 +397,8 @@ static void iscsi_tx_data ( struct iscsi_session *iscsi ) {
|
260
|
397
|
struct iscsi_bhs_common *common = &iscsi->tx_bhs.common;
|
261
|
398
|
|
262
|
399
|
switch ( common->opcode & ISCSI_OPCODE_MASK ) {
|
263
|
|
- case ISCSI_OPCODE_SCSI_COMMAND:
|
264
|
|
- iscsi_tx_command ( iscsi );
|
|
400
|
+ case ISCSI_OPCODE_DATA_OUT:
|
|
401
|
+ iscsi_tx_data_out ( iscsi );
|
265
|
402
|
break;
|
266
|
403
|
case ISCSI_OPCODE_LOGIN_REQUEST:
|
267
|
404
|
iscsi_tx_login_request ( iscsi );
|
|
@@ -272,6 +409,27 @@ static void iscsi_tx_data ( struct iscsi_session *iscsi ) {
|
272
|
409
|
}
|
273
|
410
|
}
|
274
|
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
|
+
|
275
|
433
|
/**
|
276
|
434
|
* Handle TCP ACKs
|
277
|
435
|
*
|
|
@@ -318,9 +476,14 @@ static void iscsi_acked ( struct tcp_connection *conn, size_t len ) {
|
318
|
476
|
*/
|
319
|
477
|
if ( iscsi->tx_offset != max_tx_offset )
|
320
|
478
|
return;
|
321
|
|
-
|
|
479
|
+
|
|
480
|
+ /* Move to next state. Call iscsi_tx_done() when PDU
|
|
481
|
+ * transmission is complete.
|
|
482
|
+ */
|
322
|
483
|
iscsi->tx_state = next_state;
|
323
|
484
|
iscsi->tx_offset = 0;
|
|
485
|
+ if ( next_state == ISCSI_TX_IDLE )
|
|
486
|
+ iscsi_tx_done ( iscsi );
|
324
|
487
|
}
|
325
|
488
|
}
|
326
|
489
|
|
|
@@ -381,17 +544,19 @@ static void iscsi_rx_data ( struct iscsi_session *iscsi, void *data,
|
381
|
544
|
iscsi->cmdsn = ntohl ( response->expcmdsn );
|
382
|
545
|
iscsi->statsn = ntohl ( response->statsn );
|
383
|
546
|
|
384
|
|
- /* Increment itt when we receive a final response */
|
385
|
|
- if ( response->flags & ISCSI_FLAG_FINAL )
|
386
|
|
- iscsi->itt++;
|
387
|
|
-
|
388
|
547
|
switch ( response->opcode & ISCSI_OPCODE_MASK ) {
|
389
|
548
|
case ISCSI_OPCODE_LOGIN_RESPONSE:
|
390
|
549
|
iscsi_rx_login_response ( iscsi, data, len, remaining );
|
391
|
550
|
break;
|
|
551
|
+ case ISCSI_OPCODE_SCSI_RESPONSE:
|
|
552
|
+ iscsi_rx_scsi_response ( iscsi, data, len, remaining );
|
|
553
|
+ break;
|
392
|
554
|
case ISCSI_OPCODE_DATA_IN:
|
393
|
555
|
iscsi_rx_data_in ( iscsi, data, len, remaining );
|
394
|
556
|
break;
|
|
557
|
+ case ISCSI_OPCODE_R2T:
|
|
558
|
+ iscsi_rx_r2t ( iscsi, data, len, remaining );
|
|
559
|
+ break;
|
395
|
560
|
default:
|
396
|
561
|
printf ( "Unknown iSCSI opcode %02x\n", response->opcode );
|
397
|
562
|
iscsi->status |= ( ISCSI_STATUS_DONE | ISCSI_STATUS_ERR );
|
|
@@ -521,6 +686,7 @@ static void iscsi_closed ( struct tcp_connection *conn, int status __unused ) {
|
521
|
686
|
if ( ++iscsi->retry_count <= ISCSI_MAX_RETRIES ) {
|
522
|
687
|
tcp_connect ( conn );
|
523
|
688
|
} else {
|
|
689
|
+ printf ( "iSCSI retry count exceeded\n" );
|
524
|
690
|
iscsi->status |= ( ISCSI_STATUS_DONE | ISCSI_STATUS_ERR );
|
525
|
691
|
}
|
526
|
692
|
}
|