diff options
author | Boaz Harrosh <bharrosh@panasas.com> | 2007-12-13 13:43:23 -0500 |
---|---|---|
committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2008-01-11 19:28:23 -0500 |
commit | 004d6530f83bee43a55b51bb5960db96e7ae0ffa (patch) | |
tree | a7bb6ffb3e6e89ca6ee9b164d5f19899a0c100dd | |
parent | 7207fea452cfdd2d4e2f4419e2c31f570edbade3 (diff) |
[SCSI] iscsi_tcp, libiscsi: initial AHS Support
at libiscsi generic code
- currently code assumes a storage space of pdu header is allocated
at llds ctask and is pointed to by iscsi_cmd_task->hdr. Here I add
a hdr_max field pertaining to that storage, and an hdr_len that
accumulates the current use of the pdu-header.
- Add an iscsi_next_hdr() inline which returns the next free space
to write new Header at. Also iscsi_next_hdr() is used to retrieve
the address at which to write the header-digest.
- Add iscsi_add_hdr(length). What the user do is calls iscsi_next_hdr()
for address of the new header, than calls iscsi_add_hdr(length) with
the size of the new header. iscsi_add_hdr() will check if space is
available and update to the new size. length must be padded according
to standard.
- Add 2 padding inline helpers thanks to Olaf. Current patch does not
use them but Following patches will.
Also moved definition of ISCSI_PAD_LEN to iscsi_proto.h which had
PAD_WORD_LEN that was never used anywhere.
- Let iscsi_prep_scsi_cmd_pdu() signal an Error return since now it is
possible that it will fail.
- I was tired of yet again writing a "this is a digest" comment next to
sizeof(__u32) so I defined a new ISCSI_DIGEST_SIZE. Now I don't need
any comments. Changed all places that used sizeof(__u32) or "4" in
connection to a digest.
iscsi_tcp specific code
- At struct iscsi_tcp_cmd_task allocate maximum space allowed in
standard for all headers following the iscsi_cmd header. and mark
it so in iscsi_tcp_session_create()
- At iscsi_send_cmd_hdr() retrieve the correct headers size and
write header digest at iscsi_next_hdr().
Signed-off-by: Boaz Harrosh <bharrosh@panasas.com>
Signed-off-by: Olaf Kirch <olaf.kirch@oracle.com>
Signed-off-by: Mike Christie <michaelc@cs.wisc.edu>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
-rw-r--r-- | drivers/scsi/iscsi_tcp.c | 16 | ||||
-rw-r--r-- | drivers/scsi/iscsi_tcp.h | 13 | ||||
-rw-r--r-- | drivers/scsi/libiscsi.c | 41 | ||||
-rw-r--r-- | include/scsi/iscsi_proto.h | 10 | ||||
-rw-r--r-- | include/scsi/libiscsi.h | 33 |
5 files changed, 94 insertions, 19 deletions
diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c index fd88777df28b..491845f18710 100644 --- a/drivers/scsi/iscsi_tcp.c +++ b/drivers/scsi/iscsi_tcp.c | |||
@@ -113,7 +113,7 @@ iscsi_hdr_digest(struct iscsi_conn *conn, struct iscsi_buf *buf, | |||
113 | struct iscsi_tcp_conn *tcp_conn = conn->dd_data; | 113 | struct iscsi_tcp_conn *tcp_conn = conn->dd_data; |
114 | 114 | ||
115 | crypto_hash_digest(&tcp_conn->tx_hash, &buf->sg, buf->sg.length, crc); | 115 | crypto_hash_digest(&tcp_conn->tx_hash, &buf->sg, buf->sg.length, crc); |
116 | buf->sg.length += sizeof(u32); | 116 | buf->sg.length += ISCSI_DIGEST_SIZE; |
117 | } | 117 | } |
118 | 118 | ||
119 | /* | 119 | /* |
@@ -220,6 +220,7 @@ static inline int | |||
220 | iscsi_tcp_chunk_done(struct iscsi_chunk *chunk) | 220 | iscsi_tcp_chunk_done(struct iscsi_chunk *chunk) |
221 | { | 221 | { |
222 | static unsigned char padbuf[ISCSI_PAD_LEN]; | 222 | static unsigned char padbuf[ISCSI_PAD_LEN]; |
223 | unsigned int pad; | ||
223 | 224 | ||
224 | if (chunk->copied < chunk->size) { | 225 | if (chunk->copied < chunk->size) { |
225 | iscsi_tcp_chunk_map(chunk); | 226 | iscsi_tcp_chunk_map(chunk); |
@@ -243,10 +244,8 @@ iscsi_tcp_chunk_done(struct iscsi_chunk *chunk) | |||
243 | } | 244 | } |
244 | 245 | ||
245 | /* Do we need to handle padding? */ | 246 | /* Do we need to handle padding? */ |
246 | if (chunk->total_copied & (ISCSI_PAD_LEN-1)) { | 247 | pad = iscsi_padding(chunk->total_copied); |
247 | unsigned int pad; | 248 | if (pad != 0) { |
248 | |||
249 | pad = ISCSI_PAD_LEN - (chunk->total_copied & (ISCSI_PAD_LEN-1)); | ||
250 | debug_tcp("consume %d pad bytes\n", pad); | 249 | debug_tcp("consume %d pad bytes\n", pad); |
251 | chunk->total_size += pad; | 250 | chunk->total_size += pad; |
252 | chunk->size = pad; | 251 | chunk->size = pad; |
@@ -1385,11 +1384,11 @@ iscsi_send_cmd_hdr(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) | |||
1385 | } | 1384 | } |
1386 | 1385 | ||
1387 | iscsi_buf_init_iov(&tcp_ctask->headbuf, (char*)ctask->hdr, | 1386 | iscsi_buf_init_iov(&tcp_ctask->headbuf, (char*)ctask->hdr, |
1388 | sizeof(struct iscsi_hdr)); | 1387 | ctask->hdr_len); |
1389 | 1388 | ||
1390 | if (conn->hdrdgst_en) | 1389 | if (conn->hdrdgst_en) |
1391 | iscsi_hdr_digest(conn, &tcp_ctask->headbuf, | 1390 | iscsi_hdr_digest(conn, &tcp_ctask->headbuf, |
1392 | (u8*)tcp_ctask->hdrext); | 1391 | iscsi_next_hdr(ctask)); |
1393 | tcp_ctask->xmstate &= ~XMSTATE_CMD_HDR_INIT; | 1392 | tcp_ctask->xmstate &= ~XMSTATE_CMD_HDR_INIT; |
1394 | tcp_ctask->xmstate |= XMSTATE_CMD_HDR_XMIT; | 1393 | tcp_ctask->xmstate |= XMSTATE_CMD_HDR_XMIT; |
1395 | } | 1394 | } |
@@ -2176,7 +2175,8 @@ iscsi_tcp_session_create(struct iscsi_transport *iscsit, | |||
2176 | struct iscsi_cmd_task *ctask = session->cmds[cmd_i]; | 2175 | struct iscsi_cmd_task *ctask = session->cmds[cmd_i]; |
2177 | struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; | 2176 | struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; |
2178 | 2177 | ||
2179 | ctask->hdr = &tcp_ctask->hdr; | 2178 | ctask->hdr = &tcp_ctask->hdr.cmd_hdr; |
2179 | ctask->hdr_max = sizeof(tcp_ctask->hdr) - ISCSI_DIGEST_SIZE; | ||
2180 | } | 2180 | } |
2181 | 2181 | ||
2182 | for (cmd_i = 0; cmd_i < session->mgmtpool_max; cmd_i++) { | 2182 | for (cmd_i = 0; cmd_i < session->mgmtpool_max; cmd_i++) { |
diff --git a/drivers/scsi/iscsi_tcp.h b/drivers/scsi/iscsi_tcp.h index f1c541151100..eb3784f949fd 100644 --- a/drivers/scsi/iscsi_tcp.h +++ b/drivers/scsi/iscsi_tcp.h | |||
@@ -41,7 +41,6 @@ | |||
41 | #define XMSTATE_IMM_HDR_INIT 0x1000 | 41 | #define XMSTATE_IMM_HDR_INIT 0x1000 |
42 | #define XMSTATE_SOL_HDR_INIT 0x2000 | 42 | #define XMSTATE_SOL_HDR_INIT 0x2000 |
43 | 43 | ||
44 | #define ISCSI_PAD_LEN 4 | ||
45 | #define ISCSI_SG_TABLESIZE SG_ALL | 44 | #define ISCSI_SG_TABLESIZE SG_ALL |
46 | #define ISCSI_TCP_MAX_CMD_LEN 16 | 45 | #define ISCSI_TCP_MAX_CMD_LEN 16 |
47 | 46 | ||
@@ -130,14 +129,14 @@ struct iscsi_buf { | |||
130 | 129 | ||
131 | struct iscsi_data_task { | 130 | struct iscsi_data_task { |
132 | struct iscsi_data hdr; /* PDU */ | 131 | struct iscsi_data hdr; /* PDU */ |
133 | char hdrext[sizeof(__u32)]; /* Header-Digest */ | 132 | char hdrext[ISCSI_DIGEST_SIZE];/* Header-Digest */ |
134 | struct iscsi_buf digestbuf; /* digest buffer */ | 133 | struct iscsi_buf digestbuf; /* digest buffer */ |
135 | uint32_t digest; /* data digest */ | 134 | uint32_t digest; /* data digest */ |
136 | }; | 135 | }; |
137 | 136 | ||
138 | struct iscsi_tcp_mgmt_task { | 137 | struct iscsi_tcp_mgmt_task { |
139 | struct iscsi_hdr hdr; | 138 | struct iscsi_hdr hdr; |
140 | char hdrext[sizeof(__u32)]; /* Header-Digest */ | 139 | char hdrext[ISCSI_DIGEST_SIZE]; /* Header-Digest */ |
141 | int xmstate; /* mgmt xmit progress */ | 140 | int xmstate; /* mgmt xmit progress */ |
142 | struct iscsi_buf headbuf; /* header buffer */ | 141 | struct iscsi_buf headbuf; /* header buffer */ |
143 | struct iscsi_buf sendbuf; /* in progress buffer */ | 142 | struct iscsi_buf sendbuf; /* in progress buffer */ |
@@ -159,9 +158,11 @@ struct iscsi_r2t_info { | |||
159 | }; | 158 | }; |
160 | 159 | ||
161 | struct iscsi_tcp_cmd_task { | 160 | struct iscsi_tcp_cmd_task { |
162 | struct iscsi_cmd hdr; | 161 | struct iscsi_hdr_buff { |
163 | char hdrext[4*sizeof(__u16)+ /* AHS */ | 162 | struct iscsi_cmd cmd_hdr; |
164 | sizeof(__u32)]; /* HeaderDigest */ | 163 | char hdrextbuf[ISCSI_MAX_AHS_SIZE + |
164 | ISCSI_DIGEST_SIZE]; | ||
165 | } hdr; | ||
165 | char pad[ISCSI_PAD_LEN]; | 166 | char pad[ISCSI_PAD_LEN]; |
166 | int pad_count; /* padded bytes */ | 167 | int pad_count; /* padded bytes */ |
167 | struct iscsi_buf headbuf; /* header buf (xmit) */ | 168 | struct iscsi_buf headbuf; /* header buf (xmit) */ |
diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index 0beb4c620962..0d7914fd8ef3 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c | |||
@@ -37,6 +37,9 @@ | |||
37 | #include <scsi/scsi_transport_iscsi.h> | 37 | #include <scsi/scsi_transport_iscsi.h> |
38 | #include <scsi/libiscsi.h> | 38 | #include <scsi/libiscsi.h> |
39 | 39 | ||
40 | static void fail_command(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask, | ||
41 | int err); | ||
42 | |||
40 | struct iscsi_session * | 43 | struct iscsi_session * |
41 | class_to_transport_session(struct iscsi_cls_session *cls_session) | 44 | class_to_transport_session(struct iscsi_cls_session *cls_session) |
42 | { | 45 | { |
@@ -122,6 +125,20 @@ void iscsi_prep_unsolicit_data_pdu(struct iscsi_cmd_task *ctask, | |||
122 | } | 125 | } |
123 | EXPORT_SYMBOL_GPL(iscsi_prep_unsolicit_data_pdu); | 126 | EXPORT_SYMBOL_GPL(iscsi_prep_unsolicit_data_pdu); |
124 | 127 | ||
128 | static int iscsi_add_hdr(struct iscsi_cmd_task *ctask, unsigned len) | ||
129 | { | ||
130 | unsigned exp_len = ctask->hdr_len + len; | ||
131 | |||
132 | if (exp_len > ctask->hdr_max) { | ||
133 | WARN_ON(1); | ||
134 | return -EINVAL; | ||
135 | } | ||
136 | |||
137 | WARN_ON(len & (ISCSI_PAD_LEN - 1)); /* caller must pad the AHS */ | ||
138 | ctask->hdr_len = exp_len; | ||
139 | return 0; | ||
140 | } | ||
141 | |||
125 | /** | 142 | /** |
126 | * iscsi_prep_scsi_cmd_pdu - prep iscsi scsi cmd pdu | 143 | * iscsi_prep_scsi_cmd_pdu - prep iscsi scsi cmd pdu |
127 | * @ctask: iscsi cmd task | 144 | * @ctask: iscsi cmd task |
@@ -129,13 +146,19 @@ EXPORT_SYMBOL_GPL(iscsi_prep_unsolicit_data_pdu); | |||
129 | * Prep basic iSCSI PDU fields for a scsi cmd pdu. The LLD should set | 146 | * Prep basic iSCSI PDU fields for a scsi cmd pdu. The LLD should set |
130 | * fields like dlength or final based on how much data it sends | 147 | * fields like dlength or final based on how much data it sends |
131 | */ | 148 | */ |
132 | static void iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask) | 149 | static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask) |
133 | { | 150 | { |
134 | struct iscsi_conn *conn = ctask->conn; | 151 | struct iscsi_conn *conn = ctask->conn; |
135 | struct iscsi_session *session = conn->session; | 152 | struct iscsi_session *session = conn->session; |
136 | struct iscsi_cmd *hdr = ctask->hdr; | 153 | struct iscsi_cmd *hdr = ctask->hdr; |
137 | struct scsi_cmnd *sc = ctask->sc; | 154 | struct scsi_cmnd *sc = ctask->sc; |
155 | unsigned hdrlength; | ||
156 | int rc; | ||
138 | 157 | ||
158 | ctask->hdr_len = 0; | ||
159 | rc = iscsi_add_hdr(ctask, sizeof(*hdr)); | ||
160 | if (rc) | ||
161 | return rc; | ||
139 | hdr->opcode = ISCSI_OP_SCSI_CMD; | 162 | hdr->opcode = ISCSI_OP_SCSI_CMD; |
140 | hdr->flags = ISCSI_ATTR_SIMPLE; | 163 | hdr->flags = ISCSI_ATTR_SIMPLE; |
141 | int_to_scsilun(sc->device->lun, (struct scsi_lun *)hdr->lun); | 164 | int_to_scsilun(sc->device->lun, (struct scsi_lun *)hdr->lun); |
@@ -199,6 +222,15 @@ static void iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask) | |||
199 | hdr->flags |= ISCSI_FLAG_CMD_READ; | 222 | hdr->flags |= ISCSI_FLAG_CMD_READ; |
200 | } | 223 | } |
201 | 224 | ||
225 | /* calculate size of additional header segments (AHSs) */ | ||
226 | hdrlength = ctask->hdr_len - sizeof(*hdr); | ||
227 | |||
228 | WARN_ON(hdrlength & (ISCSI_PAD_LEN-1)); | ||
229 | hdrlength /= ISCSI_PAD_LEN; | ||
230 | |||
231 | WARN_ON(hdrlength >= 256); | ||
232 | hdr->hlength = hdrlength & 0xFF; | ||
233 | |||
202 | conn->scsicmd_pdus_cnt++; | 234 | conn->scsicmd_pdus_cnt++; |
203 | 235 | ||
204 | debug_scsi("iscsi prep [%s cid %d sc %p cdb 0x%x itt 0x%x len %d " | 236 | debug_scsi("iscsi prep [%s cid %d sc %p cdb 0x%x itt 0x%x len %d " |
@@ -206,6 +238,7 @@ static void iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask) | |||
206 | sc->sc_data_direction == DMA_TO_DEVICE ? "write" : "read", | 238 | sc->sc_data_direction == DMA_TO_DEVICE ? "write" : "read", |
207 | conn->id, sc, sc->cmnd[0], ctask->itt, scsi_bufflen(sc), | 239 | conn->id, sc, sc->cmnd[0], ctask->itt, scsi_bufflen(sc), |
208 | session->cmdsn, session->max_cmdsn - session->exp_cmdsn + 1); | 240 | session->cmdsn, session->max_cmdsn - session->exp_cmdsn + 1); |
241 | return 0; | ||
209 | } | 242 | } |
210 | 243 | ||
211 | /** | 244 | /** |
@@ -744,7 +777,10 @@ check_mgmt: | |||
744 | 777 | ||
745 | conn->ctask = list_entry(conn->xmitqueue.next, | 778 | conn->ctask = list_entry(conn->xmitqueue.next, |
746 | struct iscsi_cmd_task, running); | 779 | struct iscsi_cmd_task, running); |
747 | iscsi_prep_scsi_cmd_pdu(conn->ctask); | 780 | if (iscsi_prep_scsi_cmd_pdu(conn->ctask)) { |
781 | fail_command(conn, conn->ctask, DID_ABORT << 16); | ||
782 | continue; | ||
783 | } | ||
748 | conn->session->tt->init_cmd_task(conn->ctask); | 784 | conn->session->tt->init_cmd_task(conn->ctask); |
749 | conn->ctask->state = ISCSI_TASK_RUNNING; | 785 | conn->ctask->state = ISCSI_TASK_RUNNING; |
750 | list_move_tail(conn->xmitqueue.next, &conn->run_list); | 786 | list_move_tail(conn->xmitqueue.next, &conn->run_list); |
@@ -1534,6 +1570,7 @@ iscsi_session_setup(struct iscsi_transport *iscsit, | |||
1534 | if (cmd_task_size) | 1570 | if (cmd_task_size) |
1535 | ctask->dd_data = &ctask[1]; | 1571 | ctask->dd_data = &ctask[1]; |
1536 | ctask->itt = cmd_i; | 1572 | ctask->itt = cmd_i; |
1573 | ctask->hdr_max = sizeof(struct iscsi_cmd); | ||
1537 | INIT_LIST_HEAD(&ctask->running); | 1574 | INIT_LIST_HEAD(&ctask->running); |
1538 | } | 1575 | } |
1539 | 1576 | ||
diff --git a/include/scsi/iscsi_proto.h b/include/scsi/iscsi_proto.h index 751c81eaa7f3..6947082eee6d 100644 --- a/include/scsi/iscsi_proto.h +++ b/include/scsi/iscsi_proto.h | |||
@@ -27,7 +27,7 @@ | |||
27 | #define ISCSI_LISTEN_PORT 3260 | 27 | #define ISCSI_LISTEN_PORT 3260 |
28 | 28 | ||
29 | /* Padding word length */ | 29 | /* Padding word length */ |
30 | #define PAD_WORD_LEN 4 | 30 | #define ISCSI_PAD_LEN 4 |
31 | 31 | ||
32 | /* | 32 | /* |
33 | * useful common(control and data pathes) macro | 33 | * useful common(control and data pathes) macro |
@@ -147,6 +147,14 @@ struct iscsi_rlength_ahdr { | |||
147 | __be32 read_length; | 147 | __be32 read_length; |
148 | }; | 148 | }; |
149 | 149 | ||
150 | /* Extended CDB AHS */ | ||
151 | struct iscsi_ecdb_ahdr { | ||
152 | __be16 ahslength; /* CDB length - 15, including reserved byte */ | ||
153 | uint8_t ahstype; | ||
154 | uint8_t reserved; | ||
155 | uint8_t ecdb[260 - 16]; /* 4-byte aligned extended CDB spillover */ | ||
156 | }; | ||
157 | |||
150 | /* SCSI Response Header */ | 158 | /* SCSI Response Header */ |
151 | struct iscsi_cmd_rsp { | 159 | struct iscsi_cmd_rsp { |
152 | uint8_t opcode; | 160 | uint8_t opcode; |
diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h index e1fb3d0927b0..a9a9e869188d 100644 --- a/include/scsi/libiscsi.h +++ b/include/scsi/libiscsi.h | |||
@@ -78,6 +78,9 @@ enum { | |||
78 | #define ISCSI_ADDRESS_BUF_LEN 64 | 78 | #define ISCSI_ADDRESS_BUF_LEN 64 |
79 | 79 | ||
80 | enum { | 80 | enum { |
81 | /* this is the maximum possible storage for AHSs */ | ||
82 | ISCSI_MAX_AHS_SIZE = sizeof(struct iscsi_ecdb_ahdr) + | ||
83 | sizeof(struct iscsi_rlength_ahdr), | ||
81 | ISCSI_DIGEST_SIZE = sizeof(__u32), | 84 | ISCSI_DIGEST_SIZE = sizeof(__u32), |
82 | }; | 85 | }; |
83 | 86 | ||
@@ -102,10 +105,13 @@ enum { | |||
102 | 105 | ||
103 | struct iscsi_cmd_task { | 106 | struct iscsi_cmd_task { |
104 | /* | 107 | /* |
105 | * Becuae LLDs allocate their hdr differently, this is a pointer to | 108 | * Because LLDs allocate their hdr differently, this is a pointer |
106 | * that storage. It must be setup at session creation time. | 109 | * and length to that storage. It must be setup at session |
110 | * creation time. | ||
107 | */ | 111 | */ |
108 | struct iscsi_cmd *hdr; | 112 | struct iscsi_cmd *hdr; |
113 | unsigned short hdr_max; | ||
114 | unsigned short hdr_len; /* accumulated size of hdr used */ | ||
109 | int itt; /* this ITT */ | 115 | int itt; /* this ITT */ |
110 | 116 | ||
111 | uint32_t unsol_datasn; | 117 | uint32_t unsol_datasn; |
@@ -124,6 +130,11 @@ struct iscsi_cmd_task { | |||
124 | void *dd_data; /* driver/transport data */ | 130 | void *dd_data; /* driver/transport data */ |
125 | }; | 131 | }; |
126 | 132 | ||
133 | static inline void* iscsi_next_hdr(struct iscsi_cmd_task *ctask) | ||
134 | { | ||
135 | return (void*)ctask->hdr + ctask->hdr_len; | ||
136 | } | ||
137 | |||
127 | struct iscsi_conn { | 138 | struct iscsi_conn { |
128 | struct iscsi_cls_conn *cls_conn; /* ptr to class connection */ | 139 | struct iscsi_cls_conn *cls_conn; /* ptr to class connection */ |
129 | void *dd_data; /* iscsi_transport data */ | 140 | void *dd_data; /* iscsi_transport data */ |
@@ -342,4 +353,22 @@ extern void iscsi_requeue_ctask(struct iscsi_cmd_task *ctask); | |||
342 | extern void iscsi_pool_free(struct iscsi_queue *, void **); | 353 | extern void iscsi_pool_free(struct iscsi_queue *, void **); |
343 | extern int iscsi_pool_init(struct iscsi_queue *, int, void ***, int); | 354 | extern int iscsi_pool_init(struct iscsi_queue *, int, void ***, int); |
344 | 355 | ||
356 | /* | ||
357 | * inline functions to deal with padding. | ||
358 | */ | ||
359 | static inline unsigned int | ||
360 | iscsi_padded(unsigned int len) | ||
361 | { | ||
362 | return (len + ISCSI_PAD_LEN - 1) & ~(ISCSI_PAD_LEN - 1); | ||
363 | } | ||
364 | |||
365 | static inline unsigned int | ||
366 | iscsi_padding(unsigned int len) | ||
367 | { | ||
368 | len &= (ISCSI_PAD_LEN - 1); | ||
369 | if (len) | ||
370 | len = ISCSI_PAD_LEN - len; | ||
371 | return len; | ||
372 | } | ||
373 | |||
345 | #endif | 374 | #endif |