diff options
author | Olaf Kirch <olaf.kirch@oracle.com> | 2007-12-13 13:43:21 -0500 |
---|---|---|
committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2008-01-11 19:28:20 -0500 |
commit | da32dd681f7a1a17073c42b375fc23cf73c92155 (patch) | |
tree | 5bdca9ef0806fbf09b13d2fc272cda24702f482c | |
parent | 843c0a8a76078cf961b244b839683d0667313740 (diff) |
[SCSI] iscsi_tcp: rewrite recv path
Rewrite recv path. Fixes:
- data digest processing and error handling.
- ahs support.
Some fixups by Mike Christie
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 | 1018 | ||||
-rw-r--r-- | drivers/scsi/iscsi_tcp.h | 66 | ||||
-rw-r--r-- | include/scsi/libiscsi.h | 4 |
3 files changed, 552 insertions, 536 deletions
diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c index 4b226b88b68a..1b540e03f5bf 100644 --- a/drivers/scsi/iscsi_tcp.c +++ b/drivers/scsi/iscsi_tcp.c | |||
@@ -48,7 +48,7 @@ MODULE_AUTHOR("Dmitry Yusupov <dmitry_yus@yahoo.com>, " | |||
48 | "Alex Aizman <itn780@yahoo.com>"); | 48 | "Alex Aizman <itn780@yahoo.com>"); |
49 | MODULE_DESCRIPTION("iSCSI/TCP data-path"); | 49 | MODULE_DESCRIPTION("iSCSI/TCP data-path"); |
50 | MODULE_LICENSE("GPL"); | 50 | MODULE_LICENSE("GPL"); |
51 | /* #define DEBUG_TCP */ | 51 | #undef DEBUG_TCP |
52 | #define DEBUG_ASSERT | 52 | #define DEBUG_ASSERT |
53 | 53 | ||
54 | #ifdef DEBUG_TCP | 54 | #ifdef DEBUG_TCP |
@@ -67,10 +67,15 @@ MODULE_LICENSE("GPL"); | |||
67 | static unsigned int iscsi_max_lun = 512; | 67 | static unsigned int iscsi_max_lun = 512; |
68 | module_param_named(max_lun, iscsi_max_lun, uint, S_IRUGO); | 68 | module_param_named(max_lun, iscsi_max_lun, uint, S_IRUGO); |
69 | 69 | ||
70 | static int iscsi_tcp_hdr_recv_done(struct iscsi_tcp_conn *tcp_conn, | ||
71 | struct iscsi_chunk *chunk); | ||
72 | |||
70 | static inline void | 73 | static inline void |
71 | iscsi_buf_init_iov(struct iscsi_buf *ibuf, char *vbuf, int size) | 74 | iscsi_buf_init_iov(struct iscsi_buf *ibuf, char *vbuf, int size) |
72 | { | 75 | { |
73 | sg_init_one(&ibuf->sg, vbuf, size); | 76 | ibuf->sg.page = virt_to_page(vbuf); |
77 | ibuf->sg.offset = offset_in_page(vbuf); | ||
78 | ibuf->sg.length = size; | ||
74 | ibuf->sent = 0; | 79 | ibuf->sent = 0; |
75 | ibuf->use_sendmsg = 1; | 80 | ibuf->use_sendmsg = 1; |
76 | } | 81 | } |
@@ -78,12 +83,13 @@ iscsi_buf_init_iov(struct iscsi_buf *ibuf, char *vbuf, int size) | |||
78 | static inline void | 83 | static inline void |
79 | iscsi_buf_init_sg(struct iscsi_buf *ibuf, struct scatterlist *sg) | 84 | iscsi_buf_init_sg(struct iscsi_buf *ibuf, struct scatterlist *sg) |
80 | { | 85 | { |
81 | sg_init_table(&ibuf->sg, 1); | 86 | ibuf->sg.page = sg->page; |
82 | sg_set_page(&ibuf->sg, sg_page(sg), sg->length, sg->offset); | 87 | ibuf->sg.offset = sg->offset; |
88 | ibuf->sg.length = sg->length; | ||
83 | /* | 89 | /* |
84 | * Fastpath: sg element fits into single page | 90 | * Fastpath: sg element fits into single page |
85 | */ | 91 | */ |
86 | if (sg->length + sg->offset <= PAGE_SIZE && !PageSlab(sg_page(sg))) | 92 | if (sg->length + sg->offset <= PAGE_SIZE && !PageSlab(sg->page)) |
87 | ibuf->use_sendmsg = 0; | 93 | ibuf->use_sendmsg = 0; |
88 | else | 94 | else |
89 | ibuf->use_sendmsg = 1; | 95 | ibuf->use_sendmsg = 1; |
@@ -110,72 +116,331 @@ iscsi_hdr_digest(struct iscsi_conn *conn, struct iscsi_buf *buf, | |||
110 | buf->sg.length += sizeof(u32); | 116 | buf->sg.length += sizeof(u32); |
111 | } | 117 | } |
112 | 118 | ||
119 | /* | ||
120 | * Scatterlist handling: inside the iscsi_chunk, we | ||
121 | * remember an index into the scatterlist, and set data/size | ||
122 | * to the current scatterlist entry. For highmem pages, we | ||
123 | * kmap as needed. | ||
124 | * | ||
125 | * Note that the page is unmapped when we return from | ||
126 | * TCP's data_ready handler, so we may end up mapping and | ||
127 | * unmapping the same page repeatedly. The whole reason | ||
128 | * for this is that we shouldn't keep the page mapped | ||
129 | * outside the softirq. | ||
130 | */ | ||
131 | |||
132 | /** | ||
133 | * iscsi_tcp_chunk_init_sg - init indicated scatterlist entry | ||
134 | * @chunk: the buffer object | ||
135 | * @idx: index into scatterlist | ||
136 | * @offset: byte offset into that sg entry | ||
137 | * | ||
138 | * This function sets up the chunk so that subsequent | ||
139 | * data is copied to the indicated sg entry, at the given | ||
140 | * offset. | ||
141 | */ | ||
142 | static inline void | ||
143 | iscsi_tcp_chunk_init_sg(struct iscsi_chunk *chunk, | ||
144 | unsigned int idx, unsigned int offset) | ||
145 | { | ||
146 | struct scatterlist *sg; | ||
147 | |||
148 | BUG_ON(chunk->sg == NULL); | ||
149 | |||
150 | sg = &chunk->sg[idx]; | ||
151 | chunk->sg_index = idx; | ||
152 | chunk->sg_offset = offset; | ||
153 | chunk->size = min(sg->length - offset, chunk->total_size); | ||
154 | chunk->data = NULL; | ||
155 | } | ||
156 | |||
157 | /** | ||
158 | * iscsi_tcp_chunk_map - map the current S/G page | ||
159 | * @chunk: iscsi chunk | ||
160 | * | ||
161 | * We only need to possibly kmap data if scatter lists are being used, | ||
162 | * because the iscsi passthrough and internal IO paths will never use high | ||
163 | * mem pages. | ||
164 | */ | ||
165 | static inline void | ||
166 | iscsi_tcp_chunk_map(struct iscsi_chunk *chunk) | ||
167 | { | ||
168 | struct scatterlist *sg; | ||
169 | |||
170 | if (chunk->data != NULL || !chunk->sg) | ||
171 | return; | ||
172 | |||
173 | sg = &chunk->sg[chunk->sg_index]; | ||
174 | BUG_ON(chunk->sg_mapped); | ||
175 | BUG_ON(sg->length == 0); | ||
176 | chunk->sg_mapped = kmap_atomic(sg->page, KM_SOFTIRQ0); | ||
177 | chunk->data = chunk->sg_mapped + sg->offset + chunk->sg_offset; | ||
178 | } | ||
179 | |||
180 | static inline void | ||
181 | iscsi_tcp_chunk_unmap(struct iscsi_chunk *chunk) | ||
182 | { | ||
183 | if (chunk->sg_mapped) { | ||
184 | kunmap_atomic(chunk->sg_mapped, KM_SOFTIRQ0); | ||
185 | chunk->sg_mapped = NULL; | ||
186 | chunk->data = NULL; | ||
187 | } | ||
188 | } | ||
189 | |||
190 | /* | ||
191 | * Splice the digest buffer into the buffer | ||
192 | */ | ||
193 | static inline void | ||
194 | iscsi_tcp_chunk_splice_digest(struct iscsi_chunk *chunk, void *digest) | ||
195 | { | ||
196 | chunk->data = digest; | ||
197 | chunk->digest_len = ISCSI_DIGEST_SIZE; | ||
198 | chunk->total_size += ISCSI_DIGEST_SIZE; | ||
199 | chunk->size = ISCSI_DIGEST_SIZE; | ||
200 | chunk->copied = 0; | ||
201 | chunk->sg = NULL; | ||
202 | chunk->sg_index = 0; | ||
203 | chunk->hash = NULL; | ||
204 | } | ||
205 | |||
206 | /** | ||
207 | * iscsi_tcp_chunk_done - check whether the chunk is complete | ||
208 | * @chunk: iscsi chunk to check | ||
209 | * | ||
210 | * Check if we're done receiving this chunk. If the receive | ||
211 | * buffer is full but we expect more data, move on to the | ||
212 | * next entry in the scatterlist. | ||
213 | * | ||
214 | * If the amount of data we received isn't a multiple of 4, | ||
215 | * we will transparently receive the pad bytes, too. | ||
216 | * | ||
217 | * This function must be re-entrant. | ||
218 | */ | ||
113 | static inline int | 219 | static inline int |
114 | iscsi_hdr_extract(struct iscsi_tcp_conn *tcp_conn) | 220 | iscsi_tcp_chunk_done(struct iscsi_chunk *chunk) |
115 | { | 221 | { |
116 | struct sk_buff *skb = tcp_conn->in.skb; | 222 | static unsigned char padbuf[ISCSI_PAD_LEN]; |
223 | |||
224 | if (chunk->copied < chunk->size) { | ||
225 | iscsi_tcp_chunk_map(chunk); | ||
226 | return 0; | ||
227 | } | ||
117 | 228 | ||
118 | tcp_conn->in.zero_copy_hdr = 0; | 229 | chunk->total_copied += chunk->copied; |
230 | chunk->copied = 0; | ||
231 | chunk->size = 0; | ||
119 | 232 | ||
120 | if (tcp_conn->in.copy >= tcp_conn->hdr_size && | 233 | /* Unmap the current scatterlist page, if there is one. */ |
121 | tcp_conn->in_progress == IN_PROGRESS_WAIT_HEADER) { | 234 | iscsi_tcp_chunk_unmap(chunk); |
122 | /* | 235 | |
123 | * Zero-copy PDU Header: using connection context | 236 | /* Do we have more scatterlist entries? */ |
124 | * to store header pointer. | 237 | if (chunk->total_copied < chunk->total_size) { |
125 | */ | 238 | /* Proceed to the next entry in the scatterlist. */ |
126 | if (skb_shinfo(skb)->frag_list == NULL && | 239 | iscsi_tcp_chunk_init_sg(chunk, chunk->sg_index + 1, 0); |
127 | !skb_shinfo(skb)->nr_frags) { | 240 | iscsi_tcp_chunk_map(chunk); |
128 | tcp_conn->in.hdr = (struct iscsi_hdr *) | 241 | BUG_ON(chunk->size == 0); |
129 | ((char*)skb->data + tcp_conn->in.offset); | 242 | return 0; |
130 | tcp_conn->in.zero_copy_hdr = 1; | 243 | } |
131 | } else { | 244 | |
132 | /* ignoring return code since we checked | 245 | /* Do we need to handle padding? */ |
133 | * in.copy before */ | 246 | if (chunk->total_copied & (ISCSI_PAD_LEN-1)) { |
134 | skb_copy_bits(skb, tcp_conn->in.offset, | 247 | unsigned int pad; |
135 | &tcp_conn->hdr, tcp_conn->hdr_size); | 248 | |
136 | tcp_conn->in.hdr = &tcp_conn->hdr; | 249 | pad = ISCSI_PAD_LEN - (chunk->total_copied & (ISCSI_PAD_LEN-1)); |
250 | debug_tcp("consume %d pad bytes\n", pad); | ||
251 | chunk->total_size += pad; | ||
252 | chunk->size = pad; | ||
253 | chunk->data = padbuf; | ||
254 | return 0; | ||
255 | } | ||
256 | |||
257 | /* | ||
258 | * Set us up for receiving the data digest. hdr digest | ||
259 | * is completely handled in hdr done function. | ||
260 | */ | ||
261 | if (chunk->hash) { | ||
262 | if (chunk->digest_len == 0) { | ||
263 | crypto_hash_final(chunk->hash, chunk->digest); | ||
264 | iscsi_tcp_chunk_splice_digest(chunk, | ||
265 | chunk->recv_digest); | ||
266 | return 0; | ||
137 | } | 267 | } |
138 | tcp_conn->in.offset += tcp_conn->hdr_size; | 268 | } |
139 | tcp_conn->in.copy -= tcp_conn->hdr_size; | ||
140 | } else { | ||
141 | int hdr_remains; | ||
142 | int copylen; | ||
143 | 269 | ||
144 | /* | 270 | return 1; |
145 | * PDU header scattered across SKB's, | 271 | } |
146 | * copying it... This'll happen quite rarely. | 272 | |
147 | */ | 273 | /** |
274 | * iscsi_tcp_chunk_recv - copy data to chunk | ||
275 | * @tcp_conn: the iSCSI TCP connection | ||
276 | * @chunk: the buffer to copy to | ||
277 | * @ptr: data pointer | ||
278 | * @len: amount of data available | ||
279 | * | ||
280 | * This function copies up to @len bytes to the | ||
281 | * given buffer, and returns the number of bytes | ||
282 | * consumed, which can actually be less than @len. | ||
283 | * | ||
284 | * If hash digest is enabled, the function will update the | ||
285 | * hash while copying. | ||
286 | * Combining these two operations doesn't buy us a lot (yet), | ||
287 | * but in the future we could implement combined copy+crc, | ||
288 | * just way we do for network layer checksums. | ||
289 | */ | ||
290 | static int | ||
291 | iscsi_tcp_chunk_recv(struct iscsi_tcp_conn *tcp_conn, | ||
292 | struct iscsi_chunk *chunk, const void *ptr, | ||
293 | unsigned int len) | ||
294 | { | ||
295 | struct scatterlist sg; | ||
296 | unsigned int copy, copied = 0; | ||
297 | |||
298 | while (!iscsi_tcp_chunk_done(chunk)) { | ||
299 | if (copied == len) | ||
300 | goto out; | ||
301 | |||
302 | copy = min(len - copied, chunk->size - chunk->copied); | ||
303 | memcpy(chunk->data + chunk->copied, ptr + copied, copy); | ||
304 | |||
305 | if (chunk->hash) { | ||
306 | sg_init_one(&sg, ptr + copied, copy); | ||
307 | crypto_hash_update(chunk->hash, &sg, copy); | ||
308 | } | ||
309 | chunk->copied += copy; | ||
310 | copied += copy; | ||
311 | } | ||
312 | |||
313 | out: | ||
314 | return copied; | ||
315 | } | ||
148 | 316 | ||
149 | if (tcp_conn->in_progress == IN_PROGRESS_WAIT_HEADER) | 317 | static inline void |
150 | tcp_conn->in.hdr_offset = 0; | 318 | iscsi_tcp_dgst_header(struct hash_desc *hash, const void *hdr, size_t hdrlen, |
319 | unsigned char digest[ISCSI_DIGEST_SIZE]) | ||
320 | { | ||
321 | struct scatterlist sg; | ||
322 | |||
323 | sg_init_one(&sg, hdr, hdrlen); | ||
324 | crypto_hash_digest(hash, &sg, hdrlen, digest); | ||
325 | } | ||
326 | |||
327 | static inline int | ||
328 | iscsi_tcp_dgst_verify(struct iscsi_tcp_conn *tcp_conn, | ||
329 | struct iscsi_chunk *chunk) | ||
330 | { | ||
331 | if (!chunk->digest_len) | ||
332 | return 1; | ||
333 | |||
334 | if (memcmp(chunk->recv_digest, chunk->digest, chunk->digest_len)) { | ||
335 | debug_scsi("digest mismatch\n"); | ||
336 | return 0; | ||
337 | } | ||
151 | 338 | ||
152 | hdr_remains = tcp_conn->hdr_size - tcp_conn->in.hdr_offset; | 339 | return 1; |
153 | BUG_ON(hdr_remains <= 0); | 340 | } |
154 | 341 | ||
155 | copylen = min(tcp_conn->in.copy, hdr_remains); | 342 | /* |
156 | skb_copy_bits(skb, tcp_conn->in.offset, | 343 | * Helper function to set up chunk buffer |
157 | (char*)&tcp_conn->hdr + tcp_conn->in.hdr_offset, | 344 | */ |
158 | copylen); | 345 | static inline void |
346 | __iscsi_chunk_init(struct iscsi_chunk *chunk, size_t size, | ||
347 | iscsi_chunk_done_fn_t *done, struct hash_desc *hash) | ||
348 | { | ||
349 | memset(chunk, 0, sizeof(*chunk)); | ||
350 | chunk->total_size = size; | ||
351 | chunk->done = done; | ||
159 | 352 | ||
160 | debug_tcp("PDU gather offset %d bytes %d in.offset %d " | 353 | if (hash) { |
161 | "in.copy %d\n", tcp_conn->in.hdr_offset, copylen, | 354 | chunk->hash = hash; |
162 | tcp_conn->in.offset, tcp_conn->in.copy); | 355 | crypto_hash_init(hash); |
356 | } | ||
357 | } | ||
163 | 358 | ||
164 | tcp_conn->in.offset += copylen; | 359 | static inline void |
165 | tcp_conn->in.copy -= copylen; | 360 | iscsi_chunk_init_linear(struct iscsi_chunk *chunk, void *data, size_t size, |
166 | if (copylen < hdr_remains) { | 361 | iscsi_chunk_done_fn_t *done, struct hash_desc *hash) |
167 | tcp_conn->in_progress = IN_PROGRESS_HEADER_GATHER; | 362 | { |
168 | tcp_conn->in.hdr_offset += copylen; | 363 | __iscsi_chunk_init(chunk, size, done, hash); |
169 | return -EAGAIN; | 364 | chunk->data = data; |
365 | chunk->size = size; | ||
366 | } | ||
367 | |||
368 | static inline int | ||
369 | iscsi_chunk_seek_sg(struct iscsi_chunk *chunk, | ||
370 | struct scatterlist *sg, unsigned int sg_count, | ||
371 | unsigned int offset, size_t size, | ||
372 | iscsi_chunk_done_fn_t *done, struct hash_desc *hash) | ||
373 | { | ||
374 | unsigned int i; | ||
375 | |||
376 | __iscsi_chunk_init(chunk, size, done, hash); | ||
377 | for (i = 0; i < sg_count; ++i) { | ||
378 | if (offset < sg[i].length) { | ||
379 | chunk->sg = sg; | ||
380 | chunk->sg_count = sg_count; | ||
381 | iscsi_tcp_chunk_init_sg(chunk, i, offset); | ||
382 | return 0; | ||
170 | } | 383 | } |
171 | tcp_conn->in.hdr = &tcp_conn->hdr; | 384 | offset -= sg[i].length; |
172 | tcp_conn->discontiguous_hdr_cnt++; | ||
173 | tcp_conn->in_progress = IN_PROGRESS_WAIT_HEADER; | ||
174 | } | 385 | } |
175 | 386 | ||
387 | return ISCSI_ERR_DATA_OFFSET; | ||
388 | } | ||
389 | |||
390 | /** | ||
391 | * iscsi_tcp_hdr_recv_prep - prep chunk for hdr reception | ||
392 | * @tcp_conn: iscsi connection to prep for | ||
393 | * | ||
394 | * This function always passes NULL for the hash argument, because when this | ||
395 | * function is called we do not yet know the final size of the header and want | ||
396 | * to delay the digest processing until we know that. | ||
397 | */ | ||
398 | static void | ||
399 | iscsi_tcp_hdr_recv_prep(struct iscsi_tcp_conn *tcp_conn) | ||
400 | { | ||
401 | debug_tcp("iscsi_tcp_hdr_recv_prep(%p%s)\n", tcp_conn, | ||
402 | tcp_conn->iscsi_conn->hdrdgst_en ? ", digest enabled" : ""); | ||
403 | iscsi_chunk_init_linear(&tcp_conn->in.chunk, | ||
404 | tcp_conn->in.hdr_buf, sizeof(struct iscsi_hdr), | ||
405 | iscsi_tcp_hdr_recv_done, NULL); | ||
406 | } | ||
407 | |||
408 | /* | ||
409 | * Handle incoming reply to any other type of command | ||
410 | */ | ||
411 | static int | ||
412 | iscsi_tcp_data_recv_done(struct iscsi_tcp_conn *tcp_conn, | ||
413 | struct iscsi_chunk *chunk) | ||
414 | { | ||
415 | struct iscsi_conn *conn = tcp_conn->iscsi_conn; | ||
416 | int rc = 0; | ||
417 | |||
418 | if (!iscsi_tcp_dgst_verify(tcp_conn, chunk)) | ||
419 | return ISCSI_ERR_DATA_DGST; | ||
420 | |||
421 | rc = iscsi_complete_pdu(conn, tcp_conn->in.hdr, | ||
422 | conn->data, tcp_conn->in.datalen); | ||
423 | if (rc) | ||
424 | return rc; | ||
425 | |||
426 | iscsi_tcp_hdr_recv_prep(tcp_conn); | ||
176 | return 0; | 427 | return 0; |
177 | } | 428 | } |
178 | 429 | ||
430 | static void | ||
431 | iscsi_tcp_data_recv_prep(struct iscsi_tcp_conn *tcp_conn) | ||
432 | { | ||
433 | struct iscsi_conn *conn = tcp_conn->iscsi_conn; | ||
434 | struct hash_desc *rx_hash = NULL; | ||
435 | |||
436 | if (conn->datadgst_en) | ||
437 | rx_hash = &tcp_conn->rx_hash; | ||
438 | |||
439 | iscsi_chunk_init_linear(&tcp_conn->in.chunk, | ||
440 | conn->data, tcp_conn->in.datalen, | ||
441 | iscsi_tcp_data_recv_done, rx_hash); | ||
442 | } | ||
443 | |||
179 | /* | 444 | /* |
180 | * must be called with session lock | 445 | * must be called with session lock |
181 | */ | 446 | */ |
@@ -417,16 +682,49 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) | |||
417 | return 0; | 682 | return 0; |
418 | } | 683 | } |
419 | 684 | ||
685 | /* | ||
686 | * Handle incoming reply to DataIn command | ||
687 | */ | ||
420 | static int | 688 | static int |
421 | iscsi_tcp_hdr_recv(struct iscsi_conn *conn) | 689 | iscsi_tcp_process_data_in(struct iscsi_tcp_conn *tcp_conn, |
690 | struct iscsi_chunk *chunk) | ||
691 | { | ||
692 | struct iscsi_conn *conn = tcp_conn->iscsi_conn; | ||
693 | struct iscsi_hdr *hdr = tcp_conn->in.hdr; | ||
694 | int rc; | ||
695 | |||
696 | if (!iscsi_tcp_dgst_verify(tcp_conn, chunk)) | ||
697 | return ISCSI_ERR_DATA_DGST; | ||
698 | |||
699 | /* check for non-exceptional status */ | ||
700 | if (hdr->flags & ISCSI_FLAG_DATA_STATUS) { | ||
701 | rc = iscsi_complete_pdu(conn, tcp_conn->in.hdr, NULL, 0); | ||
702 | if (rc) | ||
703 | return rc; | ||
704 | } | ||
705 | |||
706 | iscsi_tcp_hdr_recv_prep(tcp_conn); | ||
707 | return 0; | ||
708 | } | ||
709 | |||
710 | /** | ||
711 | * iscsi_tcp_hdr_dissect - process PDU header | ||
712 | * @conn: iSCSI connection | ||
713 | * @hdr: PDU header | ||
714 | * | ||
715 | * This function analyzes the header of the PDU received, | ||
716 | * and performs several sanity checks. If the PDU is accompanied | ||
717 | * by data, the receive buffer is set up to copy the incoming data | ||
718 | * to the correct location. | ||
719 | */ | ||
720 | static int | ||
721 | iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) | ||
422 | { | 722 | { |
423 | int rc = 0, opcode, ahslen; | 723 | int rc = 0, opcode, ahslen; |
424 | struct iscsi_hdr *hdr; | ||
425 | struct iscsi_session *session = conn->session; | 724 | struct iscsi_session *session = conn->session; |
426 | struct iscsi_tcp_conn *tcp_conn = conn->dd_data; | 725 | struct iscsi_tcp_conn *tcp_conn = conn->dd_data; |
427 | uint32_t cdgst, rdgst = 0, itt; | 726 | struct iscsi_cmd_task *ctask; |
428 | 727 | uint32_t itt; | |
429 | hdr = tcp_conn->in.hdr; | ||
430 | 728 | ||
431 | /* verify PDU length */ | 729 | /* verify PDU length */ |
432 | tcp_conn->in.datalen = ntoh24(hdr->dlength); | 730 | tcp_conn->in.datalen = ntoh24(hdr->dlength); |
@@ -435,77 +733,72 @@ iscsi_tcp_hdr_recv(struct iscsi_conn *conn) | |||
435 | tcp_conn->in.datalen, conn->max_recv_dlength); | 733 | tcp_conn->in.datalen, conn->max_recv_dlength); |
436 | return ISCSI_ERR_DATALEN; | 734 | return ISCSI_ERR_DATALEN; |
437 | } | 735 | } |
438 | tcp_conn->data_copied = 0; | ||
439 | 736 | ||
440 | /* read AHS */ | 737 | /* Additional header segments. So far, we don't |
738 | * process additional headers. | ||
739 | */ | ||
441 | ahslen = hdr->hlength << 2; | 740 | ahslen = hdr->hlength << 2; |
442 | tcp_conn->in.offset += ahslen; | ||
443 | tcp_conn->in.copy -= ahslen; | ||
444 | if (tcp_conn->in.copy < 0) { | ||
445 | printk(KERN_ERR "iscsi_tcp: can't handle AHS with length " | ||
446 | "%d bytes\n", ahslen); | ||
447 | return ISCSI_ERR_AHSLEN; | ||
448 | } | ||
449 | |||
450 | /* calculate read padding */ | ||
451 | tcp_conn->in.padding = tcp_conn->in.datalen & (ISCSI_PAD_LEN-1); | ||
452 | if (tcp_conn->in.padding) { | ||
453 | tcp_conn->in.padding = ISCSI_PAD_LEN - tcp_conn->in.padding; | ||
454 | debug_scsi("read padding %d bytes\n", tcp_conn->in.padding); | ||
455 | } | ||
456 | |||
457 | if (conn->hdrdgst_en) { | ||
458 | struct scatterlist sg; | ||
459 | |||
460 | sg_init_one(&sg, (u8 *)hdr, | ||
461 | sizeof(struct iscsi_hdr) + ahslen); | ||
462 | crypto_hash_digest(&tcp_conn->rx_hash, &sg, sg.length, | ||
463 | (u8 *)&cdgst); | ||
464 | rdgst = *(uint32_t*)((char*)hdr + sizeof(struct iscsi_hdr) + | ||
465 | ahslen); | ||
466 | if (cdgst != rdgst) { | ||
467 | printk(KERN_ERR "iscsi_tcp: hdrdgst error " | ||
468 | "recv 0x%x calc 0x%x\n", rdgst, cdgst); | ||
469 | return ISCSI_ERR_HDR_DGST; | ||
470 | } | ||
471 | } | ||
472 | 741 | ||
473 | opcode = hdr->opcode & ISCSI_OPCODE_MASK; | 742 | opcode = hdr->opcode & ISCSI_OPCODE_MASK; |
474 | /* verify itt (itt encoding: age+cid+itt) */ | 743 | /* verify itt (itt encoding: age+cid+itt) */ |
475 | rc = iscsi_verify_itt(conn, hdr, &itt); | 744 | rc = iscsi_verify_itt(conn, hdr, &itt); |
476 | if (rc == ISCSI_ERR_NO_SCSI_CMD) { | 745 | if (rc == ISCSI_ERR_NO_SCSI_CMD) { |
746 | /* XXX: what does this do? */ | ||
477 | tcp_conn->in.datalen = 0; /* force drop */ | 747 | tcp_conn->in.datalen = 0; /* force drop */ |
478 | return 0; | 748 | return 0; |
479 | } else if (rc) | 749 | } else if (rc) |
480 | return rc; | 750 | return rc; |
481 | 751 | ||
482 | debug_tcp("opcode 0x%x offset %d copy %d ahslen %d datalen %d\n", | 752 | debug_tcp("opcode 0x%x ahslen %d datalen %d\n", |
483 | opcode, tcp_conn->in.offset, tcp_conn->in.copy, | 753 | opcode, ahslen, tcp_conn->in.datalen); |
484 | ahslen, tcp_conn->in.datalen); | ||
485 | 754 | ||
486 | switch(opcode) { | 755 | switch(opcode) { |
487 | case ISCSI_OP_SCSI_DATA_IN: | 756 | case ISCSI_OP_SCSI_DATA_IN: |
488 | tcp_conn->in.ctask = session->cmds[itt]; | 757 | ctask = session->cmds[itt]; |
489 | rc = iscsi_data_rsp(conn, tcp_conn->in.ctask); | 758 | rc = iscsi_data_rsp(conn, ctask); |
490 | if (rc) | 759 | if (rc) |
491 | return rc; | 760 | return rc; |
761 | if (tcp_conn->in.datalen) { | ||
762 | struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; | ||
763 | struct hash_desc *rx_hash = NULL; | ||
764 | |||
765 | /* | ||
766 | * Setup copy of Data-In into the Scsi_Cmnd | ||
767 | * Scatterlist case: | ||
768 | * We set up the iscsi_chunk to point to the next | ||
769 | * scatterlist entry to copy to. As we go along, | ||
770 | * we move on to the next scatterlist entry and | ||
771 | * update the digest per-entry. | ||
772 | */ | ||
773 | if (conn->datadgst_en) | ||
774 | rx_hash = &tcp_conn->rx_hash; | ||
775 | |||
776 | debug_tcp("iscsi_tcp_begin_data_in(%p, offset=%d, " | ||
777 | "datalen=%d)\n", tcp_conn, | ||
778 | tcp_ctask->data_offset, | ||
779 | tcp_conn->in.datalen); | ||
780 | return iscsi_chunk_seek_sg(&tcp_conn->in.chunk, | ||
781 | scsi_sglist(ctask->sc), | ||
782 | scsi_sg_count(ctask->sc), | ||
783 | tcp_ctask->data_offset, | ||
784 | tcp_conn->in.datalen, | ||
785 | iscsi_tcp_process_data_in, | ||
786 | rx_hash); | ||
787 | } | ||
492 | /* fall through */ | 788 | /* fall through */ |
493 | case ISCSI_OP_SCSI_CMD_RSP: | 789 | case ISCSI_OP_SCSI_CMD_RSP: |
494 | tcp_conn->in.ctask = session->cmds[itt]; | 790 | if (tcp_conn->in.datalen) { |
495 | if (tcp_conn->in.datalen) | 791 | iscsi_tcp_data_recv_prep(tcp_conn); |
496 | goto copy_hdr; | 792 | return 0; |
497 | 793 | } | |
498 | spin_lock(&session->lock); | 794 | rc = iscsi_complete_pdu(conn, hdr, NULL, 0); |
499 | rc = __iscsi_complete_pdu(conn, hdr, NULL, 0); | ||
500 | spin_unlock(&session->lock); | ||
501 | break; | 795 | break; |
502 | case ISCSI_OP_R2T: | 796 | case ISCSI_OP_R2T: |
503 | tcp_conn->in.ctask = session->cmds[itt]; | 797 | ctask = session->cmds[itt]; |
504 | if (ahslen) | 798 | if (ahslen) |
505 | rc = ISCSI_ERR_AHSLEN; | 799 | rc = ISCSI_ERR_AHSLEN; |
506 | else if (tcp_conn->in.ctask->sc->sc_data_direction == | 800 | else if (ctask->sc->sc_data_direction == DMA_TO_DEVICE) |
507 | DMA_TO_DEVICE) | 801 | rc = iscsi_r2t_rsp(conn, ctask); |
508 | rc = iscsi_r2t_rsp(conn, tcp_conn->in.ctask); | ||
509 | else | 802 | else |
510 | rc = ISCSI_ERR_PROTO; | 803 | rc = ISCSI_ERR_PROTO; |
511 | break; | 804 | break; |
@@ -518,8 +811,7 @@ iscsi_tcp_hdr_recv(struct iscsi_conn *conn) | |||
518 | * than 8K, but there are no targets that currently do this. | 811 | * than 8K, but there are no targets that currently do this. |
519 | * For now we fail until we find a vendor that needs it | 812 | * For now we fail until we find a vendor that needs it |
520 | */ | 813 | */ |
521 | if (ISCSI_DEF_MAX_RECV_SEG_LEN < | 814 | if (ISCSI_DEF_MAX_RECV_SEG_LEN < tcp_conn->in.datalen) { |
522 | tcp_conn->in.datalen) { | ||
523 | printk(KERN_ERR "iscsi_tcp: received buffer of len %u " | 815 | printk(KERN_ERR "iscsi_tcp: received buffer of len %u " |
524 | "but conn buffer is only %u (opcode %0x)\n", | 816 | "but conn buffer is only %u (opcode %0x)\n", |
525 | tcp_conn->in.datalen, | 817 | tcp_conn->in.datalen, |
@@ -528,8 +820,13 @@ iscsi_tcp_hdr_recv(struct iscsi_conn *conn) | |||
528 | break; | 820 | break; |
529 | } | 821 | } |
530 | 822 | ||
531 | if (tcp_conn->in.datalen) | 823 | /* If there's data coming in with the response, |
532 | goto copy_hdr; | 824 | * receive it to the connection's buffer. |
825 | */ | ||
826 | if (tcp_conn->in.datalen) { | ||
827 | iscsi_tcp_data_recv_prep(tcp_conn); | ||
828 | return 0; | ||
829 | } | ||
533 | /* fall through */ | 830 | /* fall through */ |
534 | case ISCSI_OP_LOGOUT_RSP: | 831 | case ISCSI_OP_LOGOUT_RSP: |
535 | case ISCSI_OP_NOOP_IN: | 832 | case ISCSI_OP_NOOP_IN: |
@@ -541,129 +838,15 @@ iscsi_tcp_hdr_recv(struct iscsi_conn *conn) | |||
541 | break; | 838 | break; |
542 | } | 839 | } |
543 | 840 | ||
544 | return rc; | 841 | if (rc == 0) { |
545 | 842 | /* Anything that comes with data should have | |
546 | copy_hdr: | 843 | * been handled above. */ |
547 | /* | 844 | if (tcp_conn->in.datalen) |
548 | * if we did zero copy for the header but we will need multiple | 845 | return ISCSI_ERR_PROTO; |
549 | * skbs to complete the command then we have to copy the header | 846 | iscsi_tcp_hdr_recv_prep(tcp_conn); |
550 | * for later use | ||
551 | */ | ||
552 | if (tcp_conn->in.zero_copy_hdr && tcp_conn->in.copy <= | ||
553 | (tcp_conn->in.datalen + tcp_conn->in.padding + | ||
554 | (conn->datadgst_en ? 4 : 0))) { | ||
555 | debug_tcp("Copying header for later use. in.copy %d in.datalen" | ||
556 | " %d\n", tcp_conn->in.copy, tcp_conn->in.datalen); | ||
557 | memcpy(&tcp_conn->hdr, tcp_conn->in.hdr, | ||
558 | sizeof(struct iscsi_hdr)); | ||
559 | tcp_conn->in.hdr = &tcp_conn->hdr; | ||
560 | tcp_conn->in.zero_copy_hdr = 0; | ||
561 | } | ||
562 | return 0; | ||
563 | } | ||
564 | |||
565 | /** | ||
566 | * iscsi_ctask_copy - copy skb bits to the destanation cmd task | ||
567 | * @conn: iscsi tcp connection | ||
568 | * @ctask: scsi command task | ||
569 | * @buf: buffer to copy to | ||
570 | * @buf_size: size of buffer | ||
571 | * @offset: offset within the buffer | ||
572 | * | ||
573 | * Notes: | ||
574 | * The function calls skb_copy_bits() and updates per-connection and | ||
575 | * per-cmd byte counters. | ||
576 | * | ||
577 | * Read counters (in bytes): | ||
578 | * | ||
579 | * conn->in.offset offset within in progress SKB | ||
580 | * conn->in.copy left to copy from in progress SKB | ||
581 | * including padding | ||
582 | * conn->in.copied copied already from in progress SKB | ||
583 | * conn->data_copied copied already from in progress buffer | ||
584 | * ctask->sent total bytes sent up to the MidLayer | ||
585 | * ctask->data_count left to copy from in progress Data-In | ||
586 | * buf_left left to copy from in progress buffer | ||
587 | **/ | ||
588 | static inline int | ||
589 | iscsi_ctask_copy(struct iscsi_tcp_conn *tcp_conn, struct iscsi_cmd_task *ctask, | ||
590 | void *buf, int buf_size, int offset) | ||
591 | { | ||
592 | struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; | ||
593 | int buf_left = buf_size - (tcp_conn->data_copied + offset); | ||
594 | unsigned size = min(tcp_conn->in.copy, buf_left); | ||
595 | int rc; | ||
596 | |||
597 | size = min(size, ctask->data_count); | ||
598 | |||
599 | debug_tcp("ctask_copy %d bytes at offset %d copied %d\n", | ||
600 | size, tcp_conn->in.offset, tcp_conn->in.copied); | ||
601 | |||
602 | BUG_ON(size <= 0); | ||
603 | BUG_ON(tcp_ctask->sent + size > scsi_bufflen(ctask->sc)); | ||
604 | |||
605 | rc = skb_copy_bits(tcp_conn->in.skb, tcp_conn->in.offset, | ||
606 | (char*)buf + (offset + tcp_conn->data_copied), size); | ||
607 | /* must fit into skb->len */ | ||
608 | BUG_ON(rc); | ||
609 | |||
610 | tcp_conn->in.offset += size; | ||
611 | tcp_conn->in.copy -= size; | ||
612 | tcp_conn->in.copied += size; | ||
613 | tcp_conn->data_copied += size; | ||
614 | tcp_ctask->sent += size; | ||
615 | ctask->data_count -= size; | ||
616 | |||
617 | BUG_ON(tcp_conn->in.copy < 0); | ||
618 | BUG_ON(ctask->data_count < 0); | ||
619 | |||
620 | if (buf_size != (tcp_conn->data_copied + offset)) { | ||
621 | if (!ctask->data_count) { | ||
622 | BUG_ON(buf_size - tcp_conn->data_copied < 0); | ||
623 | /* done with this PDU */ | ||
624 | return buf_size - tcp_conn->data_copied; | ||
625 | } | ||
626 | return -EAGAIN; | ||
627 | } | 847 | } |
628 | 848 | ||
629 | /* done with this buffer or with both - PDU and buffer */ | 849 | return rc; |
630 | tcp_conn->data_copied = 0; | ||
631 | return 0; | ||
632 | } | ||
633 | |||
634 | /** | ||
635 | * iscsi_tcp_copy - copy skb bits to the destanation buffer | ||
636 | * @conn: iscsi tcp connection | ||
637 | * | ||
638 | * Notes: | ||
639 | * The function calls skb_copy_bits() and updates per-connection | ||
640 | * byte counters. | ||
641 | **/ | ||
642 | static inline int | ||
643 | iscsi_tcp_copy(struct iscsi_conn *conn, int buf_size) | ||
644 | { | ||
645 | struct iscsi_tcp_conn *tcp_conn = conn->dd_data; | ||
646 | int buf_left = buf_size - tcp_conn->data_copied; | ||
647 | int size = min(tcp_conn->in.copy, buf_left); | ||
648 | int rc; | ||
649 | |||
650 | debug_tcp("tcp_copy %d bytes at offset %d copied %d\n", | ||
651 | size, tcp_conn->in.offset, tcp_conn->data_copied); | ||
652 | BUG_ON(size <= 0); | ||
653 | |||
654 | rc = skb_copy_bits(tcp_conn->in.skb, tcp_conn->in.offset, | ||
655 | (char*)conn->data + tcp_conn->data_copied, size); | ||
656 | BUG_ON(rc); | ||
657 | |||
658 | tcp_conn->in.offset += size; | ||
659 | tcp_conn->in.copy -= size; | ||
660 | tcp_conn->in.copied += size; | ||
661 | tcp_conn->data_copied += size; | ||
662 | |||
663 | if (buf_size != tcp_conn->data_copied) | ||
664 | return -EAGAIN; | ||
665 | |||
666 | return 0; | ||
667 | } | 850 | } |
668 | 851 | ||
669 | static inline void | 852 | static inline void |
@@ -677,325 +860,146 @@ partial_sg_digest_update(struct hash_desc *desc, struct scatterlist *sg, | |||
677 | crypto_hash_update(desc, &temp, length); | 860 | crypto_hash_update(desc, &temp, length); |
678 | } | 861 | } |
679 | 862 | ||
680 | static void | 863 | /** |
681 | iscsi_recv_digest_update(struct iscsi_tcp_conn *tcp_conn, char* buf, int len) | 864 | * iscsi_tcp_hdr_recv_done - process PDU header |
682 | { | 865 | * |
683 | struct scatterlist tmp; | 866 | * This is the callback invoked when the PDU header has |
684 | 867 | * been received. If the header is followed by additional | |
685 | sg_init_one(&tmp, buf, len); | 868 | * header segments, we go back for more data. |
686 | crypto_hash_update(&tcp_conn->rx_hash, &tmp, len); | 869 | */ |
687 | } | 870 | static int |
688 | 871 | iscsi_tcp_hdr_recv_done(struct iscsi_tcp_conn *tcp_conn, | |
689 | static int iscsi_scsi_data_in(struct iscsi_conn *conn) | 872 | struct iscsi_chunk *chunk) |
690 | { | 873 | { |
691 | struct iscsi_tcp_conn *tcp_conn = conn->dd_data; | 874 | struct iscsi_conn *conn = tcp_conn->iscsi_conn; |
692 | struct iscsi_cmd_task *ctask = tcp_conn->in.ctask; | 875 | struct iscsi_hdr *hdr; |
693 | struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; | ||
694 | struct scsi_cmnd *sc = ctask->sc; | ||
695 | struct scatterlist *sg; | ||
696 | int i, offset, rc = 0; | ||
697 | |||
698 | BUG_ON((void*)ctask != sc->SCp.ptr); | ||
699 | |||
700 | offset = tcp_ctask->data_offset; | ||
701 | sg = scsi_sglist(sc); | ||
702 | |||
703 | if (tcp_ctask->data_offset) | ||
704 | for (i = 0; i < tcp_ctask->sg_count; i++) | ||
705 | offset -= sg[i].length; | ||
706 | /* we've passed through partial sg*/ | ||
707 | if (offset < 0) | ||
708 | offset = 0; | ||
709 | |||
710 | for (i = tcp_ctask->sg_count; i < scsi_sg_count(sc); i++) { | ||
711 | char *dest; | ||
712 | |||
713 | dest = kmap_atomic(sg_page(&sg[i]), KM_SOFTIRQ0); | ||
714 | rc = iscsi_ctask_copy(tcp_conn, ctask, dest + sg[i].offset, | ||
715 | sg[i].length, offset); | ||
716 | kunmap_atomic(dest, KM_SOFTIRQ0); | ||
717 | if (rc == -EAGAIN) | ||
718 | /* continue with the next SKB/PDU */ | ||
719 | return rc; | ||
720 | if (!rc) { | ||
721 | if (conn->datadgst_en) { | ||
722 | if (!offset) | ||
723 | crypto_hash_update( | ||
724 | &tcp_conn->rx_hash, | ||
725 | &sg[i], sg[i].length); | ||
726 | else | ||
727 | partial_sg_digest_update( | ||
728 | &tcp_conn->rx_hash, | ||
729 | &sg[i], | ||
730 | sg[i].offset + offset, | ||
731 | sg[i].length - offset); | ||
732 | } | ||
733 | offset = 0; | ||
734 | tcp_ctask->sg_count++; | ||
735 | } | ||
736 | |||
737 | if (!ctask->data_count) { | ||
738 | if (rc && conn->datadgst_en) | ||
739 | /* | ||
740 | * data-in is complete, but buffer not... | ||
741 | */ | ||
742 | partial_sg_digest_update(&tcp_conn->rx_hash, | ||
743 | &sg[i], | ||
744 | sg[i].offset, | ||
745 | sg[i].length-rc); | ||
746 | rc = 0; | ||
747 | break; | ||
748 | } | ||
749 | |||
750 | if (!tcp_conn->in.copy) | ||
751 | return -EAGAIN; | ||
752 | } | ||
753 | BUG_ON(ctask->data_count); | ||
754 | 876 | ||
755 | /* check for non-exceptional status */ | 877 | /* Check if there are additional header segments |
756 | if (tcp_conn->in.hdr->flags & ISCSI_FLAG_DATA_STATUS) { | 878 | * *prior* to computing the digest, because we |
757 | debug_scsi("done [sc %lx res %d itt 0x%x flags 0x%x]\n", | 879 | * may need to go back to the caller for more. |
758 | (long)sc, sc->result, ctask->itt, | 880 | */ |
759 | tcp_conn->in.hdr->flags); | 881 | hdr = (struct iscsi_hdr *) tcp_conn->in.hdr_buf; |
760 | spin_lock(&conn->session->lock); | 882 | if (chunk->copied == sizeof(struct iscsi_hdr) && hdr->hlength) { |
761 | __iscsi_complete_pdu(conn, tcp_conn->in.hdr, NULL, 0); | 883 | /* Bump the header length - the caller will |
762 | spin_unlock(&conn->session->lock); | 884 | * just loop around and get the AHS for us, and |
885 | * call again. */ | ||
886 | unsigned int ahslen = hdr->hlength << 2; | ||
887 | |||
888 | /* Make sure we don't overflow */ | ||
889 | if (sizeof(*hdr) + ahslen > sizeof(tcp_conn->in.hdr_buf)) | ||
890 | return ISCSI_ERR_AHSLEN; | ||
891 | |||
892 | chunk->total_size += ahslen; | ||
893 | chunk->size += ahslen; | ||
894 | return 0; | ||
763 | } | 895 | } |
764 | 896 | ||
765 | return rc; | 897 | /* We're done processing the header. See if we're doing |
766 | } | 898 | * header digests; if so, set up the recv_digest buffer |
767 | 899 | * and go back for more. */ | |
768 | static int | 900 | if (conn->hdrdgst_en) { |
769 | iscsi_data_recv(struct iscsi_conn *conn) | 901 | if (chunk->digest_len == 0) { |
770 | { | 902 | iscsi_tcp_chunk_splice_digest(chunk, |
771 | struct iscsi_tcp_conn *tcp_conn = conn->dd_data; | 903 | chunk->recv_digest); |
772 | int rc = 0, opcode; | 904 | return 0; |
773 | |||
774 | opcode = tcp_conn->in.hdr->opcode & ISCSI_OPCODE_MASK; | ||
775 | switch (opcode) { | ||
776 | case ISCSI_OP_SCSI_DATA_IN: | ||
777 | rc = iscsi_scsi_data_in(conn); | ||
778 | break; | ||
779 | case ISCSI_OP_SCSI_CMD_RSP: | ||
780 | case ISCSI_OP_TEXT_RSP: | ||
781 | case ISCSI_OP_LOGIN_RSP: | ||
782 | case ISCSI_OP_ASYNC_EVENT: | ||
783 | case ISCSI_OP_REJECT: | ||
784 | /* | ||
785 | * Collect data segment to the connection's data | ||
786 | * placeholder | ||
787 | */ | ||
788 | if (iscsi_tcp_copy(conn, tcp_conn->in.datalen)) { | ||
789 | rc = -EAGAIN; | ||
790 | goto exit; | ||
791 | } | 905 | } |
906 | iscsi_tcp_dgst_header(&tcp_conn->rx_hash, hdr, | ||
907 | chunk->total_copied - ISCSI_DIGEST_SIZE, | ||
908 | chunk->digest); | ||
792 | 909 | ||
793 | rc = iscsi_complete_pdu(conn, tcp_conn->in.hdr, conn->data, | 910 | if (!iscsi_tcp_dgst_verify(tcp_conn, chunk)) |
794 | tcp_conn->in.datalen); | 911 | return ISCSI_ERR_HDR_DGST; |
795 | if (!rc && conn->datadgst_en && opcode != ISCSI_OP_LOGIN_RSP) | ||
796 | iscsi_recv_digest_update(tcp_conn, conn->data, | ||
797 | tcp_conn->in.datalen); | ||
798 | break; | ||
799 | default: | ||
800 | BUG_ON(1); | ||
801 | } | 912 | } |
802 | exit: | 913 | |
803 | return rc; | 914 | tcp_conn->in.hdr = hdr; |
915 | return iscsi_tcp_hdr_dissect(conn, hdr); | ||
804 | } | 916 | } |
805 | 917 | ||
806 | /** | 918 | /** |
807 | * iscsi_tcp_data_recv - TCP receive in sendfile fashion | 919 | * iscsi_tcp_recv - TCP receive in sendfile fashion |
808 | * @rd_desc: read descriptor | 920 | * @rd_desc: read descriptor |
809 | * @skb: socket buffer | 921 | * @skb: socket buffer |
810 | * @offset: offset in skb | 922 | * @offset: offset in skb |
811 | * @len: skb->len - offset | 923 | * @len: skb->len - offset |
812 | **/ | 924 | **/ |
813 | static int | 925 | static int |
814 | iscsi_tcp_data_recv(read_descriptor_t *rd_desc, struct sk_buff *skb, | 926 | iscsi_tcp_recv(read_descriptor_t *rd_desc, struct sk_buff *skb, |
815 | unsigned int offset, size_t len) | 927 | unsigned int offset, size_t len) |
816 | { | 928 | { |
817 | int rc; | ||
818 | struct iscsi_conn *conn = rd_desc->arg.data; | 929 | struct iscsi_conn *conn = rd_desc->arg.data; |
819 | struct iscsi_tcp_conn *tcp_conn = conn->dd_data; | 930 | struct iscsi_tcp_conn *tcp_conn = conn->dd_data; |
820 | int processed; | 931 | struct iscsi_chunk *chunk = &tcp_conn->in.chunk; |
821 | char pad[ISCSI_PAD_LEN]; | 932 | struct skb_seq_state seq; |
822 | struct scatterlist sg; | 933 | unsigned int consumed = 0; |
823 | 934 | int rc = 0; | |
824 | /* | ||
825 | * Save current SKB and its offset in the corresponding | ||
826 | * connection context. | ||
827 | */ | ||
828 | tcp_conn->in.copy = skb->len - offset; | ||
829 | tcp_conn->in.offset = offset; | ||
830 | tcp_conn->in.skb = skb; | ||
831 | tcp_conn->in.len = tcp_conn->in.copy; | ||
832 | BUG_ON(tcp_conn->in.copy <= 0); | ||
833 | debug_tcp("in %d bytes\n", tcp_conn->in.copy); | ||
834 | 935 | ||
835 | more: | 936 | debug_tcp("in %d bytes\n", skb->len - offset); |
836 | tcp_conn->in.copied = 0; | ||
837 | rc = 0; | ||
838 | 937 | ||
839 | if (unlikely(conn->suspend_rx)) { | 938 | if (unlikely(conn->suspend_rx)) { |
840 | debug_tcp("conn %d Rx suspended!\n", conn->id); | 939 | debug_tcp("conn %d Rx suspended!\n", conn->id); |
841 | return 0; | 940 | return 0; |
842 | } | 941 | } |
843 | 942 | ||
844 | if (tcp_conn->in_progress == IN_PROGRESS_WAIT_HEADER || | 943 | skb_prepare_seq_read(skb, offset, skb->len, &seq); |
845 | tcp_conn->in_progress == IN_PROGRESS_HEADER_GATHER) { | 944 | while (1) { |
846 | rc = iscsi_hdr_extract(tcp_conn); | 945 | unsigned int avail; |
847 | if (rc) { | 946 | const u8 *ptr; |
848 | if (rc == -EAGAIN) | ||
849 | goto nomore; | ||
850 | else { | ||
851 | iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); | ||
852 | return 0; | ||
853 | } | ||
854 | } | ||
855 | |||
856 | /* | ||
857 | * Verify and process incoming PDU header. | ||
858 | */ | ||
859 | rc = iscsi_tcp_hdr_recv(conn); | ||
860 | if (!rc && tcp_conn->in.datalen) { | ||
861 | if (conn->datadgst_en) | ||
862 | crypto_hash_init(&tcp_conn->rx_hash); | ||
863 | tcp_conn->in_progress = IN_PROGRESS_DATA_RECV; | ||
864 | } else if (rc) { | ||
865 | iscsi_conn_failure(conn, rc); | ||
866 | return 0; | ||
867 | } | ||
868 | } | ||
869 | |||
870 | if (tcp_conn->in_progress == IN_PROGRESS_DDIGEST_RECV && | ||
871 | tcp_conn->in.copy) { | ||
872 | uint32_t recv_digest; | ||
873 | 947 | ||
874 | debug_tcp("extra data_recv offset %d copy %d\n", | 948 | avail = skb_seq_read(consumed, &ptr, &seq); |
875 | tcp_conn->in.offset, tcp_conn->in.copy); | 949 | if (avail == 0) |
876 | 950 | break; | |
877 | if (!tcp_conn->data_copied) { | 951 | BUG_ON(chunk->copied >= chunk->size); |
878 | if (tcp_conn->in.padding) { | 952 | |
879 | debug_tcp("padding -> %d\n", | 953 | debug_tcp("skb %p ptr=%p avail=%u\n", skb, ptr, avail); |
880 | tcp_conn->in.padding); | 954 | rc = iscsi_tcp_chunk_recv(tcp_conn, chunk, ptr, avail); |
881 | memset(pad, 0, tcp_conn->in.padding); | 955 | BUG_ON(rc == 0); |
882 | sg_init_one(&sg, pad, tcp_conn->in.padding); | 956 | consumed += rc; |
883 | crypto_hash_update(&tcp_conn->rx_hash, | 957 | |
884 | &sg, sg.length); | 958 | if (chunk->total_copied >= chunk->total_size) { |
959 | rc = chunk->done(tcp_conn, chunk); | ||
960 | if (rc != 0) { | ||
961 | skb_abort_seq_read(&seq); | ||
962 | goto error; | ||
885 | } | 963 | } |
886 | crypto_hash_final(&tcp_conn->rx_hash, | ||
887 | (u8 *) &tcp_conn->in.datadgst); | ||
888 | debug_tcp("rx digest 0x%x\n", tcp_conn->in.datadgst); | ||
889 | } | ||
890 | |||
891 | rc = iscsi_tcp_copy(conn, sizeof(uint32_t)); | ||
892 | if (rc) { | ||
893 | if (rc == -EAGAIN) | ||
894 | goto again; | ||
895 | iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); | ||
896 | return 0; | ||
897 | } | ||
898 | |||
899 | memcpy(&recv_digest, conn->data, sizeof(uint32_t)); | ||
900 | if (recv_digest != tcp_conn->in.datadgst) { | ||
901 | debug_tcp("iscsi_tcp: data digest error!" | ||
902 | "0x%x != 0x%x\n", recv_digest, | ||
903 | tcp_conn->in.datadgst); | ||
904 | iscsi_conn_failure(conn, ISCSI_ERR_DATA_DGST); | ||
905 | return 0; | ||
906 | } else { | ||
907 | debug_tcp("iscsi_tcp: data digest match!" | ||
908 | "0x%x == 0x%x\n", recv_digest, | ||
909 | tcp_conn->in.datadgst); | ||
910 | tcp_conn->in_progress = IN_PROGRESS_WAIT_HEADER; | ||
911 | } | ||
912 | } | ||
913 | |||
914 | if (tcp_conn->in_progress == IN_PROGRESS_DATA_RECV && | ||
915 | tcp_conn->in.copy) { | ||
916 | debug_tcp("data_recv offset %d copy %d\n", | ||
917 | tcp_conn->in.offset, tcp_conn->in.copy); | ||
918 | 964 | ||
919 | rc = iscsi_data_recv(conn); | 965 | /* The done() functions sets up the |
920 | if (rc) { | 966 | * next chunk. */ |
921 | if (rc == -EAGAIN) | ||
922 | goto again; | ||
923 | iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); | ||
924 | return 0; | ||
925 | } | 967 | } |
926 | |||
927 | if (tcp_conn->in.padding) | ||
928 | tcp_conn->in_progress = IN_PROGRESS_PAD_RECV; | ||
929 | else if (conn->datadgst_en) | ||
930 | tcp_conn->in_progress = IN_PROGRESS_DDIGEST_RECV; | ||
931 | else | ||
932 | tcp_conn->in_progress = IN_PROGRESS_WAIT_HEADER; | ||
933 | tcp_conn->data_copied = 0; | ||
934 | } | ||
935 | |||
936 | if (tcp_conn->in_progress == IN_PROGRESS_PAD_RECV && | ||
937 | tcp_conn->in.copy) { | ||
938 | int copylen = min(tcp_conn->in.padding - tcp_conn->data_copied, | ||
939 | tcp_conn->in.copy); | ||
940 | |||
941 | tcp_conn->in.copy -= copylen; | ||
942 | tcp_conn->in.offset += copylen; | ||
943 | tcp_conn->data_copied += copylen; | ||
944 | |||
945 | if (tcp_conn->data_copied != tcp_conn->in.padding) | ||
946 | tcp_conn->in_progress = IN_PROGRESS_PAD_RECV; | ||
947 | else if (conn->datadgst_en) | ||
948 | tcp_conn->in_progress = IN_PROGRESS_DDIGEST_RECV; | ||
949 | else | ||
950 | tcp_conn->in_progress = IN_PROGRESS_WAIT_HEADER; | ||
951 | tcp_conn->data_copied = 0; | ||
952 | } | 968 | } |
953 | 969 | ||
954 | debug_tcp("f, processed %d from out of %d padding %d\n", | 970 | conn->rxdata_octets += consumed; |
955 | tcp_conn->in.offset - offset, (int)len, tcp_conn->in.padding); | 971 | return consumed; |
956 | BUG_ON(tcp_conn->in.offset - offset > len); | ||
957 | 972 | ||
958 | if (tcp_conn->in.offset - offset != len) { | 973 | error: |
959 | debug_tcp("continue to process %d bytes\n", | 974 | debug_tcp("Error receiving PDU, errno=%d\n", rc); |
960 | (int)len - (tcp_conn->in.offset - offset)); | 975 | iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); |
961 | goto more; | 976 | return 0; |
962 | } | ||
963 | |||
964 | nomore: | ||
965 | processed = tcp_conn->in.offset - offset; | ||
966 | BUG_ON(processed == 0); | ||
967 | return processed; | ||
968 | |||
969 | again: | ||
970 | processed = tcp_conn->in.offset - offset; | ||
971 | debug_tcp("c, processed %d from out of %d rd_desc_cnt %d\n", | ||
972 | processed, (int)len, (int)rd_desc->count); | ||
973 | BUG_ON(processed == 0); | ||
974 | BUG_ON(processed > len); | ||
975 | |||
976 | conn->rxdata_octets += processed; | ||
977 | return processed; | ||
978 | } | 977 | } |
979 | 978 | ||
980 | static void | 979 | static void |
981 | iscsi_tcp_data_ready(struct sock *sk, int flag) | 980 | iscsi_tcp_data_ready(struct sock *sk, int flag) |
982 | { | 981 | { |
983 | struct iscsi_conn *conn = sk->sk_user_data; | 982 | struct iscsi_conn *conn = sk->sk_user_data; |
983 | struct iscsi_tcp_conn *tcp_conn = conn->dd_data; | ||
984 | read_descriptor_t rd_desc; | 984 | read_descriptor_t rd_desc; |
985 | 985 | ||
986 | read_lock(&sk->sk_callback_lock); | 986 | read_lock(&sk->sk_callback_lock); |
987 | 987 | ||
988 | /* | 988 | /* |
989 | * Use rd_desc to pass 'conn' to iscsi_tcp_data_recv. | 989 | * Use rd_desc to pass 'conn' to iscsi_tcp_recv. |
990 | * We set count to 1 because we want the network layer to | 990 | * We set count to 1 because we want the network layer to |
991 | * hand us all the skbs that are available. iscsi_tcp_data_recv | 991 | * hand us all the skbs that are available. iscsi_tcp_recv |
992 | * handled pdus that cross buffers or pdus that still need data. | 992 | * handled pdus that cross buffers or pdus that still need data. |
993 | */ | 993 | */ |
994 | rd_desc.arg.data = conn; | 994 | rd_desc.arg.data = conn; |
995 | rd_desc.count = 1; | 995 | rd_desc.count = 1; |
996 | tcp_read_sock(sk, &rd_desc, iscsi_tcp_data_recv); | 996 | tcp_read_sock(sk, &rd_desc, iscsi_tcp_recv); |
997 | 997 | ||
998 | read_unlock(&sk->sk_callback_lock); | 998 | read_unlock(&sk->sk_callback_lock); |
999 | |||
1000 | /* If we had to (atomically) map a highmem page, | ||
1001 | * unmap it now. */ | ||
1002 | iscsi_tcp_chunk_unmap(&tcp_conn->in.chunk); | ||
999 | } | 1003 | } |
1000 | 1004 | ||
1001 | static void | 1005 | static void |
@@ -1097,9 +1101,9 @@ iscsi_send(struct iscsi_conn *conn, struct iscsi_buf *buf, int size, int flags) | |||
1097 | * slab case. | 1101 | * slab case. |
1098 | */ | 1102 | */ |
1099 | if (buf->use_sendmsg) | 1103 | if (buf->use_sendmsg) |
1100 | res = sock_no_sendpage(sk, sg_page(&buf->sg), offset, size, flags); | 1104 | res = sock_no_sendpage(sk, buf->sg.page, offset, size, flags); |
1101 | else | 1105 | else |
1102 | res = tcp_conn->sendpage(sk, sg_page(&buf->sg), offset, size, flags); | 1106 | res = tcp_conn->sendpage(sk, buf->sg.page, offset, size, flags); |
1103 | 1107 | ||
1104 | if (res >= 0) { | 1108 | if (res >= 0) { |
1105 | conn->txdata_octets += res; | 1109 | conn->txdata_octets += res; |
@@ -1783,9 +1787,6 @@ iscsi_tcp_conn_create(struct iscsi_cls_session *cls_session, uint32_t conn_idx) | |||
1783 | 1787 | ||
1784 | conn->dd_data = tcp_conn; | 1788 | conn->dd_data = tcp_conn; |
1785 | tcp_conn->iscsi_conn = conn; | 1789 | tcp_conn->iscsi_conn = conn; |
1786 | tcp_conn->in_progress = IN_PROGRESS_WAIT_HEADER; | ||
1787 | /* initial operational parameters */ | ||
1788 | tcp_conn->hdr_size = sizeof(struct iscsi_hdr); | ||
1789 | 1790 | ||
1790 | tcp_conn->tx_hash.tfm = crypto_alloc_hash("crc32c", 0, | 1791 | tcp_conn->tx_hash.tfm = crypto_alloc_hash("crc32c", 0, |
1791 | CRYPTO_ALG_ASYNC); | 1792 | CRYPTO_ALG_ASYNC); |
@@ -1862,11 +1863,9 @@ static void | |||
1862 | iscsi_tcp_conn_stop(struct iscsi_cls_conn *cls_conn, int flag) | 1863 | iscsi_tcp_conn_stop(struct iscsi_cls_conn *cls_conn, int flag) |
1863 | { | 1864 | { |
1864 | struct iscsi_conn *conn = cls_conn->dd_data; | 1865 | struct iscsi_conn *conn = cls_conn->dd_data; |
1865 | struct iscsi_tcp_conn *tcp_conn = conn->dd_data; | ||
1866 | 1866 | ||
1867 | iscsi_conn_stop(cls_conn, flag); | 1867 | iscsi_conn_stop(cls_conn, flag); |
1868 | iscsi_tcp_release_conn(conn); | 1868 | iscsi_tcp_release_conn(conn); |
1869 | tcp_conn->hdr_size = sizeof(struct iscsi_hdr); | ||
1870 | } | 1869 | } |
1871 | 1870 | ||
1872 | static int iscsi_tcp_get_addr(struct iscsi_conn *conn, struct socket *sock, | 1871 | static int iscsi_tcp_get_addr(struct iscsi_conn *conn, struct socket *sock, |
@@ -1966,7 +1965,7 @@ iscsi_tcp_conn_bind(struct iscsi_cls_session *cls_session, | |||
1966 | /* | 1965 | /* |
1967 | * set receive state machine into initial state | 1966 | * set receive state machine into initial state |
1968 | */ | 1967 | */ |
1969 | tcp_conn->in_progress = IN_PROGRESS_WAIT_HEADER; | 1968 | iscsi_tcp_hdr_recv_prep(tcp_conn); |
1970 | return 0; | 1969 | return 0; |
1971 | 1970 | ||
1972 | free_socket: | 1971 | free_socket: |
@@ -2059,9 +2058,6 @@ iscsi_conn_set_param(struct iscsi_cls_conn *cls_conn, enum iscsi_param param, | |||
2059 | switch(param) { | 2058 | switch(param) { |
2060 | case ISCSI_PARAM_HDRDGST_EN: | 2059 | case ISCSI_PARAM_HDRDGST_EN: |
2061 | iscsi_set_param(cls_conn, param, buf, buflen); | 2060 | iscsi_set_param(cls_conn, param, buf, buflen); |
2062 | tcp_conn->hdr_size = sizeof(struct iscsi_hdr); | ||
2063 | if (conn->hdrdgst_en) | ||
2064 | tcp_conn->hdr_size += sizeof(__u32); | ||
2065 | break; | 2061 | break; |
2066 | case ISCSI_PARAM_DATADGST_EN: | 2062 | case ISCSI_PARAM_DATADGST_EN: |
2067 | iscsi_set_param(cls_conn, param, buf, buflen); | 2063 | iscsi_set_param(cls_conn, param, buf, buflen); |
diff --git a/drivers/scsi/iscsi_tcp.h b/drivers/scsi/iscsi_tcp.h index 7eba44df0a7f..f1c541151100 100644 --- a/drivers/scsi/iscsi_tcp.h +++ b/drivers/scsi/iscsi_tcp.h | |||
@@ -24,13 +24,6 @@ | |||
24 | 24 | ||
25 | #include <scsi/libiscsi.h> | 25 | #include <scsi/libiscsi.h> |
26 | 26 | ||
27 | /* Socket's Receive state machine */ | ||
28 | #define IN_PROGRESS_WAIT_HEADER 0x0 | ||
29 | #define IN_PROGRESS_HEADER_GATHER 0x1 | ||
30 | #define IN_PROGRESS_DATA_RECV 0x2 | ||
31 | #define IN_PROGRESS_DDIGEST_RECV 0x3 | ||
32 | #define IN_PROGRESS_PAD_RECV 0x4 | ||
33 | |||
34 | /* xmit state machine */ | 27 | /* xmit state machine */ |
35 | #define XMSTATE_IDLE 0x0 | 28 | #define XMSTATE_IDLE 0x0 |
36 | #define XMSTATE_CMD_HDR_INIT 0x1 | 29 | #define XMSTATE_CMD_HDR_INIT 0x1 |
@@ -54,41 +47,64 @@ | |||
54 | 47 | ||
55 | struct crypto_hash; | 48 | struct crypto_hash; |
56 | struct socket; | 49 | struct socket; |
50 | struct iscsi_tcp_conn; | ||
51 | struct iscsi_chunk; | ||
52 | |||
53 | typedef int iscsi_chunk_done_fn_t(struct iscsi_tcp_conn *, | ||
54 | struct iscsi_chunk *); | ||
55 | |||
56 | struct iscsi_chunk { | ||
57 | unsigned char *data; | ||
58 | unsigned int size; | ||
59 | unsigned int copied; | ||
60 | unsigned int total_size; | ||
61 | unsigned int total_copied; | ||
62 | |||
63 | struct hash_desc *hash; | ||
64 | unsigned char recv_digest[ISCSI_DIGEST_SIZE]; | ||
65 | unsigned char digest[ISCSI_DIGEST_SIZE]; | ||
66 | unsigned int digest_len; | ||
67 | |||
68 | struct scatterlist *sg; | ||
69 | void *sg_mapped; | ||
70 | unsigned int sg_offset; | ||
71 | unsigned int sg_index; | ||
72 | unsigned int sg_count; | ||
73 | |||
74 | iscsi_chunk_done_fn_t *done; | ||
75 | }; | ||
57 | 76 | ||
58 | /* Socket connection recieve helper */ | 77 | /* Socket connection recieve helper */ |
59 | struct iscsi_tcp_recv { | 78 | struct iscsi_tcp_recv { |
60 | struct iscsi_hdr *hdr; | 79 | struct iscsi_hdr *hdr; |
61 | struct sk_buff *skb; | 80 | struct iscsi_chunk chunk; |
62 | int offset; | 81 | |
63 | int len; | 82 | /* Allocate buffer for BHS + AHS */ |
64 | int hdr_offset; | 83 | uint32_t hdr_buf[64]; |
65 | int copy; | ||
66 | int copied; | ||
67 | int padding; | ||
68 | struct iscsi_cmd_task *ctask; /* current cmd in progress */ | ||
69 | 84 | ||
70 | /* copied and flipped values */ | 85 | /* copied and flipped values */ |
71 | int datalen; | 86 | int datalen; |
72 | int datadgst; | 87 | }; |
73 | char zero_copy_hdr; | 88 | |
89 | /* Socket connection send helper */ | ||
90 | struct iscsi_tcp_send { | ||
91 | struct iscsi_hdr *hdr; | ||
92 | struct iscsi_chunk chunk; | ||
93 | struct iscsi_chunk data_chunk; | ||
94 | |||
95 | /* Allocate buffer for BHS + AHS */ | ||
96 | uint32_t hdr_buf[64]; | ||
74 | }; | 97 | }; |
75 | 98 | ||
76 | struct iscsi_tcp_conn { | 99 | struct iscsi_tcp_conn { |
77 | struct iscsi_conn *iscsi_conn; | 100 | struct iscsi_conn *iscsi_conn; |
78 | struct socket *sock; | 101 | struct socket *sock; |
79 | struct iscsi_hdr hdr; /* header placeholder */ | ||
80 | char hdrext[4*sizeof(__u16) + | ||
81 | sizeof(__u32)]; | ||
82 | int data_copied; | ||
83 | int stop_stage; /* conn_stop() flag: * | 102 | int stop_stage; /* conn_stop() flag: * |
84 | * stop to recover, * | 103 | * stop to recover, * |
85 | * stop to terminate */ | 104 | * stop to terminate */ |
86 | /* iSCSI connection-wide sequencing */ | ||
87 | int hdr_size; /* PDU header size */ | ||
88 | |||
89 | /* control data */ | 105 | /* control data */ |
90 | struct iscsi_tcp_recv in; /* TCP receive context */ | 106 | struct iscsi_tcp_recv in; /* TCP receive context */ |
91 | int in_progress; /* connection state machine */ | 107 | struct iscsi_tcp_send out; /* TCP send context */ |
92 | 108 | ||
93 | /* old values for socket callbacks */ | 109 | /* old values for socket callbacks */ |
94 | void (*old_data_ready)(struct sock *, int); | 110 | void (*old_data_ready)(struct sock *, int); |
diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h index 89429f433f85..e1fb3d0927b0 100644 --- a/include/scsi/libiscsi.h +++ b/include/scsi/libiscsi.h | |||
@@ -77,6 +77,10 @@ enum { | |||
77 | 77 | ||
78 | #define ISCSI_ADDRESS_BUF_LEN 64 | 78 | #define ISCSI_ADDRESS_BUF_LEN 64 |
79 | 79 | ||
80 | enum { | ||
81 | ISCSI_DIGEST_SIZE = sizeof(__u32), | ||
82 | }; | ||
83 | |||
80 | struct iscsi_mgmt_task { | 84 | struct iscsi_mgmt_task { |
81 | /* | 85 | /* |
82 | * Becuae LLDs allocate their hdr differently, this is a pointer to | 86 | * Becuae LLDs allocate their hdr differently, this is a pointer to |