xref: /netbsd-src/sys/dev/iscsi/iscsi_rcv.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*	$NetBSD: iscsi_rcv.c,v 1.25 2018/03/04 07:37:43 mlelstv Exp $	*/
2 
3 /*-
4  * Copyright (c) 2004,2005,2006,2011 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Wasabi Systems, Inc.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 #include "iscsi_globals.h"
32 
33 #include <sys/file.h>
34 #include <sys/socket.h>
35 #include <sys/socketvar.h>
36 
37 /*****************************************************************************/
38 
39 /*
40  * my_soo_read:
41  *    Replacement for soo_read with flag handling.
42  *
43  *    Parameter:
44  *          conn     The connection
45  *          u        The uio descriptor
46  *          flags    Read flags
47  *
48  *    Returns:    0 on success, else 1
49  */
50 
51 STATIC int
52 my_soo_read(connection_t *conn, struct uio *u, int flags)
53 {
54 	struct socket *so = conn->c_sock->f_socket;
55 	int ret;
56 #ifdef ISCSI_DEBUG
57 	size_t resid = u->uio_resid;
58 #endif
59 
60 	DEBC(conn, 99, ("soo_read req: %zu\n", resid));
61 
62 	if (flags & MSG_WAITALL) {
63 		flags &= ~MSG_WAITALL;
64 		do {
65 			int oresid = u->uio_resid;
66 			ret = (*so->so_receive)(so, NULL, u, NULL, NULL, &flags);
67 			if (!ret && u->uio_resid == oresid)
68 				break;
69 		} while (!ret && u->uio_resid > 0);
70 	} else
71 		ret = (*so->so_receive)(so, NULL, u, NULL, NULL, &flags);
72 
73 	if (ret || (flags != MSG_DONTWAIT && u->uio_resid)) {
74 		DEBC(conn, 1, ("Read failed (ret: %d, req: %zu, out: %zu)\n",
75 		               ret, resid, u->uio_resid));
76 		handle_connection_error(conn, ISCSI_STATUS_SOCKET_ERROR,
77 		                        RECOVER_CONNECTION);
78 		return 1;
79 	}
80 	return 0;
81 }
82 
83 
84 /*
85  * try_resynch_receive:
86  *    Skip over everything in the socket's receive buffer, in the hope of
87  *    ending up at the start of a new PDU.
88  *
89  *    Parameter:
90  *          conn     The connection
91  */
92 
93 STATIC void
94 try_resynch_receive(connection_t *conn)
95 {
96 	uint8_t buffer[64];
97 	struct uio uio;
98 	struct iovec io_vec;
99 	int rc;
100 
101 	uio.uio_rw = UIO_READ;
102 	UIO_SETUP_SYSSPACE(&uio);
103 
104 	do {
105 		io_vec.iov_base = buffer;
106 		uio.uio_iov = &io_vec;
107 		uio.uio_iovcnt = 1;
108 		uio.uio_resid = io_vec.iov_len = sizeof(buffer);
109 
110 		rc = my_soo_read(conn, &uio, MSG_DONTWAIT);
111 		DEBC(conn, 9, ("try_resynch_receive: rc = %d, resid = %zu\n",
112 				rc, uio.uio_resid));
113 	} while (!rc && !uio.uio_resid);
114 }
115 
116 
117 /*
118  * ccb_from_itt
119  *    Translate ITT into CCB pointer.
120  *
121  *    Parameter:
122  *          conn     The connection
123  *          itt      The Initiator Task Tag
124  *
125  *    Returns:
126  *          Pointer to CCB, or NULL if ITT is not a valid CCB index.
127  */
128 
129 STATIC ccb_t *
130 ccb_from_itt(connection_t *conn, uint32_t itt)
131 {
132 	ccb_t *ccb;
133 	int cidx;
134 
135 	if (itt == 0xffffffff)
136 		return NULL;
137 
138 	cidx = itt & 0xff;
139 	if (cidx >= CCBS_PER_SESSION)
140 		return NULL;
141 
142 	ccb = &conn->c_session->s_ccb[cidx];
143 
144 	if (ccb->ccb_ITT != itt) {
145 		DEBC(conn, 0,
146 		     ("ccb_from_itt: received invalid CCB itt %08x != %08x\n",
147 		      itt, ccb->ccb_ITT));
148 		return NULL;
149 	}
150 
151 	if (ccb->ccb_disp <= CCBDISP_BUSY) {
152 		DEBC(conn, 0,
153 		     ("ccb_from_itt: received CCB with invalid disp %d\n",
154 		      ccb->ccb_disp));
155 		return NULL;
156 	}
157 
158 	return ccb;
159 }
160 
161 
162 /*
163  * read_pdu_data:
164  *    Initialize the uio structure for receiving everything after the
165  *    header, including data (if present), and padding. Read the data.
166  *
167  *    Parameter:
168  *          pdu      The PDU
169  *          data     Pointer to data (may be NULL for auto-allocation)
170  *          offset   The offset into the data pointer
171  *
172  *    Returns:     0 on success
173  *                 1 if an error occurs during read
174  *                -1 if the data digest was incorrect (PDU must be ignored)
175  */
176 
177 STATIC int
178 read_pdu_data(pdu_t *pdu, uint8_t *data, uint32_t offset)
179 {
180 	static uint8_t pad_bytes[4];
181 	uint32_t len, digest;
182 	struct uio *uio;
183 	int i, pad;
184 	connection_t *conn = pdu->pdu_connection;
185 
186 	DEB(15, ("read_pdu_data: data segment length = %d\n",
187 		ntoh3(pdu->pdu_hdr.pduh_DataSegmentLength)));
188 	if (!(len = ntoh3(pdu->pdu_hdr.pduh_DataSegmentLength))) {
189 		return 0;
190 	}
191 	pad = len & 0x03;
192 	if (pad) {
193 		pad = 4 - pad;
194 	}
195 
196 	KASSERT(data != NULL || offset == 0);
197 
198 	if (data == NULL) {
199 		/*
200 		 * NOTE: Always allocate 2 extra bytes when reading temp data,
201 		 * since temp data is mostly used for received text, and we can
202 		 * make sure there's a double zero at the end of the data to mark EOF.
203 		 */
204 		if ((data = (uint8_t *) malloc(len + 2, M_TEMP, M_WAITOK)) == NULL) {
205 			DEBOUT(("ran out of mem on receive\n"));
206 			handle_connection_error(pdu->pdu_connection,
207 				ISCSI_STATUS_NO_RESOURCES, LOGOUT_SESSION);
208 			return 1;
209 		}
210 		pdu->pdu_temp_data = data;
211 		pdu->pdu_temp_data_len = len;
212 	}
213 
214 	pdu->pdu_io_vec[0].iov_base = data + offset;
215 	pdu->pdu_io_vec[0].iov_len = len;
216 
217 	uio = &pdu->pdu_uio;
218 
219 	uio->uio_iov = pdu->pdu_io_vec;
220 	uio->uio_iovcnt = 1;
221 	uio->uio_rw = UIO_READ;
222 	uio->uio_resid = len;
223 	UIO_SETUP_SYSSPACE(uio);
224 
225 	if (pad) {
226 		uio->uio_iovcnt++;
227 		uio->uio_iov[1].iov_base = pad_bytes;
228 		uio->uio_iov[1].iov_len = pad;
229 		uio->uio_resid += pad;
230 	}
231 
232 	if (conn->c_DataDigest) {
233 		i = uio->uio_iovcnt++;
234 		pdu->pdu_io_vec[i].iov_base = &pdu->pdu_data_digest;
235 		pdu->pdu_io_vec[i].iov_len = 4;
236 		uio->uio_resid += 4;
237 	}
238 
239 	/* get the data */
240 	if (my_soo_read(conn, &pdu->pdu_uio, MSG_WAITALL) != 0) {
241 		return 1;
242 	}
243 	if (conn->c_DataDigest) {
244 		digest = gen_digest_2(data, len, pad_bytes, pad);
245 
246 		if (digest != pdu->pdu_data_digest) {
247 			DEBOUT(("Data Digest Error: comp = %08x, rx = %08x\n",
248 					digest, pdu->pdu_data_digest));
249 			switch (pdu->pdu_hdr.pduh_Opcode & OPCODE_MASK) {
250 			case TOP_SCSI_Response:
251 			case TOP_Text_Response:
252 				send_snack(pdu->pdu_connection, pdu, NULL, SNACK_STATUS_NAK);
253 				break;
254 
255 			case TOP_SCSI_Data_in:
256 				send_snack(pdu->pdu_connection, pdu, NULL, SNACK_DATA_NAK);
257 				break;
258 
259 			default:
260 				/* ignore all others */
261 				break;
262 			}
263 			return -1;
264 		}
265 	}
266 	return 0;
267 }
268 
269 
270 /*
271  * collect_text_data
272  *    Handle text continuation in login and text response PDUs
273  *
274  *    Parameter:
275  *          pdu      The received PDU
276  *          req_CCB  The CCB associated with the original request
277  *
278  *    Returns:    -1    if continue flag is set
279  *                0     if text is complete
280  *                +1    if an error occurred (out of resources)
281  */
282 STATIC int
283 collect_text_data(pdu_t *pdu, ccb_t *req_ccb)
284 {
285 
286 	if (req_ccb->ccb_text_data) {
287 		int nlen;
288 		uint8_t *newp;
289 
290 		nlen = req_ccb->ccb_text_len + pdu->pdu_temp_data_len;
291 		/* Note: allocate extra 2 bytes for text terminator */
292 		if ((newp = malloc(nlen + 2, M_TEMP, M_WAITOK)) == NULL) {
293 			DEBOUT(("Collect Text Data: Out of Memory, ccb = %p\n", req_ccb));
294 			req_ccb->ccb_status = ISCSI_STATUS_NO_RESOURCES;
295 			/* XXX where is CCB freed? */
296 			return 1;
297 		}
298 		memcpy(newp, req_ccb->ccb_text_data, req_ccb->ccb_text_len);
299 		memcpy(&newp[req_ccb->ccb_text_len], pdu->pdu_temp_data, pdu->pdu_temp_data_len);
300 
301 		free(req_ccb->ccb_text_data, M_TEMP);
302 		free(pdu->pdu_temp_data, M_TEMP);
303 
304 		req_ccb->ccb_text_data = NULL;
305 		pdu->pdu_temp_data = newp;
306 		pdu->pdu_temp_data_len = nlen;
307 	}
308 
309 	if (pdu->pdu_hdr.pduh_Flags & FLAG_CONTINUE) {
310 		req_ccb->ccb_text_data = pdu->pdu_temp_data;
311 		req_ccb->ccb_text_len = pdu->pdu_temp_data_len;
312 		pdu->pdu_temp_data = NULL;
313 
314 		acknowledge_text(req_ccb->ccb_connection, pdu, req_ccb);
315 		return -1;
316 	}
317 	return 0;
318 }
319 
320 
321 /*
322  * check_StatSN
323  *    Check received vs. expected StatSN
324  *
325  *    Parameter:
326  *          conn     The connection
327  *          nw_sn    The received StatSN in network byte order
328  *          ack      Acknowledge this SN if TRUE
329  */
330 
331 STATIC int
332 check_StatSN(connection_t *conn, uint32_t nw_sn, bool ack)
333 {
334 	int rc;
335 	uint32_t sn = ntohl(nw_sn);
336 
337 	rc = add_sernum(&conn->c_StatSN_buf, sn);
338 
339 	if (ack)
340 		ack_sernum(&conn->c_StatSN_buf, sn);
341 
342 	if (rc != 1) {
343 		if (rc == 0) {
344 			DEBOUT(("Duplicate PDU, ExpSN %d, Recvd: %d\n",
345 				conn->c_StatSN_buf.ExpSN, sn));
346 			return -1;
347 		}
348 
349 		if (rc < 0) {
350 			DEBOUT(("Excessive outstanding Status PDUs, ExpSN %d, Recvd: %d\n",
351 					conn->c_StatSN_buf.ExpSN, sn));
352 			handle_connection_error(conn, ISCSI_STATUS_PDUS_LOST,
353 									RECOVER_CONNECTION);
354 			return rc;
355 		}
356 
357 		DEBOUT(("Missing Status PDUs: First %d, num: %d\n",
358 				conn->c_StatSN_buf.ExpSN, rc - 1));
359 		if (conn->c_state == ST_FULL_FEATURE &&
360 			conn->c_session->s_ErrorRecoveryLevel) {
361 			snack_missing(conn, NULL, SNACK_STATUS_NAK,
362 						  conn->c_StatSN_buf.ExpSN, rc - 1);
363 		} else {
364 			DEBOUT(("StatSN killing connection (State = %d, "
365 					"ErrorRecoveryLevel = %d)\n",
366 					conn->c_state, conn->c_session->s_ErrorRecoveryLevel));
367 			handle_connection_error(conn, ISCSI_STATUS_PDUS_LOST,
368 									RECOVER_CONNECTION);
369 			return -1;
370 		}
371 	}
372 	return 0;
373 }
374 
375 
376 /*
377  * check_CmdSN
378  *    Check received vs. expected CmdSN
379  *
380  *    Parameter:
381  *          conn     The connection
382  *          nw_sn    The received ExpCmdSN in network byte order
383  */
384 
385 STATIC void
386 check_CmdSN(connection_t *conn, uint32_t nw_sn)
387 {
388 	uint32_t sn = ntohl(nw_sn);
389 	ccb_t *ccb, *nxt;
390 
391 	TAILQ_FOREACH_SAFE(ccb, &conn->c_ccbs_waiting, ccb_chain, nxt) {
392 		DEBC(conn, 10,
393 			("CheckCmdSN - CmdSN=%d, ExpCmdSn=%d, waiting=%p, flags=%x\n",
394 			ccb->ccb_CmdSN, sn, ccb->ccb_pdu_waiting, ccb->ccb_flags));
395 		if (ccb->ccb_pdu_waiting != NULL &&
396 			sn_a_lt_b(sn, ccb->ccb_CmdSN) &&
397 			!(ccb->ccb_flags & CCBF_GOT_RSP)) {
398 			DEBC(conn, 1, ("CheckCmdSN resending - CmdSN=%d, ExpCmdSn=%d\n",
399 			               ccb->ccb_CmdSN, sn));
400 
401 			ccb->ccb_total_tries++;
402 
403 			if (++ccb->ccb_num_timeouts > MAX_CCB_TIMEOUTS ||
404 				ccb->ccb_total_tries > MAX_CCB_TRIES) {
405 				handle_connection_error(conn,
406 					ISCSI_STATUS_TIMEOUT,
407 					(ccb->ccb_total_tries <= MAX_CCB_TRIES)
408 						? RECOVER_CONNECTION
409 						: LOGOUT_CONNECTION);
410 				break;
411 			} else {
412 				resend_pdu(ccb);
413 			}
414 		}
415 
416 		/*
417 		 * The target can respond to a NOP-In before subsequent
418 		 * commands are processed. So our CmdSN can exceed the
419 		 * returned ExpCmdSN by the number of commands that are
420 		 * in flight. Adjust the expected value accordingly.
421 		 */
422 		sn++;
423 	}
424 }
425 
426 
427 /*
428  * receive_login_pdu
429  *    Handle receipt of a login response PDU.
430  *
431  *    Parameter:
432  *          conn     The connection
433  *          pdu      The PDU
434  *          req_CCB  The CCB associated with the original request (if any)
435  */
436 
437 STATIC int
438 receive_login_pdu(connection_t *conn, pdu_t *pdu, ccb_t *req_ccb)
439 {
440 	int rc;
441 
442 	DEBC(conn, 9, ("Received Login Response PDU, op=%x, flags=%x, sn=%u\n",
443 			pdu->pdu_hdr.pduh_Opcode, pdu->pdu_hdr.pduh_Flags,
444 			ntohl(pdu->pdu_hdr.pduh_p.login_rsp.StatSN)));
445 
446 	if (req_ccb == NULL) {
447 		/* Duplicate?? */
448 		DEBOUT(("Received duplicate login response (no associated CCB)\n"));
449 		return -1;
450 	}
451 
452 	if (pdu->pdu_hdr.pduh_p.login_rsp.StatusClass) {
453 		DEBC(conn, 1, ("Login problem - Class = %x, Detail = %x\n",
454 				pdu->pdu_hdr.pduh_p.login_rsp.StatusClass,
455 				pdu->pdu_hdr.pduh_p.login_rsp.StatusDetail));
456 		wake_ccb(req_ccb, ISCSI_STATUS_LOGIN_FAILED);
457 		return 0;
458 	}
459 
460 	if (!conn->c_StatSN_buf.next_sn) {
461 		conn->c_StatSN_buf.next_sn = conn->c_StatSN_buf.ExpSN =
462 			ntohl(pdu->pdu_hdr.pduh_p.login_rsp.StatSN) + 1;
463 	} else if (check_StatSN(conn, pdu->pdu_hdr.pduh_p.login_rsp.StatSN, TRUE))
464 		return -1;
465 
466 	if (pdu->pdu_temp_data_len) {
467 		if ((rc = collect_text_data(pdu, req_ccb)) != 0)
468 			return max(rc, 0);
469 	}
470 
471 	negotiate_login(conn, pdu, req_ccb);
472 
473 	/* negotiate_login will decide whether login is complete or not */
474 	return 0;
475 }
476 
477 
478 /*
479  * receive_text_response_pdu
480  *    Handle receipt of a text response PDU.
481  *
482  *    Parameter:
483  *          conn     The connection
484  *          pdu      The PDU
485  *          req_CCB  The CCB associated with the original request (if any)
486  */
487 
488 STATIC int
489 receive_text_response_pdu(connection_t *conn, pdu_t *pdu, ccb_t *req_ccb)
490 {
491 	int rc;
492 
493 	DEBC(conn, 9, ("Received Text Response PDU, op=%x, flags=%x\n",
494 			pdu->pdu_hdr.pduh_Opcode, pdu->pdu_hdr.pduh_Flags));
495 
496 	if (check_StatSN(conn, pdu->pdu_hdr.pduh_p.text_rsp.StatSN, TRUE)) {
497 		return -1;
498 	}
499 	if (req_ccb == NULL) {
500 		DEBOUT(("Received unsolicited text response\n"));
501 		handle_connection_error(conn, ISCSI_STATUS_TARGET_ERROR,
502 							LOGOUT_CONNECTION);
503 		return -1;
504 	}
505 
506 	if (req_ccb->ccb_pdu_waiting != NULL) {
507 		ccb_timeout_start(req_ccb, COMMAND_TIMEOUT);
508 		req_ccb->ccb_num_timeouts = 0;
509 	}
510 
511 	if ((rc = collect_text_data(pdu, req_ccb)) != 0) {
512 		return max(0, rc);
513 	}
514 	negotiate_text(conn, pdu, req_ccb);
515 
516 	return 0;
517 }
518 
519 
520 /*
521  * receive_logout_pdu
522  *    Handle receipt of a logout response PDU.
523  *
524  *    Parameter:
525  *          conn     The connection
526  *          pdu      The PDU
527  *          req_CCB  The CCB associated with the original request (if any)
528  */
529 
530 STATIC int
531 receive_logout_pdu(connection_t *conn, pdu_t *pdu, ccb_t *req_ccb)
532 {
533 	bool otherconn;
534 	uint8_t response;
535 	uint32_t status;
536 
537 	otherconn = (req_ccb != NULL) ? (req_ccb->ccb_flags & CCBF_OTHERCONN) != 0 : 1;
538 	response = pdu->pdu_hdr.pduh_OpcodeSpecific [0];
539 	DEBC(conn, 1,
540 		("Received Logout PDU - CCB = %p, otherconn=%d, response=%d\n",
541 		req_ccb, otherconn, response));
542 
543 	if (req_ccb == NULL)
544 		return 0;
545 
546 	if (otherconn && check_StatSN(conn, pdu->pdu_hdr.pduh_p.logout_rsp.StatSN, TRUE))
547 		return -1;
548 
549 	switch (response) {
550 	case 0:
551 		status = ISCSI_STATUS_SUCCESS;
552 		break;
553 	case 1:
554 		status = ISCSI_STATUS_LOGOUT_CID_NOT_FOUND;
555 		break;
556 	case 2:
557 		status = ISCSI_STATUS_LOGOUT_RECOVERY_NS;
558 		break;
559 	default:
560 		status = ISCSI_STATUS_LOGOUT_ERROR;
561 		break;
562 	}
563 
564 	if (conn->c_session->s_ErrorRecoveryLevel >= 2 && response != 1) {
565 		connection_t *refconn = (otherconn) ? req_ccb->ccb_par : conn;
566 
567 		refconn->c_Time2Wait = ntohs(pdu->pdu_hdr.pduh_p.logout_rsp.Time2Wait);
568 		refconn->c_Time2Retain = ntohs(pdu->pdu_hdr.pduh_p.logout_rsp.Time2Retain);
569 	}
570 
571 	wake_ccb(req_ccb, status);
572 
573 	if (!otherconn && conn->c_state == ST_LOGOUT_SENT) {
574 		conn->c_terminating = ISCSI_STATUS_LOGOUT;
575 		conn->c_state = ST_SETTLING;
576 		conn->c_loggedout = (response) ? LOGOUT_FAILED : LOGOUT_SUCCESS;
577 
578 		connection_timeout_stop(conn);
579 
580 		/* let send thread take over next step of cleanup */
581 		mutex_enter(&conn->c_lock);
582 		cv_broadcast(&conn->c_conn_cv);
583 		mutex_exit(&conn->c_lock);
584 	}
585 
586 	return !otherconn;
587 }
588 
589 
590 /*
591  * receive_data_in_pdu
592  *    Handle receipt of a data in PDU.
593  *
594  *    Parameter:
595  *          conn     The connection
596  *          pdu      The PDU
597  *          req_CCB  The CCB associated with the original request (if any)
598  */
599 
600 STATIC int
601 receive_data_in_pdu(connection_t *conn, pdu_t *pdu, ccb_t *req_ccb)
602 {
603 	uint32_t dsl, sn;
604 	bool done;
605 	int rc;
606 
607 	dsl = ntoh3(pdu->pdu_hdr.pduh_DataSegmentLength);
608 
609 	if (req_ccb == NULL || !req_ccb->ccb_data_in || !req_ccb->ccb_data_len) {
610 		DEBOUT(("Received Data In, but req_ccb not waiting for it, ignored\n"));
611 		return 0;
612 	}
613 	req_ccb->ccb_flags |= CCBF_GOT_RSP;
614 
615 	if (req_ccb->ccb_pdu_waiting != NULL) {
616 		ccb_timeout_start(req_ccb, COMMAND_TIMEOUT);
617 		req_ccb->ccb_num_timeouts = 0;
618 	}
619 
620 	sn = ntohl(pdu->pdu_hdr.pduh_p.data_in.DataSN);
621 
622 	if ((rc = add_sernum(&req_ccb->ccb_DataSN_buf, sn)) != 1) {
623 		if (!rc) {
624 			return -1;
625 		}
626 		if (rc < 0) {
627 			DEBOUT(("Excessive outstanding Data PDUs\n"));
628 			handle_connection_error(req_ccb->ccb_connection,
629 				ISCSI_STATUS_PDUS_LOST, LOGOUT_CONNECTION);
630 			return -1;
631 		}
632 		DEBOUT(("Missing Data PDUs: First %d, num: %d\n",
633 				req_ccb->ccb_DataSN_buf.ExpSN, rc - 1));
634 
635 		if (conn->c_state == ST_FULL_FEATURE &&
636 			conn->c_session->s_ErrorRecoveryLevel) {
637 			snack_missing(req_ccb->ccb_connection, req_ccb,
638 				SNACK_DATA_NAK, req_ccb->ccb_DataSN_buf.ExpSN,
639 				rc - 1);
640 		} else {
641 			DEBOUT(("Killing connection (State=%d, ErrorRecoveryLevel=%d)\n",
642 					conn->c_state, conn->c_session->s_ErrorRecoveryLevel));
643 			handle_connection_error(conn, ISCSI_STATUS_PDUS_LOST,
644 						LOGOUT_CONNECTION);
645 			return -1;
646 		}
647 	}
648 
649 	ack_sernum(&req_ccb->ccb_DataSN_buf, sn);
650 
651 	req_ccb->ccb_xfer_len += dsl;
652 
653 	if ((pdu->pdu_hdr.pduh_Flags & FLAG_ACK) && conn->c_session->s_ErrorRecoveryLevel)
654 		send_snack(conn, pdu, req_ccb, SNACK_DATA_ACK);
655 
656 	done = sn_empty(&req_ccb->ccb_DataSN_buf);
657 
658 	if (pdu->pdu_hdr.pduh_Flags & FLAG_STATUS) {
659 		DEBC(conn, 10, ("Rx Data In %d, done = %d\n",
660 			req_ccb->ccb_CmdSN, done));
661 
662 		req_ccb->ccb_flags |= CCBF_COMPLETE;
663 		/* successful transfer, reset recover count */
664 		conn->c_recover = 0;
665 
666 		if (done)
667 			wake_ccb(req_ccb, ISCSI_STATUS_SUCCESS);
668 		if (check_StatSN(conn, pdu->pdu_hdr.pduh_p.data_in.StatSN, done))
669 			return -1;
670 
671 	} else if (done && (req_ccb->ccb_flags & CCBF_COMPLETE)) {
672 		wake_ccb(req_ccb, ISCSI_STATUS_SUCCESS);
673 	}
674 	/* else wait for command response */
675 
676 	return 0;
677 }
678 
679 
680 /*
681  * receive_r2t_pdu
682  *    Handle receipt of a R2T PDU.
683  *
684  *    Parameter:
685  *          conn     The connection
686  *          pdu      The PDU
687  *          req_CCB  The CCB associated with the original request (if any)
688  */
689 
690 STATIC int
691 receive_r2t_pdu(connection_t *conn, pdu_t *pdu, ccb_t *req_ccb)
692 {
693 
694 	DEBC(conn, 10, ("Received R2T PDU - CCB = %p\n", req_ccb));
695 
696 	if (req_ccb != NULL) {
697 		if (req_ccb->ccb_pdu_waiting != NULL) {
698 			ccb_timeout_start(req_ccb, COMMAND_TIMEOUT);
699 			req_ccb->ccb_num_timeouts = 0;
700 		}
701 		send_data_out(conn, pdu, req_ccb, CCBDISP_NOWAIT, TRUE);
702 	}
703 
704 	return 0;
705 }
706 
707 
708 /*
709  * receive_command_response_pdu
710  *    Handle receipt of a command response PDU.
711  *
712  *    Parameter:
713  *          conn     The connection
714  *          pdu      The PDU
715  *          req_CCB  The CCB associated with the original request (if any)
716  */
717 
718 STATIC int
719 receive_command_response_pdu(connection_t *conn, pdu_t *pdu, ccb_t *req_ccb)
720 {
721 	int len, rc;
722 	bool done;
723 	uint32_t status;
724 
725 	/* Read any provided data */
726 	if (pdu->pdu_temp_data_len && req_ccb != NULL && req_ccb->ccb_sense_len_req) {
727 		len = min(req_ccb->ccb_sense_len_req,
728 				  ntohs(*((uint16_t *) pdu->pdu_temp_data)));
729 		memcpy(req_ccb->ccb_sense_ptr, ((uint16_t *) pdu->pdu_temp_data) + 1,
730 					len);
731 		req_ccb->ccb_sense_len_got = len;
732 	}
733 
734 	if (req_ccb == NULL) {
735 		/* Assume duplicate... */
736 		DEBOUT(("Possibly duplicate command response (no associated CCB)\n"));
737 		return -1;
738 	}
739 
740 	if (req_ccb->ccb_flags & CCBF_COMPLETE) {
741 		DEBOUT(("Possibly duplicate command response (tagged as COMPLETE)\n"));
742 		return -1;
743 	}
744 
745 	if (req_ccb->ccb_pdu_waiting != NULL) {
746 		ccb_timeout_start(req_ccb, COMMAND_TIMEOUT);
747 		req_ccb->ccb_num_timeouts = 0;
748 	}
749 
750 	req_ccb->ccb_flags |= CCBF_COMPLETE;
751 	conn->c_recover = 0;	/* successful transfer, reset recover count */
752 
753 	if (pdu->pdu_hdr.pduh_OpcodeSpecific[0]) {	/* Response */
754 		status = ISCSI_STATUS_TARGET_FAILURE;
755 	} else {
756 		switch (pdu->pdu_hdr.pduh_OpcodeSpecific[1]) {	/* Status */
757 		case 0x00:
758 			status = ISCSI_STATUS_SUCCESS;
759 			break;
760 
761 		case 0x02:
762 			status = ISCSI_STATUS_CHECK_CONDITION;
763 			break;
764 
765 		case 0x08:
766 			status = ISCSI_STATUS_TARGET_BUSY;
767 			break;
768 
769 		default:
770 			status = ISCSI_STATUS_TARGET_ERROR;
771 			break;
772 		}
773 	}
774 
775 	if (pdu->pdu_hdr.pduh_Flags & (FLAG_OVERFLOW | FLAG_UNDERFLOW))
776 		req_ccb->ccb_residual = ntohl(pdu->pdu_hdr.pduh_p.response.ResidualCount);
777 
778 	done = status || sn_empty(&req_ccb->ccb_DataSN_buf);
779 
780 	DEBC(conn, 10, ("Rx Response: CmdSN %d, rsp = %x, status = %x\n",
781 			req_ccb->ccb_CmdSN,
782 			pdu->pdu_hdr.pduh_OpcodeSpecific[0],
783 			pdu->pdu_hdr.pduh_OpcodeSpecific[1]));
784 
785 	rc = check_StatSN(conn, pdu->pdu_hdr.pduh_p.response.StatSN, done);
786 
787 	if (done)
788 		wake_ccb(req_ccb, status);
789 
790 	return rc;
791 }
792 
793 
794 /*
795  * receive_asynch_pdu
796  *    Handle receipt of an asynchronous message PDU.
797  *
798  *    Parameter:
799  *          conn     The connection
800  *          pdu      The PDU
801  */
802 
803 STATIC int
804 receive_asynch_pdu(connection_t *conn, pdu_t *pdu)
805 {
806 
807 	DEBOUT(("Received Asynch PDU, Event %d\n", pdu->pdu_hdr.pduh_p.asynch.AsyncEvent));
808 
809 	switch (pdu->pdu_hdr.pduh_p.asynch.AsyncEvent) {
810 	case 0:		   		/* SCSI Asynch event. Don't know what to do with it... */
811 		break;
812 
813 	case 1:		   		/* Target requests logout. */
814 		if (conn->c_session->s_active_connections > 1) {
815 			kill_connection(conn, ISCSI_STATUS_TARGET_LOGOUT,
816 						LOGOUT_CONNECTION, FALSE);
817 		} else {
818 			kill_session(conn->c_session, ISCSI_STATUS_TARGET_LOGOUT,
819 						 LOGOUT_SESSION, FALSE);
820 		}
821 		break;
822 
823 	case 2:				/* Target is dropping connection */
824 		conn = find_connection(conn->c_session,
825 					ntohs(pdu->pdu_hdr.pduh_p.asynch.Parameter1));
826 		if (conn != NULL) {
827 			conn->c_Time2Wait = ntohs(pdu->pdu_hdr.pduh_p.asynch.Parameter2);
828 			conn->c_Time2Retain = ntohs(pdu->pdu_hdr.pduh_p.asynch.Parameter3);
829 			kill_connection(conn, ISCSI_STATUS_TARGET_DROP,
830 					NO_LOGOUT, TRUE);
831 		}
832 		break;
833 
834 	case 3:				/* Target is dropping all connections of session */
835 		conn->c_session->s_DefaultTime2Wait = ntohs(pdu->pdu_hdr.pduh_p.asynch.Parameter2);
836 		conn->c_session->s_DefaultTime2Retain = ntohs(pdu->pdu_hdr.pduh_p.asynch.Parameter3);
837 		kill_session(conn->c_session, ISCSI_STATUS_TARGET_DROP, NO_LOGOUT, TRUE);
838 		break;
839 
840 	case 4:				/* Target requests parameter negotiation */
841 		start_text_negotiation(conn);
842 		break;
843 
844 	default:
845 		/* ignore */
846 		break;
847 	}
848 	return 0;
849 }
850 
851 
852 /*
853  * receive_reject_pdu
854  *    Handle receipt of a reject PDU.
855  *
856  *    Parameter:
857  *          conn     The connection
858  *          pdu      The PDU
859  */
860 
861 STATIC int
862 receive_reject_pdu(connection_t *conn, pdu_t *pdu)
863 {
864 	pdu_header_t *hpdu;
865 	ccb_t *req_ccb;
866 	uint32_t status;
867 
868 	DEBOUT(("Received Reject PDU, reason = %x, data_len = %d\n",
869 			pdu->pdu_hdr.pduh_OpcodeSpecific[0], pdu->pdu_temp_data_len));
870 
871 	if (pdu->pdu_temp_data_len >= BHS_SIZE) {
872 		hpdu = (pdu_header_t *) pdu->pdu_temp_data;
873 		req_ccb = ccb_from_itt(conn, hpdu->pduh_InitiatorTaskTag);
874 
875 		DEBC(conn, 9, ("Reject PDU ITT (ccb)= %x (%p)\n",
876 				hpdu->pduh_InitiatorTaskTag, req_ccb));
877 		if (!req_ccb) {
878 			return 0;
879 		}
880 		switch (pdu->pdu_hdr.pduh_OpcodeSpecific[0]) {
881 		case REJECT_DIGEST_ERROR:
882 			/* don't retransmit data out */
883 			if ((hpdu->pduh_Opcode & OPCODE_MASK) == IOP_SCSI_Data_out)
884 				return 0;
885 			resend_pdu(req_ccb);
886 			return 0;
887 
888 		case REJECT_IMMED_COMMAND:
889 		case REJECT_LONG_OPERATION:
890 			resend_pdu(req_ccb);
891 			return 0;
892 
893 		case REJECT_SNACK:
894 		case REJECT_PROTOCOL_ERROR:
895 			status = ISCSI_STATUS_PROTOCOL_ERROR;
896 			break;
897 
898 		case REJECT_CMD_NOT_SUPPORTED:
899 			status = ISCSI_STATUS_CMD_NOT_SUPPORTED;
900 			break;
901 
902 		case REJECT_INVALID_PDU_FIELD:
903 			status = ISCSI_STATUS_PDU_ERROR;
904 			break;
905 
906 		default:
907 			status = ISCSI_STATUS_GENERAL_ERROR;
908 			break;
909 		}
910 
911 		wake_ccb(req_ccb, status);
912 		handle_connection_error(conn, ISCSI_STATUS_PROTOCOL_ERROR,
913 							LOGOUT_CONNECTION);
914 	}
915 	return 0;
916 }
917 
918 
919 /*
920  * receive_task_management_pdu
921  *    Handle receipt of a task management PDU.
922  *
923  *    Parameter:
924  *          conn     The connection
925  *          pdu      The PDU
926  *          req_CCB  The CCB associated with the original request (if any)
927  */
928 
929 STATIC int
930 receive_task_management_pdu(connection_t *conn, pdu_t *pdu, ccb_t *req_ccb)
931 {
932 	uint32_t status;
933 
934 	DEBC(conn, 2, ("Received Task Management PDU, response %d, req_ccb %p\n",
935 			pdu->pdu_hdr.pduh_OpcodeSpecific[0], req_ccb));
936 
937 	if (req_ccb != NULL) {
938 		switch (pdu->pdu_hdr.pduh_OpcodeSpecific[0]) {	/* Response */
939 		case 0:
940 			status = ISCSI_STATUS_SUCCESS;
941 			break;
942 		case 1:
943 			status = ISCSI_STATUS_TASK_NOT_FOUND;
944 			break;
945 		case 2:
946 			status = ISCSI_STATUS_LUN_NOT_FOUND;
947 			break;
948 		case 3:
949 			status = ISCSI_STATUS_TASK_ALLEGIANT;
950 			break;
951 		case 4:
952 			status = ISCSI_STATUS_CANT_REASSIGN;
953 			break;
954 		case 5:
955 			status = ISCSI_STATUS_FUNCTION_UNSUPPORTED;
956 			break;
957 		case 6:
958 			status = ISCSI_STATUS_FUNCTION_NOT_AUTHORIZED;
959 			break;
960 		case 255:
961 			status = ISCSI_STATUS_FUNCTION_REJECTED;
962 			break;
963 		default:
964 			status = ISCSI_STATUS_UNKNOWN_REASON;
965 			break;
966 		}
967 		wake_ccb(req_ccb, status);
968 	}
969 
970 	check_StatSN(conn, pdu->pdu_hdr.pduh_p.task_rsp.StatSN, TRUE);
971 
972 	return 0;
973 }
974 
975 
976 /*
977  * receive_nop_in_pdu
978  *    Handle receipt of a Nop-In PDU.
979  *
980  *    Parameter:
981  *          conn     The connection
982  *          pdu      The PDU
983  *          req_CCB  The CCB associated with the original request (if any)
984  */
985 
986 STATIC int
987 receive_nop_in_pdu(connection_t *conn, pdu_t *pdu, ccb_t *req_ccb)
988 {
989 	DEBC(conn, 10,
990 		("Received NOP_In PDU, req_ccb=%p, ITT=%x, TTT=%x, StatSN=%u\n",
991 		req_ccb, pdu->pdu_hdr.pduh_InitiatorTaskTag,
992 		pdu->pdu_hdr.pduh_p.nop_in.TargetTransferTag,
993 		ntohl(pdu->pdu_hdr.pduh_p.nop_in.StatSN)));
994 
995 	if (pdu->pdu_hdr.pduh_InitiatorTaskTag == 0xffffffff) {
996 		/* this is a target ping - respond with a pong */
997 		if (pdu->pdu_hdr.pduh_p.nop_in.TargetTransferTag != 0xffffffff)
998 			send_nop_out(conn, pdu);
999 
1000 		/*
1001 		   Any receive resets the connection timeout, but we got a ping, which
1002 		   means that it's likely the other side was waiting for something to
1003 		   happen on the connection. If we aren't idle, send a ping right
1004 		   away to synch counters (don't synch on this ping because other
1005 		   PDUs may be on the way).
1006 		 */
1007 		if (TAILQ_FIRST(&conn->c_ccbs_waiting) != NULL)
1008 			send_nop_out(conn, NULL);
1009 	} else if (req_ccb != NULL) {
1010 		/* this is a solicited ping, check CmdSN for lost commands */
1011 		/* and advance StatSN */
1012 		check_CmdSN(conn, pdu->pdu_hdr.pduh_p.nop_in.ExpCmdSN);
1013 
1014 		wake_ccb(req_ccb, ISCSI_STATUS_SUCCESS);
1015 
1016 		check_StatSN(conn, pdu->pdu_hdr.pduh_p.nop_in.StatSN, TRUE);
1017 	} else {
1018 		DEBC(conn, 0, ("Received unsolicted NOP_In, itt=%08x\n",
1019 		               pdu->pdu_hdr.pduh_InitiatorTaskTag));
1020 	}
1021 
1022 	return 0;
1023 }
1024 
1025 
1026 /*
1027  * receive_pdu
1028  *    Get parameters, call the appropriate handler for a received PDU.
1029  *
1030  *    Parameter:
1031  *          conn     The connection
1032  *          pdu      The PDU
1033  *
1034  *    Returns:    0 on success, nonzero if the connection is broken.
1035  */
1036 
1037 STATIC int
1038 receive_pdu(connection_t *conn, pdu_t *pdu)
1039 {
1040 	ccb_t *req_ccb;
1041 	int rc;
1042 	uint32_t MaxCmdSN, ExpCmdSN, digest;
1043 	session_t *sess = conn->c_session;
1044 
1045 	if (conn->c_HeaderDigest) {
1046 		digest = gen_digest(&pdu->pdu_hdr, BHS_SIZE);
1047 		if (digest != pdu->pdu_hdr.pduh_HeaderDigest) {
1048 			DEBOUT(("Header Digest Error: comp = %08x, rx = %08x\n",
1049 					digest, pdu->pdu_hdr.pduh_HeaderDigest));
1050 			/* try to skip to next PDU */
1051 			try_resynch_receive(conn);
1052 			free_pdu(pdu);
1053 			return 0;
1054 		}
1055 	}
1056 
1057 	DEBC(conn, 10, ("Received PDU StatSN=%u, ExpCmdSN=%u MaxCmdSN=%u ExpDataSN=%u\n",
1058 	     ntohl(pdu->pdu_hdr.pduh_p.response.StatSN),
1059 	     ntohl(pdu->pdu_hdr.pduh_p.response.ExpCmdSN),
1060 	     ntohl(pdu->pdu_hdr.pduh_p.response.MaxCmdSN),
1061 	     ntohl(pdu->pdu_hdr.pduh_p.response.ExpDataSN)));
1062 
1063 	req_ccb = ccb_from_itt(conn, pdu->pdu_hdr.pduh_InitiatorTaskTag);
1064 
1065 	if (req_ccb != NULL && req_ccb->ccb_data_in && req_ccb->ccb_data_len &&
1066 		(pdu->pdu_hdr.pduh_Opcode & OPCODE_MASK) == TOP_SCSI_Data_in) {
1067 		uint32_t dsl, offset;
1068 
1069 		dsl = ntoh3(pdu->pdu_hdr.pduh_DataSegmentLength);
1070 		offset = ntohl(pdu->pdu_hdr.pduh_p.data_in.BufferOffset);
1071 
1072 		if ((offset + dsl) > req_ccb->ccb_data_len) {
1073 			DEBOUT(("Received more data than requested (len %d, offset %d)\n",
1074 					dsl, offset));
1075 			handle_connection_error(conn, ISCSI_STATUS_TARGET_ERROR, NO_LOGOUT);
1076 			return 1;
1077 		}
1078 		DEBC(conn, 10,
1079 			("Received Data in PDU - CCB = %p, Datalen = %d, Offset = %d\n",
1080 			req_ccb, dsl, offset));
1081 
1082 		rc = read_pdu_data(pdu, req_ccb->ccb_data_ptr, offset);
1083 	} else {
1084 		rc = read_pdu_data(pdu, NULL, 0);
1085 	}
1086 	if (!rc && (conn->c_state <= ST_WINDING_DOWN ||
1087 		(pdu->pdu_hdr.pduh_Opcode & OPCODE_MASK) == TOP_Logout_Response)) {
1088 
1089 		switch (pdu->pdu_hdr.pduh_Opcode & OPCODE_MASK) {
1090 		case TOP_NOP_In:
1091 			rc = receive_nop_in_pdu(conn, pdu, req_ccb);
1092 			break;
1093 
1094 		case TOP_SCSI_Response:
1095 			rc = receive_command_response_pdu(conn, pdu, req_ccb);
1096 			break;
1097 
1098 		case TOP_SCSI_Task_Management:
1099 			rc = receive_task_management_pdu(conn, pdu, req_ccb);
1100 			break;
1101 
1102 		case TOP_Login_Response:
1103 			rc = receive_login_pdu(conn, pdu, req_ccb);
1104 			break;
1105 
1106 		case TOP_Text_Response:
1107 			rc = receive_text_response_pdu(conn, pdu, req_ccb);
1108 			break;
1109 
1110 		case TOP_SCSI_Data_in:
1111 			rc = receive_data_in_pdu(conn, pdu, req_ccb);
1112 			break;
1113 
1114 		case TOP_Logout_Response:
1115 			rc = receive_logout_pdu(conn, pdu, req_ccb);
1116 			break;
1117 
1118 		case TOP_R2T:
1119 			rc = receive_r2t_pdu(conn, pdu, req_ccb);
1120 			break;
1121 
1122 		case TOP_Asynchronous_Message:
1123 			rc = receive_asynch_pdu(conn, pdu);
1124 			break;
1125 
1126 		case TOP_Reject:
1127 			rc = receive_reject_pdu(conn, pdu);
1128 			break;
1129 
1130 		default:
1131 			DEBOUT(("Received Invalid Opcode %x\n", pdu->pdu_hdr.pduh_Opcode));
1132 			try_resynch_receive(conn);
1133 			rc = -1;
1134 			break;
1135 		}
1136 	}
1137 
1138 	free_pdu(pdu);
1139 	if (rc)
1140 		return rc;
1141 
1142 	/* MaxCmdSN and ExpCmdSN are in the same place in all received PDUs */
1143 	ExpCmdSN = ntohl(pdu->pdu_hdr.pduh_p.nop_in.ExpCmdSN);
1144 	MaxCmdSN = ntohl(pdu->pdu_hdr.pduh_p.nop_in.MaxCmdSN);
1145 
1146 	/* received a valid frame, reset timeout */
1147 	if ((pdu->pdu_hdr.pduh_Opcode & OPCODE_MASK) == TOP_NOP_In &&
1148 	    TAILQ_EMPTY(&conn->c_ccbs_waiting))
1149 		connection_timeout_start(conn, conn->c_idle_timeout_val);
1150 	else
1151 		connection_timeout_start(conn, CONNECTION_TIMEOUT);
1152 	conn->c_num_timeouts = 0;
1153 
1154 	/* Update session window */
1155 	mutex_enter(&sess->s_lock);
1156 	if (sn_a_le_b(ExpCmdSN - 1, MaxCmdSN)) {
1157 		if (sn_a_lt_b(sess->s_ExpCmdSN, ExpCmdSN))
1158 			sess->s_ExpCmdSN = ExpCmdSN;
1159 		if (sn_a_lt_b(sess->s_MaxCmdSN, MaxCmdSN))
1160 			sess->s_MaxCmdSN = MaxCmdSN;
1161 	}
1162 	mutex_exit(&sess->s_lock);
1163 
1164 	return 0;
1165 }
1166 
1167 /*****************************************************************************/
1168 
1169 /*
1170  * iscsi_receive_thread
1171  *    Per connection thread handling receive data.
1172  *
1173  *    Parameter:
1174  *          conn     The connection
1175  */
1176 
1177 void
1178 iscsi_rcv_thread(void *par)
1179 {
1180 	connection_t *conn = (connection_t *) par;
1181 	pdu_t *pdu;
1182 	size_t hlen;
1183 
1184 	do {
1185 		while (!conn->c_terminating) {
1186 			pdu = get_pdu(conn, TRUE);
1187 			if (pdu == NULL) {
1188 				KASSERT(conn->c_terminating);
1189 				break;
1190 			}
1191 
1192 			pdu->pdu_uio.uio_iov = pdu->pdu_io_vec;
1193 			UIO_SETUP_SYSSPACE(&pdu->pdu_uio);
1194 			pdu->pdu_uio.uio_iovcnt = 1;
1195 			pdu->pdu_uio.uio_rw = UIO_READ;
1196 
1197 			pdu->pdu_io_vec[0].iov_base = &pdu->pdu_hdr;
1198 			hlen = (conn->c_HeaderDigest) ? BHS_SIZE + 4 : BHS_SIZE;
1199 			pdu->pdu_io_vec[0].iov_len = hlen;
1200 			pdu->pdu_uio.uio_resid = hlen;
1201 
1202 			DEBC(conn, 99, ("Receive thread waiting for data\n"));
1203 			if (my_soo_read(conn, &pdu->pdu_uio, MSG_WAITALL)) {
1204 				free_pdu(pdu);
1205 				break;
1206 			}
1207 			/* Check again for header digest */
1208 			/* (it may have changed during the wait) */
1209 			if (hlen == BHS_SIZE && conn->c_HeaderDigest) {
1210 				pdu->pdu_uio.uio_iov = pdu->pdu_io_vec;
1211 				pdu->pdu_uio.uio_iovcnt = 1;
1212 				pdu->pdu_io_vec[0].iov_base = &pdu->pdu_hdr.pduh_HeaderDigest;
1213 				pdu->pdu_io_vec[0].iov_len = 4;
1214 				pdu->pdu_uio.uio_resid = 4;
1215 				if (my_soo_read(conn, &pdu->pdu_uio, MSG_WAITALL)) {
1216 					free_pdu(pdu);
1217 					break;
1218 				}
1219 			}
1220 
1221 			if (receive_pdu(conn, pdu) > 0) {
1222 				break;
1223 			}
1224 		}
1225 		mutex_enter(&conn->c_lock);
1226 		if (!conn->c_destroy) {
1227 			cv_timedwait(&conn->c_idle_cv, &conn->c_lock, CONNECTION_IDLE_TIMEOUT);
1228 		}
1229 		mutex_exit(&conn->c_lock);
1230 	} while (!conn->c_destroy);
1231 
1232 	conn->c_rcvproc = NULL;
1233 	DEBC(conn, 5, ("Receive thread exits\n"));
1234 	kthread_exit(0);
1235 }
1236