Module Name:    src
Committed By:   mlelstv
Date:           Wed Jun 15 04:30:30 UTC 2016

Modified Files:
        src/sys/dev/iscsi: iscsi.h iscsi_globals.h iscsi_ioctl.c iscsi_main.c
            iscsi_rcv.c iscsi_send.c iscsi_utils.c

Log Message:
Remove throttling code, instead signal scsipi layer to reduce the openings
and retry the command. Start with a small openings number and let scsipi
request to grow it up to the current send window.

Adjust ccb and pdu counts to avoid ressource shortages. These are still
very ad-hoc numbers, but seem to be sufficient for a Gigabit link.

Use separate condvar for PDU pool and add counter to help debugging.

Revert setting PDU disposition to UNUSED before freeing. free_pdu
uses this as a flag to detect already returned PDUs.

Add reference counter for open commands to defer unmapping a session
that would lead to crashes in scsipi.

Move session cleanup to cleanup thread.

Use get_sernum to retrieve current serial number where possible and
make it check for immediate commands itself.

Adjust debug output.


To generate a diff of this commit:
cvs rdiff -u -r1.3 -r1.4 src/sys/dev/iscsi/iscsi.h
cvs rdiff -u -r1.20 -r1.21 src/sys/dev/iscsi/iscsi_globals.h \
    src/sys/dev/iscsi/iscsi_rcv.c src/sys/dev/iscsi/iscsi_utils.c
cvs rdiff -u -r1.21 -r1.22 src/sys/dev/iscsi/iscsi_ioctl.c
cvs rdiff -u -r1.22 -r1.23 src/sys/dev/iscsi/iscsi_main.c
cvs rdiff -u -r1.30 -r1.31 src/sys/dev/iscsi/iscsi_send.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/dev/iscsi/iscsi.h
diff -u src/sys/dev/iscsi/iscsi.h:1.3 src/sys/dev/iscsi/iscsi.h:1.4
--- src/sys/dev/iscsi/iscsi.h:1.3	Sat Nov 19 16:41:56 2011
+++ src/sys/dev/iscsi/iscsi.h	Wed Jun 15 04:30:30 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: iscsi.h,v 1.3 2011/11/19 16:41:56 agc Exp $	*/
+/*	$NetBSD: iscsi.h,v 1.4 2016/06/15 04:30:30 mlelstv Exp $	*/
 
 /*-
  * Copyright (c) 2004,2006,2011 The NetBSD Foundation, Inc.
@@ -182,6 +182,7 @@ typedef struct {
 #define ISCSI_STATUS_LOGOUT_CID_NOT_FOUND    45	/* Logout error: CID not found */
 #define ISCSI_STATUS_LOGOUT_RECOVERY_NS	     46	/* Logout error: Recovery not supported */
 #define ISCSI_STATUS_LOGOUT_ERROR	     47	/* Logout error: Unknown reason */
+#define ISCSI_STATUS_QUEUE_FULL              48 /* iSCSI send window exhausted */
 
 #define ISCSID_STATUS_SUCCESS                0		/* Indicates success. */
 #define ISCSID_STATUS_LIST_EMPTY             1001	/* The requested list is empty. */

Index: src/sys/dev/iscsi/iscsi_globals.h
diff -u src/sys/dev/iscsi/iscsi_globals.h:1.20 src/sys/dev/iscsi/iscsi_globals.h:1.21
--- src/sys/dev/iscsi/iscsi_globals.h:1.20	Wed Jun 15 03:51:55 2016
+++ src/sys/dev/iscsi/iscsi_globals.h	Wed Jun 15 04:30:30 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: iscsi_globals.h,v 1.20 2016/06/15 03:51:55 mlelstv Exp $	*/
+/*	$NetBSD: iscsi_globals.h,v 1.21 2016/06/15 04:30:30 mlelstv Exp $	*/
 
 /*-
  * Copyright (c) 2004,2005,2006,2011 The NetBSD Foundation, Inc.
@@ -71,33 +71,25 @@
 #define VERSION_STRING		"NetBSD iSCSI Software Initiator 20110407"
 
 /*
-Various checks are made that the expected cmd Serial Number is less than
-the actual command serial number. The extremely paranoid amongst us
-believe that a malicious iSCSI server could set this artificially low
-and effectively DoS a naive initiator. For this (possibly ludicrous)
-reason, I have added the two definitions below (agc, 2011/04/09). The
-throttling definition enables a check that the CmdSN is less than the
-ExpCmdSN in iscsi_send.c, and is enabled by default. The second definition
-effectively says "don't bother testing these values", and is used right
-now only in iscsi_send.c.
- */
-#define ISCSI_THROTTLING_ENABLED	1
-#define ISCSI_SERVER_TRUSTED		0
-
-/*
    NOTE: CCBS_PER_SESSION must not exceed 256 due to the way the ITT
    is constructed (it has the CCB index in its lower 8 bits). If it should ever
    be necessary to increase the number beyond that (which isn't expected),
    the corresponding ITT generation and extraction code must be rewritten.
 */
-#define CCBS_PER_SESSION      64	/* ToDo: Reasonable number?? */
+#define CCBS_PER_SESSION      32	/* ToDo: Reasonable number?? */
+/*
+   NOTE: CCBS_FOR_SCSPI limits the number of outstanding commands for
+   SCSI commands, leaving some CCBs for keepalive and logout attempts,
+   which are needed for each connection.
+*/
+#define CCBS_FOR_SCSIPI       16	/* ToDo: Reasonable number?? */
 /*
    NOTE: PDUS_PER_CONNECTION is a number that could potentially impact
    performance if set too low, as a single command may use up a lot of PDUs for
    high values of First/MaxBurstLength and small values of
    MaxRecvDataSegmentLength of the target.
 */
-#define PDUS_PER_CONNECTION   128	/* ToDo: Reasonable number?? */
+#define PDUS_PER_CONNECTION   64	/* ToDo: Reasonable number?? */
 
 /* max outstanding serial nums before we give up on the connection */
 #define SERNUM_BUFFER_LENGTH  (CCBS_PER_SESSION / 2)	/* ToDo: Reasonable?? */
@@ -106,7 +98,7 @@ now only in iscsi_send.c.
 #define DEFAULT_MaxRecvDataSegmentLength     (64*1024)
 
 /* Command timeout (reset on received PDU associated with the command's CCB) */
-#define COMMAND_TIMEOUT		(7 * hz) /* ToDo: Reasonable? (7 seconds) */
+#define COMMAND_TIMEOUT		(60 * hz) /* ToDo: Reasonable? (60 seconds) */
 #define MAX_CCB_TIMEOUTS	3		/* Max number of tries to resend or SNACK */
 #define MAX_CCB_TRIES		9      	/* Max number of total tries to recover */
 
@@ -131,12 +123,10 @@ now only in iscsi_send.c.
 #define CCBF_COMPLETE   0x0001	/* received status */
 #define CCBF_RESENT     0x0002	/* ccb was resent */
 #define CCBF_SENDTARGET 0x0004	/* SendTargets text request, not negotiation */
-#define CCBF_WAITING    0x0008	/* CCB is waiting for MaxCmdSN, wake it up */
 #define CCBF_GOT_RSP    0x0010	/* Got at least one response to this request */
 #define CCBF_REASSIGN   0x0020	/* Command can be reassigned */
 #define CCBF_OTHERCONN  0x0040	/* a logout for a different connection */
 #define CCBF_WAITQUEUE  0x0080	/* CCB is on waiting queue */
-#define CCBF_THROTTLING 0x0100	/* CCB is on throttling queue */
 
 /* ---------------------------  Global Types  ------------------------------- */
 
