Browse Source

First draft of iSCSI protocol support. Is capable of retrieving disk

blocks.
tags/v0.9.3
Michael Brown 18 years ago
parent
commit
6446767258
3 changed files with 1052 additions and 0 deletions
  1. 460
    0
      src/include/gpxe/iscsi.h
  2. 34
    0
      src/include/gpxe/scsi.h
  3. 558
    0
      src/proto/iscsi.c

+ 460
- 0
src/include/gpxe/iscsi.h View File

@@ -0,0 +1,460 @@
1
+#ifndef _ISCSI_H
2
+#define _ISCSI_H
3
+
4
+/** @file
5
+ *
6
+ * iSCSI protocol
7
+ *
8
+ */
9
+
10
+#include <stdint.h>
11
+#include <gpxe/tcp.h>
12
+#include <gpxe/scsi.h>
13
+
14
+/**
15
+ * iSCSI segment lengths
16
+ *
17
+ * iSCSI uses an icky structure with one one-byte field (a dword
18
+ * count) and one three-byte field (a byte count).  This structure,
19
+ * and the accompanying macros, relieve some of the pain.
20
+ */
21
+union iscsi_segment_lengths {
22
+	struct {
23
+		/** The AHS length (measured in dwords) */
24
+		uint8_t ahs_len;
25
+		/** The data length (measured in bytes), in network
26
+		 * byte order
27
+		 */
28
+		uint8_t data_len[3];
29
+	} bytes;
30
+	/** Ths data length (measured in bytes), in network byte
31
+	 * order, with ahs_len as the first byte.
32
+	 */
33
+	uint32_t ahs_and_data_len;
34
+};
35
+
36
+/** The length of the additional header segment, in dwords */
37
+#define ISCSI_AHS_LEN( segment_lengths ) \
38
+	( (segment_lengths).bytes.ahs_len )
39
+
40
+/** The length of the data segment, in bytes, excluding any padding */
41
+#define ISCSI_DATA_LEN( segment_lengths ) \
42
+	( ntohl ( (segment_lengths).ahs_and_data_len ) & 0xffffff )
43
+
44
+/** The padding of the data segment, in bytes */
45
+#define ISCSI_DATA_PAD_LEN( segment_lengths ) \
46
+	( ( 0 - (segment_lengths).bytes.data_len[2] ) & 0x03 )
47
+
48
+/** Set additional header and data segment lengths */
49
+#define ISCSI_SET_LENGTHS( segment_lengths, ahs_len, data_len ) do {	\
50
+	(segment_lengths).ahs_and_data_len =				\
51
+		htonl ( data_len | ( ahs_len << 24 ) );			\
52
+	} while ( 0 )
53
+
54
+/**
55
+ * iSCSI basic header segment common fields
56
+ *
57
+ */
58
+struct iscsi_bhs_common {
59
+	/** Opcode */
60
+	uint8_t opcode;
61
+	/** Flags */
62
+	uint8_t flags;
63
+	/** Fields specific to the PDU type */
64
+	uint8_t other_a[2];
65
+	/** Segment lengths */
66
+	union iscsi_segment_lengths lengths;
67
+	/** Fields specific to the PDU type */
68
+	uint8_t other_b[8];
69
+	/** Initiator Task Tag */
70
+	uint32_t itt;
71
+	/** Fields specific to the PDU type */
72
+	uint8_t other_c[28];
73
+};
74
+
75
+/** Opcode mask */
76
+#define ISCSI_OPCODE_MASK 0x3f
77
+
78
+/** Immediate delivery */
79
+#define ISCSI_FLAG_IMMEDIATE 0x40
80
+
81
+/** Final PDU of a sequence */
82
+#define ISCSI_FLAG_FINAL 0x80
83
+
84
+/**
85
+ * iSCSI login request basic header segment
86
+ *
87
+ */
88
+struct iscsi_bhs_login_request {
89
+	/** Opcode */
90
+	uint8_t opcode;
91
+	/** Flags */
92
+	uint8_t flags;
93
+	/** Maximum supported version number */
94
+	uint8_t version_max;
95
+	/** Minimum supported version number */
96
+	uint8_t version_min;
97
+	/** Segment lengths */
98
+	union iscsi_segment_lengths lengths;
99
+	/** Initiator session ID (IANA format) enterprise number and flags */
100
+	uint32_t isid_iana_en;
101
+	/** Initiator session ID (IANA format) qualifier */
102
+	uint16_t isid_iana_qual;
103
+	/** Target session identifying handle */
104
+	uint16_t tsih;
105
+	/** Initiator Task Tag */
106
+	uint32_t itt;
107
+	/** Connection ID */
108
+	uint16_t cid;
109
+	/** Reserved */
110
+	uint16_t reserved_a;
111
+	/** Command sequence number */
112
+	uint32_t cmdsn;
113
+	/** Expected status sequence number */
114
+	uint32_t expstatsn;
115
+	/** Reserved */
116
+	uint8_t reserved_b[16];
117
+};
118
+
119
+/** Login request opcode */
120
+#define ISCSI_OPCODE_LOGIN_REQUEST 0x03
121
+
122
+/** Willingness to transition to next stage */
123
+#define ISCSI_LOGIN_FLAG_TRANSITION 0x80
124
+
125
+/** Key=value pairs continued in subsequent request */
126
+#define ISCSI_LOGIN_FLAG_CONTINUE 0x40
127
+
128
+/* Current stage values and mask */
129
+#define ISCSI_LOGIN_CSG_MASK 0x0c
130
+#define ISCSI_LOGIN_CSG_SECURITY_NEGOTIATION 0x00
131
+#define ISCSI_LOGIN_CSG_OPERATIONAL_NEGOTIATION 0x04
132
+#define ISCSI_LOGIN_CSG_FULL_FEATURE_PHASE 0x0c
133
+
134
+/* Next stage values and mask */
135
+#define ISCSI_LOGIN_NSG_MASK 0x03
136
+#define ISCSI_LOGIN_NSG_SECURITY_NEGOTIATION 0x00
137
+#define ISCSI_LOGIN_NSG_OPERATIONAL_NEGOTIATION 0x01
138
+#define ISCSI_LOGIN_NSG_FULL_FEATURE_PHASE 0x03
139
+
140
+/** ISID IANA format marker */
141
+#define ISCSI_ISID_IANA 0x40000000
142
+
143
+/** Fen Systems Ltd. IANA enterprise number
144
+ *
145
+ * Permission is hereby granted to use Fen Systems Ltd.'s IANA
146
+ * enterprise number with this iSCSI implementation.
147
+ */
148
+#define IANA_EN_FEN_SYSTEMS 10019
149
+
150
+/**
151
+ * iSCSI login response basic header segment
152
+ *
153
+ */
154
+struct iscsi_bhs_login_response {
155
+	/** Opcode */
156
+	uint8_t opcode;
157
+	/** Flags */
158
+	uint8_t flags;
159
+	/** Maximum supported version number */
160
+	uint8_t version_max;
161
+	/** Minimum supported version number */
162
+	uint8_t version_min;
163
+	/** Segment lengths */
164
+	union iscsi_segment_lengths lengths;
165
+	/** Initiator session ID (IANA format) enterprise number and flags */
166
+	uint32_t isid_iana_en;
167
+	/** Initiator session ID (IANA format) qualifier */
168
+	uint16_t isid_iana_qual;
169
+	/** Target session identifying handle */
170
+	uint16_t tsih;
171
+	/** Initiator Task Tag */
172
+	uint32_t itt;
173
+	/** Reserved */
174
+	uint32_t reserved_a;
175
+	/** Status sequence number */
176
+	uint32_t statsn;
177
+	/** Expected command sequence number */
178
+	uint32_t expcmdsn;
179
+	/** Maximum command sequence number */
180
+	uint32_t maxcmdsn;
181
+	/** Status class */
182
+	uint8_t status_class;
183
+	/** Status detail */
184
+	uint8_t status_detail;
185
+	/** Reserved */
186
+	uint8_t reserved_b[10];
187
+};
188
+
189
+/** Login response opcode */
190
+#define ISCSI_OPCODE_LOGIN_RESPONSE 0x23
191
+
192
+/**
193
+ * iSCSI SCSI command basic header segment
194
+ *
195
+ */
196
+struct iscsi_bhs_scsi_command {
197
+	/** Opcode */
198
+	uint8_t opcode;
199
+	/** Flags */
200
+	uint8_t flags;
201
+	/** Reserved */
202
+	uint16_t reserved_a;
203
+	/** Segment lengths */
204
+	union iscsi_segment_lengths lengths;
205
+	/** SCSI Logical Unit Number */
206
+	uint8_t lun[8];
207
+	/** Initiator Task Tag */
208
+	uint32_t itt;
209
+	/** Expected data transfer length */
210
+	uint32_t exp_len;
211
+	/** Command sequence number */
212
+	uint32_t cmdsn;
213
+	/** Expected status sequence number */
214
+	uint32_t expstatsn;
215
+	/** SCSI Command Descriptor Block (CDB) */
216
+	union scsi_cdb cdb;
217
+};
218
+
219
+/** SCSI command opcode */
220
+#define ISCSI_OPCODE_SCSI_COMMAND 0x01
221
+
222
+/** Command will read data */
223
+#define ISCSI_COMMAND_FLAG_READ 0x40
224
+
225
+/** Command will write data */
226
+#define ISCSI_COMMAND_FLAG_WRITE 0x20
227
+
228
+/* Task attributes */
229
+#define ISCSI_COMMAND_ATTR_UNTAGGED 0x00
230
+#define ISCSI_COMMAND_ATTR_SIMPLE 0x01
231
+#define ISCSI_COMMAND_ATTR_ORDERED 0x02
232
+#define ISCSI_COMMAND_ATTR_HEAD_OF_QUEUE 0x03
233
+#define ISCSI_COMMAND_ATTR_ACA 0x04
234
+
235
+/**
236
+ * iSCSI SCSI response basic header segment
237
+ *
238
+ */
239
+struct iscsi_bhs_scsi_response {
240
+	/** Opcode */
241
+	uint8_t opcode;
242
+	/** Flags */
243
+	uint8_t flags;
244
+	/** Response code */
245
+	uint8_t response;
246
+	/** SCSI status code */
247
+	uint8_t status;
248
+	/** Segment lengths */
249
+	union iscsi_segment_lengths lengths;
250
+	/** Reserved */
251
+	uint8_t reserved_a[8];
252
+	/** Initiator Task Tag */
253
+	uint32_t itt;
254
+	/** SNACK tag */
255
+	uint32_t snack;
256
+	/** Status sequence number */
257
+	uint32_t statsn;
258
+	/** Expected command sequence number */
259
+	uint32_t expcmdsn;
260
+	/** Maximum command sequence number */
261
+	uint32_t maxcmdsn;
262
+	/** Expected data sequence number */
263
+	uint32_t expdatasn;
264
+	/** Reserved */
265
+	uint8_t reserved_b[8];
266
+};
267
+
268
+/** SCSI response opcode */
269
+#define ISCSI_OPCODE_SCSI_RESPONSE 0x21
270
+
271
+/** SCSI command completed at target */
272
+#define ISCSI_RESPONSE_COMMAND_COMPLETE 0x00
273
+
274
+/** SCSI target failure */
275
+#define ISCSI_RESPONSE_TARGET_FAILURE 0x01
276
+
277
+/**
278
+ * iSCSI data in basic header segment
279
+ *
280
+ */
281
+struct iscsi_bhs_data_in {
282
+	/** Opcode */
283
+	uint8_t opcode;
284
+	/** Flags */
285
+	uint8_t flags;
286
+	/** Reserved */
287
+	uint8_t reserved_a;
288
+	/** SCSI status code */
289
+	uint8_t status;
290
+	/** Segment lengths */
291
+	union iscsi_segment_lengths lengths;
292
+	/** Logical Unit Number */
293
+	uint8_t lun[8];
294
+	/** Initiator Task Tag */
295
+	uint32_t itt;
296
+	/** Target Transfer Tag */
297
+	uint32_t ttt;
298
+	/** Status sequence number */
299
+	uint32_t statsn;
300
+	/** Expected command sequence number */
301
+	uint32_t expcmdsn;
302
+	/** Maximum command sequence number */
303
+	uint32_t maxcmdsn;
304
+	/** Data sequence number */
305
+	uint32_t datasn;
306
+	/** Buffer offset */
307
+	uint32_t offset;
308
+	/** Residual count */
309
+	uint32_t residual_count;
310
+};
311
+
312
+/** Data in opcode */
313
+#define ISCSI_OPCODE_DATA_IN 0x25
314
+
315
+/** Data requires acknowledgement */
316
+#define ISCSI_DATA_FLAG_ACKNOWLEDGE 0x40
317
+
318
+/** Data overflow occurred */
319
+#define ISCSI_DATA_FLAG_OVERFLOW 0x04
320
+
321
+/** Data underflow occurred */
322
+#define ISCSI_DATA_FLAG_UNDERFLOW 0x02
323
+
324
+/** SCSI status code and verflow/underflow flags are valid */
325
+#define ISCSI_DATA_FLAG_STATUS 0x01
326
+
327
+/**
328
+ * An iSCSI basic header segment
329
+ */
330
+union iscsi_bhs {
331
+	struct iscsi_bhs_common common;
332
+	struct iscsi_bhs_login_request login_request;
333
+	struct iscsi_bhs_login_response login_response;
334
+	struct iscsi_bhs_scsi_command scsi_command;
335
+	struct iscsi_bhs_scsi_response scsi_response;
336
+	struct iscsi_bhs_data_in data_in;
337
+	unsigned char bytes[ sizeof ( struct iscsi_bhs_common ) ];
338
+};
339
+
340
+/** State */
341
+enum iscsi_state {
342
+	/** In the process of logging in */
343
+	ISCSI_STATE_FAILED = -1,
344
+	ISCSI_STATE_NOT_CONNECTED = 0,
345
+	ISCSI_STATE_IDLE,
346
+	ISCSI_STATE_LOGGING_IN,
347
+	ISCSI_STATE_READING_DATA,
348
+};
349
+
350
+/** State of an iSCSI TX engine */
351
+enum iscsi_tx_state {
352
+	/** Nothing to send */
353
+	ISCSI_TX_IDLE = 0,
354
+	/** Sending the basic header segment */
355
+	ISCSI_TX_BHS,
356
+	/** Sending the additional header segment */
357
+	ISCSI_TX_AHS,
358
+	/** Sending the data segment */
359
+	ISCSI_TX_DATA,
360
+	/** Sending the data segment padding */
361
+	ISCSI_TX_DATA_PADDING,
362
+};
363
+
364
+/** State of an iSCSI RX engine */
365
+enum iscsi_rx_state {
366
+	/** Receiving the basic header segment */
367
+	ISCSI_RX_BHS = 0,
368
+	/** Receiving the additional header segment */
369
+	ISCSI_RX_AHS,
370
+	/** Receiving the data segment */
371
+	ISCSI_RX_DATA,
372
+	/** Receiving the data segment padding */
373
+	ISCSI_RX_DATA_PADDING,
374
+};
375
+
376
+/** An iSCSI session */
377
+struct iscsi_session {
378
+	/** TCP connection for this session */
379
+	struct tcp_connection tcp;
380
+
381
+	/** Initiator IQN */
382
+	const char *initiator;
383
+	/** Target IQN */
384
+	const char *target;
385
+
386
+	/** Block size in bytes */
387
+	size_t block_size;
388
+	/** Starting block number of the current data transfer */
389
+	unsigned long block_start;
390
+	/** Block count of the current data transfer */
391
+	unsigned long block_count;
392
+	/** Block read callback function
393
+	 *
394
+	 * Note that this may be called several times, since it is
395
+	 * called per-packet rather than per-block.
396
+	 */
397
+	void ( * block_read_callback ) ( void *private, const void *data,
398
+					 unsigned long offset, size_t len );
399
+	/** Block read callback private data
400
+	 *
401
+	 * This is passed to block_read_callback()
402
+	 */
403
+	void *block_read_private;
404
+
405
+	/** State of the session */
406
+	enum iscsi_state state;
407
+	/** Target session identifying handle
408
+	 *
409
+	 * This is assigned by the target when we first log in, and
410
+	 * must be reused on subsequent login attempts.
411
+	 */
412
+	uint16_t tsih;
413
+
414
+	/** Initiator task tag
415
+	 *
416
+	 * This is the tag of the current command.  It is incremented
417
+	 * whenever a final response PDU is received.
418
+	 */
419
+	uint32_t itt;
420
+	/** Command sequence number
421
+	 *
422
+	 * This is the sequence number of the current command, used to
423
+	 * fill out the CmdSN field in iSCSI request PDUs.  It is
424
+	 * updated with the value of the ExpCmdSN field whenever we
425
+	 * receive an iSCSI response PDU containing such a field.
426
+	 */
427
+	uint32_t cmdsn;
428
+	/** Status sequence number
429
+	 *
430
+	 * This is the most recent status sequence number present in
431
+	 * the StatSN field of an iSCSI response PDU containing such a
432
+	 * field.  Whenever we send an iSCSI request PDU, we fill out
433
+	 * the ExpStatSN field with this value plus one.
434
+	 */
435
+	uint32_t statsn;
436
+	
437
+	/** Basic header segment for current TX PDU */
438
+	union iscsi_bhs tx_bhs;
439
+	/** State of the TX engine */
440
+	enum iscsi_tx_state tx_state;
441
+	/** Byte offset within the current TX state */
442
+	size_t tx_offset;
443
+
444
+	/** Basic header segment for current RX PDU */
445
+	union iscsi_bhs rx_bhs;
446
+	/** State of the RX engine */
447
+	enum iscsi_rx_state rx_state;
448
+	/** Byte offset within the current RX state */
449
+	size_t rx_offset;
450
+};
451
+
452
+static inline int iscsi_busy ( struct iscsi_session *iscsi ) {
453
+	return ( iscsi->state > ISCSI_STATE_IDLE );
454
+}
455
+
456
+static inline int iscsi_error ( struct iscsi_session *iscsi ) {
457
+	return ( iscsi->state == ISCSI_STATE_FAILED );
458
+}
459
+
460
+#endif /* _ISCSI_H */

