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