@@ -322,6 +312,7 @@ struct connection_s {
 
 	kmutex_t			lock;
 	kcondvar_t			conn_cv;
+	kcondvar_t			pdu_cv;
 	kcondvar_t			ccb_cv;
 	kcondvar_t			idle_cv;
 
@@ -375,6 +366,7 @@ struct connection_s {
 	int				recover; /* recovery count */
 		/* (reset on first successful data transfer) */
 	volatile unsigned		usecount; /* number of active CCBs */
+	volatile unsigned		pducount; /* number of active PDUs */
 
 	bool				destroy; /* conn will be destroyed */
 	bool				in_session;
@@ -417,6 +409,8 @@ struct session_s {
 	device_t		child_dev;
 	/* the child we're associated with - (NULL if not mapped) */
 
+	int			refcount;	/* session in use by scsipi */
+
 	/* local stuff */
 	TAILQ_ENTRY(session_s)	sessions;	/* the list of sessions */
 
@@ -425,8 +419,8 @@ struct session_s {
 	kcondvar_t		ccb_cv;
 
 	ccb_list_t		ccb_pool;	/* The free CCB pool */
-	ccb_list_t		ccbs_throttled;
-				/* CCBs waiting for MaxCmdSN to increase */
+
+	int			send_window;
 
 	uint16_t		id;	/* session ID (unique within driver) */
 	uint16_t		TSIH;	/* Target assigned session ID */
@@ -638,7 +632,7 @@ sn_a_le_b(uint32_t a, uint32_t b)
 /* in iscsi_ioctl.c */
 
 void iscsi_init_cleanup(void);
-void iscsi_destroy_cleanup(void);
+int iscsi_destroy_cleanup(void);
 void iscsi_notify_cleanup(void);
 
 
@@ -652,7 +646,7 @@ void add_event(iscsi_event_t, uint32_t, 
 
 void kill_connection(connection_t *, uint32_t, int, bool);
 void kill_session(session_t *, uint32_t, int, bool);
-void kill_all_sessions(void);
+int kill_all_sessions(void);
 void handle_connection_error(connection_t *, uint32_t, int);
 void add_connection_cleanup(connection_t *);
 
@@ -664,7 +658,8 @@ int iscsiioctl(struct file *, u_long, vo
 
 session_t *find_session(uint32_t);
 connection_t *find_connection(session_t *, uint32_t);
-
+int ref_session(session_t *);
+void unref_session(session_t *);
 
 /* in iscsi_main.c */
 
@@ -725,7 +720,6 @@ void create_ccbs(session_t *);
 ccb_t *get_ccb(connection_t *, bool);
 void free_ccb(ccb_t *);
 void suspend_ccb(ccb_t *, bool);
-void throttle_ccb(ccb_t *, bool);
 void wake_ccb(ccb_t *, uint32_t);
 
 void create_pdus(connection_t *);
@@ -736,8 +730,9 @@ void init_sernum(sernum_buffer_t *);
 int add_sernum(sernum_buffer_t *, uint32_t);
 uint32_t ack_sernum(sernum_buffer_t *, uint32_t);
 
-uint32_t get_sernum(session_t *, bool);
+uint32_t get_sernum(session_t *, pdu_t *);
 int sernum_in_window(session_t *);
+int window_size(session_t *, int);
 
 /* in iscsi_text.c */
 
Index: src/sys/dev/iscsi/iscsi_rcv.c
diff -u src/sys/dev/iscsi/iscsi_rcv.c:1.20 src/sys/dev/iscsi/iscsi_rcv.c:1.21
--- src/sys/dev/iscsi/iscsi_rcv.c:1.20	Sun Jun  5 14:00:12 2016
+++ src/sys/dev/iscsi/iscsi_rcv.c	Wed Jun 15 04:30:30 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: iscsi_rcv.c,v 1.20 2016/06/05 14:00:12 mlelstv Exp $	*/
+/*	$NetBSD: iscsi_rcv.c,v 1.21 2016/06/15 04:30:30 mlelstv Exp $	*/
 
 /*-
  * Copyright (c) 2004,2005,2006,2011 The NetBSD Foundation, Inc.
@@ -645,7 +645,8 @@ receive_data_in_pdu(connection_t *conn, 
 	done = sn_empty(&req_ccb->DataSN_buf);
 
 	if (pdu->pdu.Flags & FLAG_STATUS) {
-		DEBC(conn, 10, ("Rx Data In Complete, done = %d\n", done));
+		DEBC(conn, 10, ("Rx Data In %d, done = %d\n",
+			req_ccb->CmdSN, done));
 
 		req_ccb->flags |= CCBF_COMPLETE;
 		/* successful transfer, reset recover count */
@@ -760,8 +761,10 @@ receive_command_response_pdu(connection_
 
 	done = status || sn_empty(&req_ccb->DataSN_buf);
 
-	DEBC(conn, 10, ("Rx Command Response rsp = %x, status = %x\n",
-			pdu->pdu.OpcodeSpecific[0], pdu->pdu.OpcodeSpecific[1]));
+	DEBC(conn, 10, ("Rx Response: CmdSN %d, rsp = %x, status = %x\n",
+			req_ccb->CmdSN,
+			pdu->pdu.OpcodeSpecific[0],
+			pdu->pdu.OpcodeSpecific[1]));
 
 	rc = check_StatSN(conn, pdu->pdu.p.response.StatSN, done);
 
@@ -968,7 +971,7 @@ STATIC int
 receive_nop_in_pdu(connection_t *conn, pdu_t *pdu, ccb_t *req_ccb)
 {
 	DEBC(conn, 10,
-		("Received NOP_In PDU, req_ccb=%p, ITT=%x, TTT=%x, StatSN=%x\n",
+		("Received NOP_In PDU, req_ccb=%p, ITT=%x, TTT=%x, StatSN=%u\n",
 		req_ccb, pdu->pdu.InitiatorTaskTag,
 		pdu->pdu.p.nop_in.TargetTransferTag,
 		ntohl(pdu->pdu.p.nop_in.StatSN)));
@@ -1019,7 +1022,6 @@ STATIC int
 receive_pdu(connection_t *conn, pdu_t *pdu)
 {
 	ccb_t *req_ccb;
-	ccb_list_t waiting;
 	int rc;
 	uint32_t MaxCmdSN, ExpCmdSN, digest;
 	session_t *sess = conn->session;
@@ -1036,9 +1038,11 @@ receive_pdu(connection_t *conn, pdu_t *p
 		}
 	}
 
-	DEBC(conn, 99, ("Received PDU ExpCmdSN = %u, MaxCmdSN = %u\n",
+	DEBC(conn, 10, ("Received PDU StatSN=%u, ExpCmdSN=%u MaxCmdSN=%u ExpDataSN=%u\n",
+	     ntohl(pdu->pdu.p.response.StatSN),
 	     ntohl(pdu->pdu.p.response.ExpCmdSN),
-	     ntohl(pdu->pdu.p.response.ExpCmdSN)));
+	     ntohl(pdu->pdu.p.response.MaxCmdSN),
+	     ntohl(pdu->pdu.p.response.ExpDataSN)));
 
 	req_ccb = ccb_from_itt(conn, pdu->pdu.InitiatorTaskTag);
 
@@ -1131,65 +1135,14 @@ receive_pdu(connection_t *conn, pdu_t *p
 		connection_timeout_start(conn, CONNECTION_TIMEOUT);
 	conn->num_timeouts = 0;
 
-	/*
-	 * Un-throttle - wakeup all CCBs waiting for MaxCmdSN to increase.
-	 * We have to handle wait/nowait CCBs a bit differently.
-	 */
+	/* Update session window */
 	mutex_enter(&sess->lock);
-
-	if (sn_a_lt_b(MaxCmdSN, ExpCmdSN-1)) {
-		/* both are ignored */
-		mutex_exit(&sess->lock);
-		return 0;
+	if (sn_a_le_b(ExpCmdSN - 1, MaxCmdSN)) {
+		if (sn_a_lt_b(sess->ExpCmdSN, ExpCmdSN))
+			sess->ExpCmdSN = ExpCmdSN;
+		if (sn_a_lt_b(sess->MaxCmdSN, MaxCmdSN))
+			sess->MaxCmdSN = MaxCmdSN;
 	}
-
-	if (sn_a_lt_b(sess->ExpCmdSN, ExpCmdSN))
-		sess->ExpCmdSN = ExpCmdSN;
-
-	if (sn_a_lt_b(sess->MaxCmdSN, MaxCmdSN)) {
-		sess->MaxCmdSN = MaxCmdSN;
-
-		if (TAILQ_FIRST(&sess->ccbs_throttled) == NULL) {
-			mutex_exit(&sess->lock);
-			return 0;
-		}
-
-		DEBC(conn, 5, ("Unthrottling - MaxCmdSN = %d\n", MaxCmdSN));
-
-		TAILQ_INIT(&waiting);
-		while ((req_ccb = TAILQ_FIRST(&sess->ccbs_throttled)) != NULL) {
-			if (!conn->terminating ||
-			    (req_ccb->flags & CCBF_WAITING) != 0) {
-				throttle_ccb(req_ccb, FALSE);
-				TAILQ_INSERT_TAIL(&waiting, req_ccb, chain);
-			}
-		}
-
-		while ((req_ccb = TAILQ_FIRST(&waiting)) != NULL) {
-			if (!sernum_in_window(sess))
-				break;
-			mutex_exit(&sess->lock);
-
-			TAILQ_REMOVE(&waiting, req_ccb, chain);
-
-			DEBC(conn, 10, ("Unthrottling - ccb = %p, disp = %d\n",
-					req_ccb, req_ccb->disp));
-
-			if ((req_ccb->flags & CCBF_WAITING) != 0) {
-				cv_broadcast(&conn->ccb_cv);
-			} else {
-				send_command(req_ccb, req_ccb->disp, FALSE, FALSE);
-			}
-
-			mutex_enter(&sess->lock);
-		}
-
-		while ((req_ccb = TAILQ_FIRST(&waiting)) != NULL) {
-			TAILQ_REMOVE(&waiting, req_ccb, chain);
-			throttle_ccb(req_ccb, TRUE);
-		}
-	}
-
 	mutex_exit(&sess->lock);
 
 	return 0;
@@ -1215,6 +1168,11 @@ iscsi_rcv_thread(void *par)
 	do {
 		while (!conn->terminating) {
 			pdu = get_pdu(conn, TRUE);
+			if (pdu == NULL) {
+				KASSERT(conn->terminating);
+				break;
+			}
+
 			pdu->uio.uio_iov = pdu->io_vec;
 			UIO_SETUP_SYSSPACE(&pdu->uio);
 			pdu->uio.uio_iovcnt = 1;
Index: src/sys/dev/iscsi/iscsi_utils.c
diff -u src/sys/dev/iscsi/iscsi_utils.c:1.20 src/sys/dev/iscsi/iscsi_utils.c:1.21
--- src/sys/dev/iscsi/iscsi_utils.c:1.20	Wed Jun 15 03:51:55 2016
+++ src/sys/dev/iscsi/iscsi_utils.c	Wed Jun 15 04:30:30 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: iscsi_utils.c,v 1.20 2016/06/15 03:51:55 mlelstv Exp $	*/
+/*	$NetBSD: iscsi_utils.c,v 1.21 2016/06/15 04:30:30 mlelstv Exp $	*/
 
 /*-
  * Copyright (c) 2004,2005,2006,2008 The NetBSD Foundation, Inc.
@@ -256,7 +256,6 @@ free_ccb(ccb_t *ccb)
 		"free_ccb: ccb = %p, usecount = %d\n",
 		ccb, conn->usecount-1));
 
-	KASSERT((ccb->flags & CCBF_THROTTLING) == 0);
 	KASSERT((ccb->flags & CCBF_WAITQUEUE) == 0);
 
 	atomic_dec_uint(&conn->usecount);
@@ -334,45 +333,20 @@ suspend_ccb(ccb_t *ccb, bool yes)
 	connection_t *conn;
 
 	conn = ccb->connection;
+
+	KASSERT(mutex_owned(&conn->lock));
+
 	if (yes) {
-		KASSERT((ccb->flags & CCBF_THROTTLING) == 0);
 		KASSERT((ccb->flags & CCBF_WAITQUEUE) == 0);
 		TAILQ_INSERT_TAIL(&conn->ccbs_waiting, ccb, chain);
 		ccb->flags |= CCBF_WAITQUEUE;
 	} else if (ccb->flags & CCBF_WAITQUEUE) {
-		KASSERT((ccb->flags & CCBF_THROTTLING) == 0);
 		TAILQ_REMOVE(&conn->ccbs_waiting, ccb, chain);
 		ccb->flags &= ~CCBF_WAITQUEUE;
 	}
 }
 
 /*
- * throttle_ccb:
- *    Put CCB on throttling queue
- */
-void
-throttle_ccb(ccb_t *ccb, bool yes)
-{
-	session_t *sess;
-
-	sess = ccb->session;
-
-	KASSERT(mutex_owned(&sess->lock));
-
-	if (yes) {
-		KASSERT((ccb->flags & CCBF_THROTTLING) == 0);
-		KASSERT((ccb->flags & CCBF_WAITQUEUE) == 0);
-		TAILQ_INSERT_TAIL(&sess->ccbs_throttled, ccb, chain);
-		ccb->flags |= CCBF_THROTTLING;
-	} else if (ccb->flags & CCBF_THROTTLING) {
-		KASSERT((ccb->flags & CCBF_WAITQUEUE) == 0);
-		TAILQ_REMOVE(&sess->ccbs_throttled, ccb, chain);
-		ccb->flags &= ~CCBF_THROTTLING;
-	}
-}
-
-
-/*
  * wake_ccb:
  *    Wake up (or dispose of) a CCB. Depending on the CCB's disposition,
  *    either wake up the requesting thread, signal SCSIPI that we're done,
@@ -386,15 +360,11 @@ wake_ccb(ccb_t *ccb, uint32_t status)
 {
 	ccb_disp_t disp;
 	connection_t *conn;
-	session_t *sess;
 
 	conn = ccb->connection;
-	sess = ccb->session;
 
-#ifdef ISCSI_DEBUG
-	DEBC(conn, 9, ("CCB done, ccb = %p, disp = %d\n",
-		ccb, ccb->disp));
-#endif
+	DEBC(conn, 9, ("CCB %d done, ccb = %p, disp = %d\n",
+		ccb->CmdSN, ccb, ccb->disp));
 
 	ccb_timeout_stop(ccb);
 
@@ -413,10 +383,6 @@ wake_ccb(ccb_t *ccb, uint32_t status)
 	ccb->status = status;
 	mutex_exit(&conn->lock);
 
-	mutex_enter(&sess->lock);
-	throttle_ccb(ccb, FALSE);
-	mutex_exit(&sess->lock);
-
 	switch (disp) {
 	case CCBDISP_FREE:
 		free_ccb(ccb);
@@ -467,22 +433,24 @@ get_pdu(connection_t *conn, bool waitok)
 		if (pdu != NULL)
 			TAILQ_REMOVE(&conn->pdu_pool, pdu, chain);
 
-		DEB(100, ("get_pdu_c: pdu = %p, waitok = %d\n", pdu, waitok));
-
 		if (pdu == NULL) {
 			if (!waitok || conn->terminating) {
 				mutex_exit(&conn->lock);
+				DEB(15, ("get_pdu: failed"));
 				return NULL;
 			}
-			cv_wait(&conn->conn_cv, &conn->lock);
+			cv_wait(&conn->pdu_cv, &conn->lock);
 		}
 	} while (pdu == NULL);
+	atomic_inc_uint(&conn->pducount);
 	mutex_exit(&conn->lock);
 
 	memset(pdu, 0, sizeof(pdu_t));
 	pdu->connection = conn;
 	pdu->disp = PDUDISP_FREE;
 
+	DEBC(conn, 15, ("get_pdu: pdu = %p, usecount = %d\n", pdu, conn->pducount));
+
 	return pdu;
 }
 
@@ -499,6 +467,8 @@ free_pdu(pdu_t *pdu)
 	connection_t *conn = pdu->connection;
 	pdu_disp_t pdisp;
 
+	DEBC(conn, 15, ("free_pdu: pdu = %p, usecount = %d\n", pdu, conn->pducount-1));
+
 	KASSERT((pdu->flags & PDUF_INQUEUE) == 0);
 
 	if (PDUDISP_UNUSED == (pdisp = pdu->disp))
@@ -510,10 +480,11 @@ free_pdu(pdu_t *pdu)
 		free(pdu->temp_data, M_TEMP);
 
 	mutex_enter(&conn->lock);
+	atomic_dec_uint(&conn->pducount);
 	TAILQ_INSERT_TAIL(&conn->pdu_pool, pdu, chain);
 	mutex_exit(&conn->lock);
 
-	cv_broadcast(&conn->conn_cv);
+	cv_broadcast(&conn->pdu_cv);
 }
 
 /*
@@ -676,14 +647,14 @@ ack_sernum(sernum_buffer_t *buff, uint32
  *   and optionally increment it for the next query
  */
 uint32_t
-get_sernum(session_t *sess, bool bump)
+get_sernum(session_t *sess, pdu_t *pdu)
 {
 	uint32_t sn;
 
 	KASSERT(mutex_owned(&sess->lock));
 
 	sn = sess->CmdSN;
-	if (bump)
+	if ((pdu->pdu.Opcode & OP_IMMEDIATE) == 0)
 		atomic_inc_32(&sess->CmdSN);
 	return sn;
 }
@@ -701,3 +672,22 @@ sernum_in_window(session_t *sess)
 	return sn_a_le_b(sess->CmdSN, sess->MaxCmdSN);
 }
 
+/*
+ * window_size:
+ *    Compute send window size
+ */
+int
+window_size(session_t *sess, int limit)
+{
+	uint32_t win;
+
+	KASSERT(mutex_owned(&sess->lock));
+
+	win = 0;
+	if (sn_a_le_b(sess->CmdSN, sess->MaxCmdSN))
+		win = sess->MaxCmdSN - sess->CmdSN + 1;
+	if (win > INT_MAX || win > limit)
+		win = limit;
+
+	return win;
+}

Index: src/sys/dev/iscsi/iscsi_ioctl.c
diff -u src/sys/dev/iscsi/iscsi_ioctl.c:1.21 src/sys/dev/iscsi/iscsi_ioctl.c:1.22
--- src/sys/dev/iscsi/iscsi_ioctl.c:1.21	Sun Jun  5 15:04:31 2016
+++ src/sys/dev/iscsi/iscsi_ioctl.c	Wed Jun 15 04:30:30 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: iscsi_ioctl.c,v 1.21 2016/06/05 15:04:31 mlelstv Exp $	*/
+/*	$NetBSD: iscsi_ioctl.c,v 1.22 2016/06/15 04:30:30 mlelstv Exp $	*/
 
 /*-
  * Copyright (c) 2004,2005,2006,2011 The NetBSD Foundation, Inc.
@@ -430,6 +430,50 @@ find_connection(session_t *session, uint
 	return curr;
 }
 
+/*
+ * ref_session:
+ *    Reference a session
+ *
+ *    Session cannot be release until reference count reaches zero
+ *
+ *    Returns: 1 if reference counter would overflow
+ */
+
+int
+ref_session(session_t *session)
+{
+	int rc = 1;
+
+	mutex_enter(&iscsi_cleanup_mtx);
+	KASSERT(session != NULL);
+	if (session->refcount <= CCBS_PER_SESSION) {
+		session->refcount++;
+		rc = 0;
+	}
+	mutex_exit(&iscsi_cleanup_mtx);
+
+	return rc;
+}
+
+/*
+ * unref_session:
+ *    Unreference a session
+ *
+ *    Release session reference, trigger cleanup
+ */
+
+void
+unref_session(session_t *session)
+{
+
+	mutex_enter(&iscsi_cleanup_mtx);
+	KASSERT(session != NULL);
+	KASSERT(session->refcount > 0);
+	if (--session->refcount == 0)
+		cv_broadcast(&session->sess_cv);
+	mutex_exit(&iscsi_cleanup_mtx);
+}
+
 
 /*
  * kill_connection:
@@ -543,8 +587,7 @@ kill_connection(connection_t *conn, uint
 void
 kill_session(session_t *session, uint32_t status, int logout, bool recover)
 {
-	connection_t *curr;
-	ccb_t *ccb;
+	connection_t *conn;
 
 	DEB(1, ("ISCSI: kill_session %d, status %d, logout %d, recover %d\n",
 			session->id, status, logout, recover));
@@ -552,6 +595,7 @@ kill_session(session_t *session, uint32_
 	mutex_enter(&iscsi_cleanup_mtx);
 	if (session->terminating) {
 		mutex_exit(&iscsi_cleanup_mtx);
+
 		DEB(5, ("Session is being killed with status %d\n",session->terminating));
 		return;
 	}
@@ -560,15 +604,16 @@ kill_session(session_t *session, uint32_
 	 * don't do anything if session isn't established yet, termination will be
 	 * handled elsewhere
 	 */
-	if (session->sessions.tqe_next == NULL &&
-	    session->sessions.tqe_prev == NULL) {
+	if (session->sessions.tqe_next == NULL && session->sessions.tqe_prev == NULL) {
 		mutex_exit(&iscsi_cleanup_mtx);
+
+		DEB(5, ("Session is being killed which is not yet established\n"));
 		return;
 	}
-	session->terminating = status;
-	mutex_exit(&iscsi_cleanup_mtx);
 
 	if (recover) {
+		mutex_exit(&iscsi_cleanup_mtx);
+
 		/*
 		 * Only recover when there's just one active connection left.
 		 * Otherwise we get in all sorts of timing problems, and it doesn't
@@ -576,41 +621,32 @@ kill_session(session_t *session, uint32_
 		 * requested that we kill a multipathed session.
 		 */
 		if (session->active_connections == 1) {
-			curr = assign_connection(session, FALSE);
-			if (curr != NULL)
-				kill_connection(curr, status, logout, TRUE);
+			conn = assign_connection(session, FALSE);
+			if (conn != NULL)
+				kill_connection(conn, status, logout, TRUE);
 		}
-		/* don't allow the session to disappear when the target */
-		/* requested the logout */
-		session->terminating = ISCSI_STATUS_SUCCESS;
 		return;
 	}
 
-	/* remove from session list */
-	mutex_enter(&iscsi_cleanup_mtx);
+	if (session->refcount > 0) {
+		mutex_exit(&iscsi_cleanup_mtx);
+
+		DEB(5, ("Session is being killed while in use (refcnt = %d)\n",
+			session->refcount));
+		return;
+	}
+
+	/* Remove session from global list */
+	session->terminating = status;
 	TAILQ_REMOVE(&iscsi_sessions, session, sessions);
 	session->sessions.tqe_next = NULL;
 	session->sessions.tqe_prev = NULL;
-	mutex_exit(&iscsi_cleanup_mtx);
-
-	/* complete any throttled CCBs */
-	mutex_enter(&session->lock);
-	while ((ccb = TAILQ_FIRST(&session->ccbs_throttled)) != NULL) {
-		throttle_ccb(ccb, FALSE);
-		mutex_exit(&session->lock);
-		wake_ccb(ccb, ISCSI_STATUS_LOGOUT);
-		mutex_enter(&session->lock);
-	}
-	mutex_exit(&session->lock);
 
-	/*
-	 * unmap first to give the system an opportunity to flush its buffers
-	 */
-	unmap_session(session);
+	mutex_exit(&iscsi_cleanup_mtx);
 
 	/* kill all connections */
-	while ((curr = TAILQ_FIRST(&session->conn_list)) != NULL) {
-		kill_connection(curr, status, logout, FALSE);
+	while ((conn = TAILQ_FIRST(&session->conn_list)) != NULL) {
+		kill_connection(conn, status, logout, FALSE);
 		logout = NO_LOGOUT;
 	}
 }
@@ -676,6 +712,7 @@ create_connection(iscsi_login_parameters
 
 	mutex_init(&connection->lock, MUTEX_DEFAULT, IPL_BIO);
 	cv_init(&connection->conn_cv, "conn");
+	cv_init(&connection->pdu_cv, "pdupool");
 	cv_init(&connection->ccb_cv, "ccbwait");
 	cv_init(&connection->idle_cv, "idle");
 
@@ -691,6 +728,7 @@ create_connection(iscsi_login_parameters
 
 		cv_destroy(&connection->idle_cv);
 		cv_destroy(&connection->ccb_cv);
+		cv_destroy(&connection->pdu_cv);
 		cv_destroy(&connection->conn_cv);
 		mutex_destroy(&connection->lock);
 		free(connection, M_DEVBUF);
@@ -707,7 +745,7 @@ create_connection(iscsi_login_parameters
 	connection->login_par = par;
 
 	DEB(5, ("Creating receive thread\n"));
-	if ((rc = kthread_create(PRI_NONE, KTHREAD_MPSAFE, NULL, iscsi_rcv_thread,
+	if ((rc = kthread_create(PRI_BIO, KTHREAD_MPSAFE, NULL, iscsi_rcv_thread,
 				connection, &connection->rcvproc,
 				"ConnRcv")) != 0) {
 		DEBOUT(("Can't create rcv thread (rc %d)\n", rc));
@@ -715,6 +753,7 @@ create_connection(iscsi_login_parameters
 		release_socket(connection->sock);
 		cv_destroy(&connection->idle_cv);
 		cv_destroy(&connection->ccb_cv);
+		cv_destroy(&connection->pdu_cv);
 		cv_destroy(&connection->conn_cv);
 		mutex_destroy(&connection->lock);
 		free(connection, M_DEVBUF);
@@ -722,7 +761,7 @@ create_connection(iscsi_login_parameters
 		return rc;
 	}
 	DEB(5, ("Creating send thread\n"));
-	if ((rc = kthread_create(PRI_NONE, KTHREAD_MPSAFE, NULL, iscsi_send_thread,
+	if ((rc = kthread_create(PRI_BIO, KTHREAD_MPSAFE, NULL, iscsi_send_thread,
 				connection, &connection->sendproc,
 				"ConnSend")) != 0) {
 		DEBOUT(("Can't create send thread (rc %d)\n", rc));
@@ -746,6 +785,7 @@ create_connection(iscsi_login_parameters
 		release_socket(connection->sock);
 		cv_destroy(&connection->idle_cv);
 		cv_destroy(&connection->ccb_cv);
+		cv_destroy(&connection->pdu_cv);
 		cv_destroy(&connection->conn_cv);
 		mutex_destroy(&connection->lock);
 		free(connection, M_DEVBUF);
@@ -767,12 +807,21 @@ create_connection(iscsi_login_parameters
 		return -1;
 	}
 
+	mutex_enter(&session->lock);
+	if (session->terminating) {
+		DEBC(connection, 0, ("Session terminating\n"));
+		kill_connection(connection, rc, NO_LOGOUT, FALSE);
+		mutex_exit(&session->lock);
+		par->status = session->terminating;
+		return -1;
+	}
 	connection->state = ST_FULL_FEATURE;
 	TAILQ_INSERT_TAIL(&session->conn_list, connection, connections);
 	connection->in_session = TRUE;
 	session->total_connections++;
 	session->active_connections++;
 	session->mru_connection = connection;
+	mutex_exit(&session->lock);
 
 	DEBC(connection, 5, ("Connection created successfully!\n"));
 	return 0;
@@ -844,7 +893,13 @@ recreate_connection(iscsi_login_paramete
 	session->active_connections++;
 
 	TAILQ_INIT(&old_waiting);
-	TAILQ_CONCAT(&old_waiting, &connection->ccbs_waiting, chain);
+
+	mutex_enter(&connection->lock);
+	while ((ccb = TAILQ_FIRST(&connection->ccbs_waiting)) != NULL) {
+		suspend_ccb(ccb, FALSE);
+		TAILQ_INSERT_TAIL(&old_waiting, ccb, chain);
+	}
+	mutex_exit(&connection->lock);
 
 	init_sernum(&connection->StatSN_buf);
 	cv_broadcast(&connection->idle_cv);
@@ -869,7 +924,9 @@ recreate_connection(iscsi_login_paramete
 
 	while ((ccb = TAILQ_FIRST(&old_waiting)) != NULL) {
 		TAILQ_REMOVE(&old_waiting, ccb, chain);
+		mutex_enter(&connection->lock);
 		suspend_ccb(ccb, TRUE);
+		mutex_exit(&connection->lock);
 
 		rc = send_task_management(connection, ccb, NULL, TASK_REASSIGN);
 		/* if we get an error on reassign, restart the original request */
@@ -877,17 +934,22 @@ recreate_connection(iscsi_login_paramete
 			mutex_enter(&session->lock);
 			if (sn_a_lt_b(ccb->CmdSN, session->ExpCmdSN)) {
 				pdu = ccb->pdu_waiting;
-				sn = get_sernum(session, !(pdu->pdu.Opcode & OP_IMMEDIATE));
+				sn = get_sernum(session, pdu);
 
 				/* update CmdSN */
-				DEBC(connection, 1, ("Resend Updating CmdSN - old %d, new %d\n",
-					   ccb->CmdSN, sn));
+				DEBC(connection, 0, ("Resend ccb %p (%d) - updating CmdSN old %u, new %u\n",
+					   ccb, rc, ccb->CmdSN, sn));
 				ccb->CmdSN = sn;
 				pdu->pdu.p.command.CmdSN = htonl(ccb->CmdSN);
+			} else {
+				DEBC(connection, 0, ("Resend ccb %p (%d) - CmdSN %u\n",
+					   ccb, rc, ccb->CmdSN));
 			}
 			mutex_exit(&session->lock);
 			resend_pdu(ccb);
 		} else {
+			DEBC(connection, 0, ("Resend ccb %p (%d) CmdSN %u - reassigned\n",
+				ccb, rc, ccb->CmdSN));
 			ccb_timeout_start(ccb, COMMAND_TIMEOUT);
 		}
 	}
@@ -1011,7 +1073,6 @@ login(iscsi_login_parameters_t *par, str
 	}
 	TAILQ_INIT(&session->conn_list);
 	TAILQ_INIT(&session->ccb_pool);
-	TAILQ_INIT(&session->ccbs_throttled);
 
 	mutex_init(&session->lock, MUTEX_DEFAULT, IPL_BIO);
 	cv_init(&session->sess_cv, "session");
@@ -1116,8 +1177,6 @@ add_connection(iscsi_login_parameters_t 
 	if ((par->status = check_login_pars(par)) == 0) {
 		create_connection(par, session, l);
 	}
-
-	iscsi_notify_cleanup();
 }
 
 
@@ -1516,10 +1575,11 @@ get_version(iscsi_get_version_parameters
  *    Terminate all sessions (called when the driver unloads).
  */
 
-void
+int
 kill_all_sessions(void)
 {
 	session_t *sess;
+	int rc = 0;
 
 	mutex_enter(&iscsi_cleanup_mtx);
 	while ((sess = TAILQ_FIRST(&iscsi_sessions)) != NULL) {
@@ -1528,7 +1588,13 @@ kill_all_sessions(void)
 				FALSE);
 		mutex_enter(&iscsi_cleanup_mtx);
 	}
+	if (TAILQ_FIRST(&iscsi_sessions) != NULL) {
+		DEBOUT(("Failed to kill all sessions\n"));
+		rc = EBUSY;
+	}
 	mutex_exit(&iscsi_cleanup_mtx);
+
+	return rc;
 }
 
 /*
@@ -1566,6 +1632,7 @@ add_connection_cleanup(connection_t *con
 	mutex_enter(&iscsi_cleanup_mtx);
 	TAILQ_INSERT_TAIL(&iscsi_cleanupc_list, conn, connections);
 	mutex_exit(&iscsi_cleanup_mtx);
+	iscsi_notify_cleanup();
 }
 
 /*
@@ -1658,24 +1725,19 @@ static void
 iscsi_cleanup_thread(void *par)
 {
 	int s, rc;
-	connection_t *conn;
+	session_t *sess, *nxts;
+	connection_t *conn, *nxtc;
 	ccb_t *ccb;
-	session_t *sess, *nxt;
-	uint32_t status;
-#ifdef ISCSI_DEBUG
-	int last_usecount;
-#endif
 
 	mutex_enter(&iscsi_cleanup_mtx);
-	while ((conn = TAILQ_FIRST(&iscsi_cleanupc_list)) != NULL ||
-		iscsi_num_send_threads ||
-		!iscsi_detaching) {
-		if (conn != NULL) {
+	while (iscsi_num_send_threads || !iscsi_detaching ||
+	       !TAILQ_EMPTY(&iscsi_cleanupc_list) || !TAILQ_EMPTY(&iscsi_cleanups_list)) {
+		TAILQ_FOREACH_SAFE(conn, &iscsi_cleanupc_list, connections, nxtc) {
+
 			TAILQ_REMOVE(&iscsi_cleanupc_list, conn, connections);
 			mutex_exit(&iscsi_cleanup_mtx);
 
 			sess = conn->session;
-			status = conn->terminating;
 
 			/*
 			 * This implies that connection cleanup only runs when
@@ -1683,97 +1745,102 @@ iscsi_cleanup_thread(void *par)
 			 */
 			DEBC(conn, 5, ("Cleanup: Waiting for threads to exit\n"));
 			while (conn->sendproc || conn->rcvproc)
-				kpause("termwait", false, hz, NULL);
+				kpause("threads", false, hz, NULL);
 
-			last_usecount = 0;
-			while (conn->usecount > 0) {
-				if (conn->usecount != last_usecount) {
-					DEBC(conn, 5,("Cleanup: %d CCBs busy\n", conn->usecount));
-					last_usecount = conn->usecount;
-					mutex_enter(&conn->lock);
-					TAILQ_FOREACH(ccb, &conn->ccbs_waiting, chain) {
-						DEBC(conn, 5,("Cleanup: ccb=%p disp=%d timedout=%d\n", ccb,ccb->disp, ccb->timedout));
-					}
-					mutex_exit(&conn->lock);
-				}
-				kpause("finalwait", false, hz, NULL);
+			for (s=1; conn->usecount > 0 && s < 3; ++s)
+				kpause("usecount", false, hz, NULL);
+
+			if (conn->usecount > 0) {
+				DEBC(conn, 5, ("Cleanup: %d CCBs busy\n", conn->usecount));
+				/* retry later */
+				mutex_enter(&iscsi_cleanup_mtx);
+				TAILQ_INSERT_HEAD(&iscsi_cleanupc_list, conn, connections);
+				continue;
 			}
 
+			KASSERT(!conn->in_session);
+
 			callout_halt(&conn->timeout, NULL);
 			closef(conn->sock);
 			cv_destroy(&conn->idle_cv);
 			cv_destroy(&conn->ccb_cv);
+			cv_destroy(&conn->pdu_cv);
 			cv_destroy(&conn->conn_cv);
 			mutex_destroy(&conn->lock);
 			free(conn, M_DEVBUF);
 
+			mutex_enter(&iscsi_cleanup_mtx);
+
 			if (--sess->total_connections == 0) {
 				DEB(1, ("Cleanup: session %d\n", sess->id));
+				KASSERT(sess->sessions.tqe_prev == NULL);
 				TAILQ_INSERT_HEAD(&iscsi_cleanups_list, sess, sessions);
 			}
+		}
 
-			TAILQ_FOREACH_SAFE(sess, &iscsi_cleanups_list, sessions, nxt) {
-				if (sess->total_connections != 0)
-					continue;
-
-				TAILQ_REMOVE(&iscsi_cleanups_list, sess, sessions);
-
-				DEB(1, ("Cleanup: Unmap session %d\n", sess->id));
-
-				rc = unmap_session(sess);
-				if (rc == 0) {
-					DEB(1, ("Cleanup: Unmap session %d failed\n", sess->id));
-					TAILQ_INSERT_HEAD(&iscsi_cleanups_list, sess, sessions);
-				}
-
-				if (sess->target_list != NULL)
-					free(sess->target_list, M_TEMP);
-				/* notify event handlers of session shutdown */
-				add_event(ISCSI_SESSION_TERMINATED, sess->id, 0, status);
-				DEB(1, ("Cleanup: session ended %d\n", sess->id));
-
-				cv_destroy(&sess->ccb_cv);
-				cv_destroy(&sess->sess_cv);
-				mutex_destroy(&sess->lock);
-				free(sess, M_DEVBUF);
+		TAILQ_FOREACH_SAFE(sess, &iscsi_cleanups_list, sessions, nxts) {
+			if (sess->refcount > 0)
+				continue;
+			TAILQ_REMOVE(&iscsi_cleanups_list, sess, sessions);
+			sess->sessions.tqe_next = NULL;
+			sess->sessions.tqe_prev = NULL;
+			mutex_exit(&iscsi_cleanup_mtx);
+
+			DEB(1, ("Cleanup: Unmap session %d\n", sess->id));
+			if (unmap_session(sess) == 0) {
+				DEB(1, ("Cleanup: Unmap session %d failed\n", sess->id));
+				mutex_enter(&iscsi_cleanup_mtx);
+				TAILQ_INSERT_HEAD(&iscsi_cleanups_list, sess, sessions);
+				continue;
 			}
-			DEB(5, ("Cleanup: Done\n"));
+
+			if (sess->target_list != NULL)
+				free(sess->target_list, M_TEMP);
+
+			/* notify event handlers of session shutdown */
+			add_event(ISCSI_SESSION_TERMINATED, sess->id, 0, sess->terminating);
+			DEB(1, ("Cleanup: session ended %d\n", sess->id));
+
+			cv_destroy(&sess->ccb_cv);
+			cv_destroy(&sess->sess_cv);
+			mutex_destroy(&sess->lock);
+			free(sess, M_DEVBUF);
 
 			mutex_enter(&iscsi_cleanup_mtx);
+		}
 
-		} else {
-			/* Go to sleep, but wake up every 30 seconds to
-			 * check for dead event handlers */
-			rc = cv_timedwait(&iscsi_cleanup_cv, &iscsi_cleanup_mtx,
-				(TAILQ_FIRST(&event_handlers)) ? 30 * hz : 0);
-
-			/* handle ccb timeouts */
-			while ((ccb = TAILQ_FIRST(&iscsi_timeout_ccb_list)) != NULL) {
-				TAILQ_REMOVE(&iscsi_timeout_ccb_list, ccb, tchain);
-				KASSERT(ccb->timedout == TOUT_QUEUED);
-				ccb->timedout = TOUT_BUSY;
-				mutex_exit(&iscsi_cleanup_mtx);
-				ccb_timeout(ccb);
-				mutex_enter(&iscsi_cleanup_mtx);
-				if (ccb->timedout == TOUT_BUSY)
-					ccb->timedout = TOUT_NONE;
-			}
-			/* handle connection timeouts */
-			while ((conn = TAILQ_FIRST(&iscsi_timeout_conn_list)) != NULL) {
-				TAILQ_REMOVE(&iscsi_timeout_conn_list, conn, tchain);
-				KASSERT(conn->timedout == TOUT_QUEUED);
-				conn->timedout = TOUT_BUSY;
-				mutex_exit(&iscsi_cleanup_mtx);
-				connection_timeout(conn);
-				mutex_enter(&iscsi_cleanup_mtx);
-				if (conn->timedout == TOUT_BUSY)
-					conn->timedout = TOUT_NONE;
-			}
+		/* handle ccb timeouts */
+		while ((ccb = TAILQ_FIRST(&iscsi_timeout_ccb_list)) != NULL) {
+			TAILQ_REMOVE(&iscsi_timeout_ccb_list, ccb, tchain);
+			KASSERT(ccb->timedout == TOUT_QUEUED);
+			ccb->timedout = TOUT_BUSY;
+			mutex_exit(&iscsi_cleanup_mtx);
+			ccb_timeout(ccb);
+			mutex_enter(&iscsi_cleanup_mtx);
+			if (ccb->timedout == TOUT_BUSY)
+				ccb->timedout = TOUT_NONE;
+		}
 
-			/* if timed out, not woken up */
-			if (rc == EWOULDBLOCK)
-				check_event_handlers();
+		/* handle connection timeouts */
+		while ((conn = TAILQ_FIRST(&iscsi_timeout_conn_list)) != NULL) {
+			TAILQ_REMOVE(&iscsi_timeout_conn_list, conn, tchain);
+			KASSERT(conn->timedout == TOUT_QUEUED);
+			conn->timedout = TOUT_BUSY;
+			mutex_exit(&iscsi_cleanup_mtx);
+			connection_timeout(conn);
+			mutex_enter(&iscsi_cleanup_mtx);
+			if (conn->timedout == TOUT_BUSY)
+				conn->timedout = TOUT_NONE;
 		}
+
+		/* Go to sleep, but wake up every 30 seconds to
+		 * check for dead event handlers */
+		rc = cv_timedwait(&iscsi_cleanup_cv, &iscsi_cleanup_mtx,
+			(TAILQ_FIRST(&event_handlers)) ? 120 * hz : 0);
+
+		/* if timed out, not woken up */
+		if (rc == EWOULDBLOCK)
+			check_event_handlers();
 	}
 	mutex_exit(&iscsi_cleanup_mtx);
 
@@ -1807,7 +1874,7 @@ iscsi_init_cleanup(void)
 	}
 }
 
-void
+int
 iscsi_destroy_cleanup(void)
 {
 	
@@ -1822,6 +1889,8 @@ iscsi_destroy_cleanup(void)
 	cv_destroy(&iscsi_event_cv);
 	cv_destroy(&iscsi_cleanup_cv);
 	mutex_destroy(&iscsi_cleanup_mtx);
+
+	return 0;
 }
 
 void

Index: src/sys/dev/iscsi/iscsi_main.c
diff -u src/sys/dev/iscsi/iscsi_main.c:1.22 src/sys/dev/iscsi/iscsi_main.c:1.23
--- src/sys/dev/iscsi/iscsi_main.c:1.22	Sun Jun  5 05:29:01 2016
+++ src/sys/dev/iscsi/iscsi_main.c	Wed Jun 15 04:30:30 2016
@@ -262,6 +262,7 @@ static int
 iscsi_detach(device_t self, int flags)
 {
 	struct iscsi_softc *sc;
+	int error;
 
 	DEB(1, ("ISCSI: detach\n"));
 	sc = (struct iscsi_softc *) device_private(self);
@@ -274,8 +275,13 @@ iscsi_detach(device_t self, int flags)
 	iscsi_detaching = true;
 	mutex_exit(&sc->lock);
 
-	kill_all_sessions();
-	iscsi_destroy_cleanup();
+	error = kill_all_sessions();
+	if (error)
+		return error;
+
+	error = iscsi_destroy_cleanup();
+	if (error)
+		return error;
 
 	mutex_destroy(&sc->lock);
 
@@ -348,6 +354,10 @@ map_session(session_t *session, device_t
 	struct scsipi_channel *chan = &session->sc_channel;
 	const quirktab_t	*tgt;
 
+	mutex_enter(&session->lock);
+	session->send_window = max(2, window_size(session, CCBS_FOR_SCSIPI));
+	mutex_exit(&session->lock);
+
 	/*
 	 * Fill in the scsipi_adapter.
 	 */
@@ -355,8 +365,8 @@ map_session(session_t *session, device_t
 	adapt->adapt_nchannels = 1;
 	adapt->adapt_request = iscsi_scsipi_request;
 	adapt->adapt_minphys = iscsi_minphys;
-	adapt->adapt_openings = CCBS_PER_SESSION - 1;
-	adapt->adapt_max_periph = CCBS_PER_SESSION - 1;
+	adapt->adapt_openings = session->send_window;
+	adapt->adapt_max_periph = CCBS_FOR_SCSIPI;
 
 	/*
 	 * Fill in the scsipi_channel.
@@ -369,7 +379,7 @@ map_session(session_t *session, device_t
 	chan->chan_adapter = adapt;
 	chan->chan_bustype = &scsi_bustype;
 	chan->chan_channel = 0;
-	chan->chan_flags = SCSIPI_CHAN_NOSETTLE;
+	chan->chan_flags = SCSIPI_CHAN_NOSETTLE | SCSIPI_CHAN_CANGROW;
 	chan->chan_ntargets = 1;
 	chan->chan_nluns = 16;		/* ToDo: ??? */
 	chan->chan_id = session->id;
@@ -405,6 +415,29 @@ unmap_session(session_t *session)
 	return rv;
 }
 
+/*
+ * grow_resources
+ *    Try to grow openings up to current window size
+ */
+static void
+grow_resources(session_t *session)
+{
+	struct scsipi_adapter *adapt = &session->sc_adapter;
+	int win;
+
+	mutex_enter(&session->lock);
+	if (session->refcount < CCBS_FOR_SCSIPI &&
+	    session->send_window < CCBS_FOR_SCSIPI) {
+		win = window_size(session, CCBS_FOR_SCSIPI - session->refcount);
+		if (win > session->send_window) {
+			session->send_window++;
+			adapt->adapt_openings++;
+			DEB(5, ("Grow send window to %d\n", session->send_window));
+		}
+	}
+	mutex_exit(&session->lock);
+}
+
 /******************************************************************************/
 
 /*****************************************************************************
@@ -425,20 +458,32 @@ iscsi_scsipi_request(struct scsipi_chann
 	session_t *session;
 	int flags;
 	struct scsipi_xfer_mode *xm;
+	int error;
 
 	session = (session_t *) adapt;	/* adapter is first field in session */
 
+	error = ref_session(session);
+
 	switch (req) {
 	case ADAPTER_REQ_RUN_XFER:
 		DEB(9, ("ISCSI: scsipi_request RUN_XFER\n"));
 		xs = arg;
 		flags = xs->xs_control;
 
+		if (error) {
+			DEB(9, ("ISCSI: refcount too high: %d, winsize %d\n",
+				session->refcount, session->send_window));
+			xs->error = XS_BUSY;
+			xs->status = XS_BUSY;
+			scsipi_done(xs);
+			return;
+		}
+
 		if ((flags & XS_CTL_POLL) != 0) {
 			xs->error = XS_DRIVER_STUFFUP;
 			DEBOUT(("Run Xfer request with polling\n"));
 			scsipi_done(xs);
-			return;
+			break;
 		}
 		/*
 		 * NOTE: It appears that XS_CTL_DATA_UIO is not actually used anywhere.
@@ -451,28 +496,32 @@ iscsi_scsipi_request(struct scsipi_chann
 			xs->error = XS_DRIVER_STUFFUP;
 			DEBOUT(("Run Xfer with data in UIO\n"));
 			scsipi_done(xs);
-			return;
+			break;
 		}
 
 		send_run_xfer(session, xs);
-		DEB(15, ("scsipi_req returns\n"));
+		DEB(15, ("scsipi_req returns, refcount = %d\n", session->refcount));
 		return;
 
 	case ADAPTER_REQ_GROW_RESOURCES:
 		DEB(5, ("ISCSI: scsipi_request GROW_RESOURCES\n"));
-		return;
+		grow_resources(session);
+		break;
 
 	case ADAPTER_REQ_SET_XFER_MODE:
 		DEB(5, ("ISCSI: scsipi_request SET_XFER_MODE\n"));
 		xm = (struct scsipi_xfer_mode *)arg;
 		xm->xm_mode = PERIPH_CAP_TQING;
 		scsipi_async_event(chan, ASYNC_EVENT_XFER_MODE, xm);
-		return;
+		break;
 
 	default:
+		DEBOUT(("ISCSI: scsipi_request with invalid REQ code %d\n", req));
 		break;
 	}
-	DEBOUT(("ISCSI: scsipi_request with invalid REQ code %d\n", req));
+
+	if (!error)
+		unref_session(session);
 }
 
 /* cap the transfer at 64K */
@@ -522,6 +571,8 @@ iscsi_done(ccb_t *ccb)
 			break;
 
 		case ISCSI_STATUS_TARGET_BUSY:
+		case ISCSI_STATUS_NO_RESOURCES:
+			DEBC(ccb->connection, 5, ("target busy, ccb %p\n", ccb));
 			xs->error = XS_BUSY;
 			xs->status = SCSI_BUSY;
 			break;
@@ -532,7 +583,8 @@ iscsi_done(ccb_t *ccb)
 			xs->status = SCSI_BUSY;
 			break;
 
-		case ISCSI_STATUS_NO_RESOURCES:
+		case ISCSI_STATUS_QUEUE_FULL:
+			DEBC(ccb->connection, 5, ("queue full, ccb %p\n", ccb));
 			xs->error = XS_BUSY;
 			xs->status = SCSI_QUEUE_FULL;
 			break;
@@ -550,6 +602,8 @@ iscsi_done(ccb_t *ccb)
 	} else {
 		DEBOUT(("ISCSI: iscsi_done CCB %p without XS\n", ccb));
 	}
+
+	unref_session(ccb->session);
 }
 
 SYSCTL_SETUP(sysctl_iscsi_setup, "ISCSI subtree setup")

Index: src/sys/dev/iscsi/iscsi_send.c
diff -u src/sys/dev/iscsi/iscsi_send.c:1.30 src/sys/dev/iscsi/iscsi_send.c:1.31
--- src/sys/dev/iscsi/iscsi_send.c:1.30	Sun Jun  5 13:54:28 2016
+++ src/sys/dev/iscsi/iscsi_send.c	Wed Jun 15 04:30:30 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: iscsi_send.c,v 1.30 2016/06/05 13:54:28 mlelstv Exp $	*/
+/*	$NetBSD: iscsi_send.c,v 1.31 2016/06/15 04:30:30 mlelstv Exp $	*/
 
 /*-
  * Copyright (c) 2004,2005,2006,2011 The NetBSD Foundation, Inc.
@@ -93,7 +93,8 @@ assign_connection(session_t *session, bo
 
 	mutex_enter(&session->lock);
 	do {
-		if ((conn = session->mru_connection) == NULL) {
+		if (session->terminating ||
+		    (conn = session->mru_connection) == NULL) {
 			mutex_exit(&session->lock);
 			return NULL;
 		}
@@ -232,7 +233,9 @@ reassign_tasks(connection_t *oldconn)
 		free_pdu(opdu);
 
 		/* put ready CCB into waiting list of new connection */
+		mutex_enter(&conn->lock);
 		suspend_ccb(ccb, TRUE);
+		mutex_exit(&conn->lock);
 	}
 
 	if (pdu == NULL) {
@@ -255,7 +258,7 @@ reassign_tasks(connection_t *oldconn)
 			mutex_enter(&sess->lock);
 			if (ccb->CmdSN < sess->ExpCmdSN) {
 				pdu = ccb->pdu_waiting;
-				sn = get_sernum(sess, !(pdu->pdu.Opcode & OP_IMMEDIATE));
+				sn = get_sernum(sess, pdu);
 
 				/* update CmdSN */
 				DEBC(conn, 1, ("Resend Updating CmdSN - old %d, new %d\n",
@@ -312,18 +315,17 @@ iscsi_send_thread(void *par)
 
 				if (conn->HeaderDigest)
 					pdu->pdu.HeaderDigest = gen_digest(&pdu->pdu, BHS_SIZE);
-				DEBC(conn, 99, ("Transmitting PDU CmdSN = %u\n",
-				                ntohl(pdu->pdu.p.command.CmdSN)));
+
+				DEBC(conn, 99, ("Transmitting PDU CmdSN = %u, ExpStatSN = %u\n",
+				                ntohl(pdu->pdu.p.command.CmdSN),
+				                ntohl(pdu->pdu.p.command.ExpStatSN)));
 				my_soo_write(conn, &pdu->uio);
 
 				mutex_enter(&conn->lock);
 				pdisp = pdu->disp;
-				if (pdisp <= PDUDISP_FREE)
-					pdu->disp = PDUDISP_UNUSED;
-				else
+				if (pdisp > PDUDISP_FREE)
 					pdu->flags &= ~PDUF_BUSY;
 				mutex_exit(&conn->lock);
-
 				if (pdisp <= PDUDISP_FREE)
 					free_pdu(pdu);
 
@@ -459,8 +461,10 @@ send_pdu(ccb_t *ccb, pdu_t *pdu, ccb_dis
 
 	pdu->disp = pdisp;
 
-	DEBC(conn, 10, ("Send_pdu: ccb=%p, pcd=%d, cdsp=%d, pdu=%p, pdsp=%d\n",
-			ccb, prev_cdisp, cdisp, pdu, pdisp));
+	DEBC(conn, 10, ("Send_pdu: CmdSN=%u ExpStatSN~%u ccb=%p, pdu=%p\n",
+	                ntohl(pdu->pdu.p.command.CmdSN),
+			conn->StatSN_buf.ExpSN,
+			ccb, pdu));
 
 	mutex_enter(&conn->lock);
 	if (pdisp == PDUDISP_WAIT) {
@@ -486,10 +490,9 @@ send_pdu(ccb_t *ccb, pdu_t *pdu, ccb_dis
 	if (cdisp != CCBDISP_NOWAIT) {
 		ccb_timeout_start(ccb, COMMAND_TIMEOUT);
 
+		mutex_enter(&conn->lock);
 		if (prev_cdisp <= CCBDISP_NOWAIT)
 			suspend_ccb(ccb, TRUE);
-
-		mutex_enter(&conn->lock);
 		while (ccb->disp == CCBDISP_WAIT) {
 			DEBC(conn, 15, ("Send_pdu: ccb=%p cdisp=%d waiting\n",
 				ccb, ccb->disp));
@@ -528,7 +531,10 @@ resend_pdu(ccb_t *ccb)
 	pdu->uio = pdu->save_uio;
 	memcpy(pdu->io_vec, pdu->save_iovec, sizeof(pdu->io_vec));
 
-	DEBC(conn, 8, ("ReSend_pdu ccb=%p, pdu=%p\n", ccb, pdu));
+	DEBC(conn, 8, ("ReSend_pdu: CmdSN=%u ExpStatSN~%u ccb=%p, pdu=%p\n",
+	                ntohl(pdu->pdu.p.command.CmdSN),
+			conn->StatSN_buf.ExpSN,
+			ccb, pdu));
 
 	mutex_enter(&conn->lock);
 	/* Enqueue for sending */
@@ -632,7 +638,7 @@ init_login_pdu(connection_t *conn, ccb_t
 	pdu->Opcode = IOP_Login_Request | OP_IMMEDIATE;
 
 	mutex_enter(&conn->session->lock);
-	ccb->CmdSN = get_sernum(conn->session, false);
+	ccb->CmdSN = get_sernum(conn->session, ppdu);
 	mutex_exit(&conn->session->lock);
 
 	if (next) {
@@ -746,7 +752,7 @@ init_text_pdu(connection_t *conn, ccb_t 
 	pdu->Flags = FLAG_FINAL;
 
 	mutex_enter(&conn->session->lock);
-	ccb->CmdSN = get_sernum(conn->session, false);
+	ccb->CmdSN = get_sernum(conn->session, ppdu);
 	mutex_exit(&conn->session->lock);
 
 	if (rx_pdu != NULL) {
@@ -952,11 +958,11 @@ send_send_targets(session_t *session, ui
 int
 send_nop_out(connection_t *conn, pdu_t *rx_pdu)
 {
+	session_t *sess;
 	ccb_t *ccb;
 	pdu_t *ppdu;
 	pdu_header_t *pdu;
-
-	DEBC(conn, 10, ("Send NOP_Out rx_pdu=%p\n", rx_pdu));
+	uint32_t sn;
 
 	if (rx_pdu != NULL) {
 		ccb = NULL;
@@ -981,18 +987,27 @@ send_nop_out(connection_t *conn, pdu_t *
 	pdu->Flags = FLAG_FINAL;
 	pdu->Opcode = IOP_NOP_Out | OP_IMMEDIATE;
 
+	sess = conn->session;
+
+	mutex_enter(&sess->lock);
+	sn = get_sernum(sess, ppdu);
+	mutex_exit(&sess->lock);
+
 	if (rx_pdu != NULL) {
 		pdu->p.nop_out.TargetTransferTag =
 			rx_pdu->pdu.p.nop_in.TargetTransferTag;
 		pdu->InitiatorTaskTag = rx_pdu->pdu.InitiatorTaskTag;
-		pdu->p.nop_out.CmdSN = htonl(conn->session->CmdSN);
+		pdu->p.nop_out.CmdSN = htonl(sn);
 		pdu->LUN = rx_pdu->pdu.LUN;
 	} else {
 		pdu->p.nop_out.TargetTransferTag = 0xffffffff;
-		ccb->CmdSN = ccb->session->CmdSN;
-		pdu->p.nop_out.CmdSN = htonl(ccb->CmdSN);
+		pdu->InitiatorTaskTag = 0xffffffff;
+		ccb->CmdSN = sn;
+		pdu->p.nop_out.CmdSN = htonl(sn);
 	}
 
+	DEBC(conn, 10, ("Send NOP_Out CmdSN=%d, rx_pdu=%p\n", sn, rx_pdu));
+
 	setup_tx_uio(ppdu, 0, NULL, FALSE);
 	send_pdu(ccb, ppdu, (rx_pdu != NULL) ? CCBDISP_NOWAIT : CCBDISP_FREE,
 			 PDUDISP_FREE);
@@ -1364,30 +1379,22 @@ send_command(ccb_t *ccb, ccb_disp_t disp
 	pdu_header_t *pdu;
 
 	mutex_enter(&sess->lock);
-	while (/*CONSTCOND*/ISCSI_THROTTLING_ENABLED &&
-	    /*CONSTCOND*/!ISCSI_SERVER_TRUSTED &&
-	    !sernum_in_window(sess)) {
-
+	while (!sernum_in_window(sess)) {
+		mutex_exit(&sess->lock);
 		ccb->disp = disp;
-		if (waitok)
-			ccb->flags |= CCBF_WAITING;
-		throttle_ccb(ccb, TRUE);
-
-		if (!waitok) {
-			mutex_exit(&sess->lock);
-			DEBC(conn, 10, ("Throttling send_command, ccb = %p\n",ccb));
-			return;
-		}
-
-		DEBC(conn, 15, ("Wait send_command, ccb = %p\n",ccb));
-		cv_wait(&sess->ccb_cv, &sess->lock);
-		DEBC(conn, 15, ("Resuming send_command, ccb = %p\n",ccb));
-
-		throttle_ccb(ccb, FALSE);
-		ccb->flags &= ~CCBF_WAITING;
+		wake_ccb(ccb, ISCSI_STATUS_QUEUE_FULL);
+		return;
 	}
 	mutex_exit(&sess->lock);
 
+	/* Don't confuse targets during (re-)negotations */
+	if (conn->state != ST_FULL_FEATURE) {
+		DEBOUT(("Invalid connection for send_command, ccb = %p\n",ccb));
+		ccb->disp = disp;
+		wake_ccb(ccb, ISCSI_STATUS_TARGET_BUSY);
+		return;
+	}
+
 	ppdu = get_pdu(conn, waitok);
 	if (ppdu == NULL) {
 		DEBOUT(("No PDU for send_command, ccb = %p\n",ccb));
@@ -1432,7 +1439,7 @@ send_command(ccb_t *ccb, ccb_disp_t disp
 	ccb->flags |= CCBF_REASSIGN;
 
 	mutex_enter(&sess->lock);
-	ccb->CmdSN = get_sernum(sess, !immed);
+	ccb->CmdSN = get_sernum(sess, ppdu);
 	mutex_exit(&sess->lock);
 
 	pdu->p.command.CmdSN = htonl(ccb->CmdSN);

Reply via email to