+ 34
- 0
src/include/gpxe/scsi.h View File

@@ -0,0 +1,34 @@
1
+#ifndef _SCSI_H
2
+#define _SCSI_H
3
+
4
+#include <stdint.h>
5
+
6
+struct scsi_cdb_read_10 {
7
+	/** Opcode */
8
+	uint8_t opcode;
9
+	/** Flags */
10
+	uint8_t flags;
11
+	/** Start address
12
+	 *
13
+	 * This is a logical block number, in big-endian order.
14
+	 */
15
+	uint32_t lba;
16
+	/** Group number */
17
+	uint8_t group;
18
+	/** Transfer length
19
+	 *
20
+	 * This is a logical block count.
21
+	 */
22
+	uint16_t len;
23
+	/** Control byte */
24
+	uint8_t control;
25
+} __attribute__ (( packed ));
26
+
27
+#define SCSI_OPCODE_READ_10 0x28
28
+
29
+union scsi_cdb {
30
+	struct scsi_cdb_read_10 read_10;
31
+	char bytes[16];
32
+};
33
+
34
+#endif /* _SCSI_H */

+ 558
- 0
src/proto/iscsi.c View File

@@ -0,0 +1,558 @@
1
+#include <stddef.h>
2
+#include <string.h>
3
+#include <vsprintf.h>
4
+#include <assert.h>
5
+#include <byteswap.h>
6
+#include <gpxe/iscsi.h>
7
+
8
+/** @file
9
+ *
10
+ * iSCSI protocol
11
+ *
12
+ */
13
+
14
+/****************************************************************************
15
+ *
16
+ * Utility functions
17
+ *
18
+ */
19
+
20
+/**
21
+ * Start up a new TX PDU
22
+ *
23
+ * @v iscsi		iSCSI session
24
+ *
25
+ * This initiates the process of sending a new PDU.  Only one PDU may
26
+ * be in transit at any one time.
27
+ */
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;
31
+	iscsi->tx_offset = 0;
32
+}
33
+
34
+/**
35
+ * Mark session as failed
36
+ *
37
+ * @v iscsi		iSCSI session
38
+ *
39
+ * This marks the session as permanently failed.  The session will not
40
+ * be automatically logged back in.
41
+ */
42
+static void iscsi_fail ( struct iscsi_session *iscsi ) {
43
+	iscsi->state = ISCSI_STATE_FAILED;
44
+	tcp_close ( &iscsi->tcp );
45
+}
46
+
47
+/****************************************************************************
48
+ *
49
+ * iSCSI SCSI command issuing
50
+ *
51
+ */
52
+
53
+/**
54
+ * Start up a block read
55
+ *
56
+ * @v iscsi		iSCSI session
57
+ *
58
+ */
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;
62
+
63
+	assert ( iscsi->block_size != 0 );
64
+	assert ( iscsi->block_count != 0 );
65
+	assert ( iscsi->block_read_callback != NULL );
66
+
67
+	/* Construct BHS */
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 );
82
+
83
+	iscsi->state = ISCSI_STATE_READING_DATA;
84
+	iscsi_start_tx ( iscsi );
85
+}
86
+
87
+/**
88
+ * Receive data segment of an iSCSI data-in PDU
89
+ *
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
94
+ * 
95
+ */
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;
99
+	unsigned long offset;
100
+
101
+	/* Update cmdsn and statsn */
102
+	iscsi->cmdsn = ntohl ( data_in->expcmdsn );
103
+	iscsi->statsn = ntohl ( data_in->statsn );
104
+
105
+	/* Process data via callback */
106
+	offset = ntohl ( data_in->offset ) + iscsi->rx_offset;
107
+	iscsi->block_read_callback ( iscsi->block_read_private,
108
+				     data, offset, len );
109
+
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;
113
+}
114
+
115
+/****************************************************************************
116
+ *
117
+ * iSCSI login
118
+ *
119
+ */
120
+
121
+/**
122
+ * Build iSCSI login request strings
123
+ *
124
+ * @v iscsi		iSCSI session
125
+ *
126
+ * These are the initial set of strings sent in the first login
127
+ * request PDU.
128
+ */
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"
133
+			  "TargetName=%s%c"
134
+			  "MaxRecvDataSegmentLength=512%c"
135
+			  "SessionType=Normal%c"
136
+			  "DataDigest=None%c"
137
+			  "HeaderDigest=None%c",
138
+			  iscsi->initiator, 0, iscsi->target, 0,
139
+			  0, 0, 0, 0 );
140
+}
141
+
142
+/**
143
+ * Transmit data segment of an iSCSI login request PDU
144
+ *
145
+ * @v iscsi		iSCSI session
146
+ *
147
+ * For login requests, the data segment consists of the login strings.
148
+ */
149
+static void iscsi_tx_login_request ( struct iscsi_session *iscsi ) {
150
+	int len;
151
+
152
+	len = iscsi_build_login_request_strings ( iscsi, tcp_buffer,
153
+						  tcp_buflen );
154
+	tcp_send ( &iscsi->tcp, tcp_buffer + iscsi->tx_offset,
155
+		   len - iscsi->tx_offset );
156
+}
157
+
158
+/**
159
+ * Start up a login request
160
+ *
161
+ * @v iscsi		iSCSI session
162
+ *
163
+ */
164
+static void iscsi_start_login ( struct iscsi_session *iscsi ) {
165
+	struct iscsi_bhs_login_request *request = &iscsi->tx_bhs.login_request;
166
+	int len;
167
+
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 );
186
+
187
+	iscsi->state = ISCSI_STATE_LOGGING_IN;
188
+	iscsi_start_tx ( iscsi );
189
+}
190
+
191
+/**
192
+ * Receive data segment of an iSCSI login response PDU
193
+ *
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
198
+ * 
199
+ */
200
+static void iscsi_rx_login_response ( struct iscsi_session *iscsi,
201
+				      void *data __unused,
202
+				      size_t len __unused,
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;
207
+
208
+	/* Sanity check */
209
+	if ( iscsi->state != ISCSI_STATE_LOGGING_IN ) {
210
+		printf ( "Spurious iSCSI login response\n" );
211
+		iscsi_fail ( iscsi );
212
+		return;
213
+	}
214
+
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 );
220
+		return;
221
+	}
222
+
223
+	/* Update cmdsn and statsn */
224
+	iscsi->cmdsn = ntohl ( response->expcmdsn );
225
+	iscsi->statsn = ntohl ( response->statsn );
226
+
227
+	/* If server did not transition, we send it another login
228
+	 * request with empty strings.
229
+	 */
230
+	if ( ! ( response->flags & ISCSI_LOGIN_FLAG_TRANSITION ) ) {
231
+		ISCSI_SET_LENGTHS ( request->lengths, 0, 0 );
232
+		iscsi_start_tx ( iscsi );
233
+		return;
234
+	}
235
+
236
+	/* Record TSIH  for future reference */
237
+	iscsi->tsih = ntohl ( response->tsih );
238
+
239
+	/* Start reading data */
240
+	iscsi_start_read_block ( iscsi );
241
+}
242
+
243
+/****************************************************************************
244
+ *
245
+ * iSCSI to TCP interface
246
+ *
247
+ */
248
+
249
+static inline struct iscsi_session *
250
+tcp_to_iscsi ( struct tcp_connection *conn ) {
251
+	return container_of ( conn, struct iscsi_session, tcp );
252
+}
253
+
254
+static void iscsi_aborted ( struct tcp_connection *conn ) {
255
+	struct iscsi_session *iscsi = tcp_to_iscsi ( conn );
256
+
257
+}
258
+
259
+static void iscsi_timedout ( struct tcp_connection *conn ) {
260
+	struct iscsi_session *iscsi = tcp_to_iscsi ( conn );
261
+
262
+}
263
+
264
+static void iscsi_closed ( struct tcp_connection *conn ) {
265
+	struct iscsi_session *iscsi = tcp_to_iscsi ( conn );
266
+
267
+}
268
+
269
+static void iscsi_connected ( struct tcp_connection *conn ) {
270
+	struct iscsi_session *iscsi = tcp_to_iscsi ( conn );
271
+
272
+	/* Prepare to receive PDUs. */
273
+	iscsi->rx_state = ISCSI_RX_BHS;
274
+	iscsi->rx_offset = 0;
275
+
276
+	/* TX state should already have been set up */
277
+	assert ( iscsi->tx_state != ISCSI_TX_IDLE );
278
+	assert ( iscsi->tx_offset == 0 );
279
+}
280
+
281
+/**
282
+ * Transmit data segment of an iSCSI PDU
283
+ *
284
+ * @v iscsi		iSCSI session
285
+ * 
286
+ * Handle transmission of part of a PDU data segment.  iscsi::tx_bhs
287
+ * will be valid when this is called.
288
+ */
289
+static void iscsi_tx_data ( struct iscsi_session *iscsi ) {
290
+	struct iscsi_bhs_common *common = &iscsi->tx_bhs.common;
291
+
292
+	switch ( common->opcode & ISCSI_OPCODE_MASK ) {
293
+	case ISCSI_OPCODE_LOGIN_REQUEST:
294
+		iscsi_tx_login_request ( iscsi );
295
+		break;
296
+	default:
297
+		assert ( 0 );
298
+		break;
299
+	}
300
+}
301
+
302
+/**
303
+ * Handle TCP ACKs
304
+ *
305
+ * @v iscsi		iSCSI session
306
+ * 
307
+ * Updates iscsi->tx_offset and, if applicable, transitions to the
308
+ * next TX state.
309
+ */
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;
315
+	
316
+	iscsi->tx_offset += len;
317
+	while ( 1 ) {
318
+		switch ( iscsi->tx_state ) {
319
+		case ISCSI_TX_BHS:
320
+			max_tx_offset = sizeof ( iscsi->tx_bhs );
321
+			next_state = ISCSI_TX_AHS;
322
+			break;
323
+		case ISCSI_TX_AHS:
324
+			max_tx_offset = 4 * ISCSI_AHS_LEN ( common->lengths );
325
+			next_state = ISCSI_TX_DATA;
326
+			break;
327
+		case ISCSI_TX_DATA:
328
+			max_tx_offset = ISCSI_DATA_LEN ( common->lengths );
329
+			next_state = ISCSI_TX_DATA_PADDING;
330
+			break;
331
+		case ISCSI_TX_DATA_PADDING:
332
+			max_tx_offset = ISCSI_DATA_PAD_LEN ( common->lengths );
333
+			next_state = ISCSI_TX_IDLE;
334
+			break;
335
+		case ISCSI_TX_IDLE:
336
+			return;
337
+		default:
338
+			assert ( 0 );
339
+			return;
340
+		}
341
+		assert ( iscsi->tx_offset <= max_tx_offset );
342
+
343
+		/* If the whole of the current portion has not yet
344
+		 * been acked, stay in this state for now.
345
+		 */
346
+		if ( iscsi->tx_offset != max_tx_offset )
347
+			return;
348
+		
349
+		iscsi->tx_state = next_state;
350
+		iscsi->tx_offset = 0;
351
+	}
352
+}
353
+
354
+/**
355
+ * Transmit iSCSI PDU
356
+ *
357
+ * @v iscsi		iSCSI session
358
+ * 
359
+ * Constructs data to be sent for the current TX state
360
+ */
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' };
365
+
366
+	switch ( iscsi->tx_state ) {
367
+	case ISCSI_TX_IDLE:
368
+		/* Do nothing */
369
+		break;
370
+	case ISCSI_TX_BHS:
371
+		tcp_send ( conn, &iscsi->tx_bhs.bytes[iscsi->tx_offset],
372
+			   ( sizeof ( iscsi->tx_bhs ) - iscsi->tx_offset ) );
373
+		break;
374
+	case ISCSI_TX_AHS:
375
+		/* We don't yet have an AHS transmission mechanism */
376
+		assert ( 0 );
377
+		break;
378
+	case ISCSI_TX_DATA:
379
+		iscsi_tx_data ( iscsi );
380
+		break;
381
+	case ISCSI_TX_DATA_PADDING:
382
+		tcp_send ( conn, pad, ( ISCSI_DATA_PAD_LEN ( common->lengths )
383
+					- iscsi->tx_offset ) );
384
+		break;
385
+	default:
386
+		assert ( 0 );
387
+		break;
388
+	}
389
+}
390
+
391
+/**
392
+ * Receive data segment of an iSCSI PDU
393
+ *
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
398
+ *
399
+ * Handle processing of part of a PDU data segment.  iscsi::rx_bhs
400
+ * will be valid when this is called.
401
+ */
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;
405
+
406
+	switch ( common->opcode & ISCSI_OPCODE_MASK ) {
407
+	case ISCSI_OPCODE_LOGIN_RESPONSE:
408
+		iscsi_rx_login_response ( iscsi, data, len, remaining );
409
+		break;
410
+	case ISCSI_OPCODE_DATA_IN:
411
+		iscsi_rx_data_in ( iscsi, data, len, remaining );
412
+		break;
413
+	default:
414
+		printf ( "Unknown iSCSI opcode %02x\n", common->opcode );
415
+		break;
416
+	}
417
+}
418
+
419
+/**
420
+ * Discard portion of an iSCSI PDU.
421
+ *
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
426
+ *
427
+ * This discards data from a portion of a received PDU.
428
+ */
429
+static void iscsi_rx_discard ( struct iscsi_session *iscsi __unused,
430
+			       void *data __unused, size_t len __unused,
431
+			       size_t remaining __unused ) {
432
+	/* Do nothing */
433
+}
434
+
435
+/**
436
+ * Receive basic header segment of an iSCSI PDU
437
+ *
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
442
+ *
443
+ * This fills in iscsi::rx_bhs with the data from the BHS portion of
444
+ * the received PDU.
445
+ */
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 );
449
+}
450
+
451
+/**
452
+ * Receive new data
453
+ *
454
+ * @v tcp		TCP connection
455
+ * @v data		Received data
456
+ * @v len		Length of received data
457
+ *
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.
464
+ */
465
+static void iscsi_newdata ( struct tcp_connection *conn, void *data,
466
+			    size_t len ) {
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;
473
+	size_t frag_len;
474
+	size_t remaining;
475
+
476
+	while ( 1 ) {
477
+		switch ( iscsi->rx_state ) {
478
+		case ISCSI_RX_BHS:
479
+			process = iscsi_rx_bhs;
480
+			max_rx_offset = sizeof ( iscsi->rx_bhs );
481
+			next_state = ISCSI_RX_AHS;			
482
+			break;
483
+		case ISCSI_RX_AHS:
484
+			process = iscsi_rx_discard;
485
+			max_rx_offset = 4 * ISCSI_AHS_LEN ( common->lengths );
486
+			next_state = ISCSI_RX_DATA;
487
+			break;
488
+		case ISCSI_RX_DATA:
489
+			process = iscsi_rx_data;
490
+			max_rx_offset = ISCSI_DATA_LEN ( common->lengths );
491
+			next_state = ISCSI_RX_DATA_PADDING;
492
+			break;
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;
497
+			break;
498
+		default:
499
+			assert ( 0 );
500
+			return;
501
+		}
502
+
503
+		frag_len = max_rx_offset - iscsi->rx_offset;
504
+		if ( frag_len > len )
505
+			frag_len = len;
506
+		remaining = max_rx_offset - iscsi->rx_offset - frag_len;
507
+		process ( iscsi, data, frag_len, remaining );
508
+
509
+		iscsi->rx_offset += frag_len;
510
+		data += frag_len;
511
+		len -= frag_len;
512
+
513
+		/* If all the data for this state has not yet been
514
+		 * received, stay in this state for now.
515
+		 */
516
+		if ( iscsi->rx_offset != max_rx_offset )
517
+			return;
518
+
519
+		iscsi->rx_state = next_state;
520
+		iscsi->rx_offset = 0;
521
+	}
522
+}
523
+
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,
533
+};
534
+
535
+/**
536
+ * Wake up session
537
+ *
538
+ * @v iscsi		iSCSI session
539
+ *
540
+ */
541
+void iscsi_wakeup ( struct iscsi_session *iscsi ) {
542
+	iscsi->tcp.tcp_op = &iscsi_tcp_operations;
543
+
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 );
550
+		break;
551
+	case ISCSI_STATE_IDLE:
552
+		iscsi_start_read_block ( iscsi );
553
+		break;
554
+	default:
555
+		/* Stay in same state */
556
+		break;
557
+	}
558
+}

Loading…
Cancel
Save