Module Name:    src
Committed By:   mlelstv
Date:           Sun May 29 13:51:16 UTC 2016

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

Log Message:
Several improvements to the ISCSI driver.

- Enable debug messages but set log level to be quiet. Provide a
  system (hw.iscsi.debug) to set the log level at run time.
- Replace old tsleep/wakeup synchronization with mutexes and condvars.
- Defer actions from callouts (basically timeouts) to the cleanup thread.
- Protect lists and unique ids with mutexes. protect connection usecount
  by using atomic operations.
- Assert kernel lock when calling into scsipi and network code.
- Use this to make send/receive/cleanup threads MPSAFE.

- Fix handling of out-of-CCB/out-of-PDU conditions against the scsipi layer.
- Bump number of PDUs to 128 to avoid virtually all out-of-PDU conditions

- Make use of softc structure for attach/detach operations.
- Track open file handles to prevent detach when busy.

- Move some global variables to make them static.

- Fix 'Overlapping Commands Attempted' error by marking commands as
  simply ordered (ATTR_SIMPLE) like FreeBSD.


To generate a diff of this commit:
cvs rdiff -u -r1.13 -r1.14 src/sys/dev/iscsi/iscsi_globals.h \
    src/sys/dev/iscsi/iscsi_ioctl.c
cvs rdiff -u -r1.17 -r1.18 src/sys/dev/iscsi/iscsi_main.c
cvs rdiff -u -r1.10 -r1.11 src/sys/dev/iscsi/iscsi_rcv.c
cvs rdiff -u -r1.15 -r1.16 src/sys/dev/iscsi/iscsi_send.c
cvs rdiff -u -r1.9 -r1.10 src/sys/dev/iscsi/iscsi_text.c
cvs rdiff -u -r1.8 -r1.9 src/sys/dev/iscsi/iscsi_utils.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_globals.h
diff -u src/sys/dev/iscsi/iscsi_globals.h:1.13 src/sys/dev/iscsi/iscsi_globals.h:1.14
--- src/sys/dev/iscsi/iscsi_globals.h:1.13	Sat May 30 20:09:47 2015
+++ src/sys/dev/iscsi/iscsi_globals.h	Sun May 29 13:51:16 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: iscsi_globals.h,v 1.13 2015/05/30 20:09:47 joerg Exp $	*/
+/*	$NetBSD: iscsi_globals.h,v 1.14 2016/05/29 13:51:16 mlelstv Exp $	*/
 
 /*-
  * Copyright (c) 2004,2005,2006,2011 The NetBSD Foundation, Inc.
@@ -58,7 +58,7 @@
 
 /* ------------------------ Code selection constants ------------------------ */
 
-/* #define ISCSI_DEBUG      1 */
+#define ISCSI_DEBUG      0
 
 /* -------------------------  Global Constants  ----------------------------- */
 
@@ -96,7 +96,7 @@ now only in iscsi_send.c.
    high values of First/MaxBurstLength and small values of
    MaxRecvDataSegmentLength of the target.
 */
-#define PDUS_PER_CONNECTION   64	/* ToDo: Reasonable number?? */
+#define PDUS_PER_CONNECTION   128	/* ToDo: Reasonable number?? */
 
 /* max outstanding serial nums before we give up on the connection */
 #define SERNUM_BUFFER_LENGTH  (CCBS_PER_SESSION / 2)	/* ToDo: Reasonable?? */
@@ -181,7 +181,6 @@ typedef enum {
 
 typedef enum {
 	PDUDISP_UNUSED,		/* 0 = In free pool */
-	PDUDISP_SIGNAL,		/* Free this PDU when done and wakeup(pdu) */
 	PDUDISP_FREE,		/* Free this PDU when done */
 	PDUDISP_WAIT		/* Waiting for acknowledge */
 } pdu_disp_t;
@@ -253,6 +252,7 @@ struct ccb_s {
 	ccb_disp_t		disp;	/* what to do with this ccb */
 
 	struct callout		timeout; /* To make sure it isn't lost */
+	TAILQ_ENTRY(ccb_s)	tchain;
 	int			num_timeouts;
 	/* How often we've sent out SNACK without answer */
 	int			total_tries;
@@ -309,6 +309,11 @@ typedef struct ccb_list_s ccb_list_t;
 struct connection_s {
 	TAILQ_ENTRY(connection_s)	connections;
 
+	kmutex_t			lock;
+	kcondvar_t			conn_cv;
+	kcondvar_t			ccb_cv;
+	kcondvar_t			idle_cv;
+
 	pdu_list_t			pdu_pool; /* the free PDU pool */
 
 	ccb_list_t			ccbs_waiting;
@@ -358,7 +363,7 @@ struct connection_s {
 					/* if closing down: status */
 	int				recover; /* recovery count */
 		/* (reset on first successful data transfer) */
-	int				usecount; /* number of active CCBs */
+	unsigned			usecount; /* number of active CCBs */
 
 	bool				destroy; /* conn will be destroyed */
 	bool				in_session;
@@ -367,6 +372,7 @@ struct connection_s {
 		/* status of logout (for recovery) */
 	struct callout			timeout;
 		/* Timeout for checking if connection is dead */
+	TAILQ_ENTRY(connection_s)	tchain;
 	int				num_timeouts;
 		/* How often we've sent out a NOP without answer */
 	uint32_t			idle_timeout_val;
@@ -402,6 +408,10 @@ struct session_s {
 	/* local stuff */
 	TAILQ_ENTRY(session_s)	sessions;	/* the list of sessions */
 
+	kmutex_t		lock;
+	kcondvar_t		sess_cv;
+	kcondvar_t		ccb_cv;
+
 	ccb_list_t		ccb_pool;	/* The free CCB pool */
 	ccb_list_t		ccbs_throttled;
 				/* CCBs waiting for MaxCmdSN to increase */
@@ -457,18 +467,6 @@ typedef struct session_list_s session_li
 
 
 /*
-   The softc structure. This driver doesn't really need one, because there's
-   always just one instance, and for the time being it's only loaded as
-   an LKM (which doesn't create a softc), but we need one to put into the
-   scsipi interface structures, so here it is.
-*/
-
-typedef struct iscsi_softc {
-	device_t		sc_dev;
-} iscsi_softc_t;
-
-
-/*
    Event notification structures
 */
 
@@ -502,7 +500,9 @@ typedef struct event_handler_list_s even
 
 /* /dev/iscsi0 state */
 struct iscsifd {
-	char dummy;
+	TAILQ_ENTRY(iscsifd)		link;
+	device_t	dev;
+	int		unit;
 };
 
 /* -------------------------  Global Variables  ----------------------------- */
@@ -512,12 +512,7 @@ struct iscsifd {
 extern struct cfattach iscsi_ca;		/* the device attach structure */
 
 extern session_list_t iscsi_sessions;		/* the list of sessions */
-
-extern connection_list_t iscsi_cleanupc_list;	/* connections to clean up */
-extern session_list_t iscsi_cleanups_list;	/* sessions to clean up */
 extern bool iscsi_detaching;			/* signal to cleanup thread it should exit */
-extern struct lwp *iscsi_cleanproc;		/* pointer to cleanup proc */
-
 extern uint32_t iscsi_num_send_threads;		/* the number of active send threads */
 
 extern uint8_t iscsi_InitiatorName[ISCSI_STRING_LENGTH];
@@ -539,7 +534,7 @@ extern int iscsi_debug_level;	/* How muc
 #define DEBC(conn,lev,x) { if (iscsi_debug_level >= lev) { printf("S%dC%d: ", \
 				conn ? conn->session->id : -1, \
 				conn ? conn->id : -1); printf x ;}}
-void dump(void *buf, int len);
+void iscsi_hexdump(void *buf, int len);
 
 #define STATIC static
 
@@ -548,7 +543,7 @@ void dump(void *buf, int len);
 #define DEBOUT(x)
 #define DEB(lev,x)
 #define DEBC(conn,lev,x)
-#define dump(a,b)
+#define iscsi_hexdump(a,b)
 
 #define STATIC static
 
@@ -634,6 +629,11 @@ sn_a_le_b(uint32_t a, uint32_t b)
 
 /* in iscsi_ioctl.c */
 
+void iscsi_init_cleanup(void);
+void iscsi_destroy_cleanup(void);
+void iscsi_notify_cleanup(void);
+
+
 /* Parameter for logout is reason code in logout PDU, -1 for don't send logout */
 #define NO_LOGOUT          -1
 #define LOGOUT_SESSION     0
@@ -646,7 +646,7 @@ void kill_connection(connection_t *, uin
 void kill_session(session_t *, uint32_t, int, bool);
 void kill_all_sessions(void);
 void handle_connection_error(connection_t *, uint32_t, int);
-void iscsi_cleanup_thread(void *);
+void add_connection_cleanup(connection_t *);
 
 #ifndef ISCSI_MINIMAL
 uint32_t map_databuf(struct proc *, void **, uint32_t);
@@ -664,7 +664,7 @@ connection_t *find_connection(session_t 
 int iscsidetach(device_t, int);
 
 void iscsi_done(ccb_t *);
-int map_session(session_t *);
+int map_session(session_t *, device_t);
 int unmap_session(session_t *);
 
 /* in iscsi_send.c */
@@ -694,8 +694,11 @@ void send_command(ccb_t *, ccb_disp_t, b
 int send_io_command(session_t *, uint64_t, scsireq_t *, bool, uint32_t);
 #endif
 
-void connection_timeout(void *);
-void ccb_timeout(void *);
+void connection_timeout_co(void *);
+void ccb_timeout_co(void *);
+
+void connection_timeout(connection_t *);
+void ccb_timeout(ccb_t *);
 
 /* in iscsi_rcv.c */
 
Index: src/sys/dev/iscsi/iscsi_ioctl.c
diff -u src/sys/dev/iscsi/iscsi_ioctl.c:1.13 src/sys/dev/iscsi/iscsi_ioctl.c:1.14
--- src/sys/dev/iscsi/iscsi_ioctl.c:1.13	Sat Sep 19 18:32:42 2015
+++ src/sys/dev/iscsi/iscsi_ioctl.c	Sun May 29 13:51:16 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: iscsi_ioctl.c,v 1.13 2015/09/19 18:32:42 dholland Exp $	*/
+/*	$NetBSD: iscsi_ioctl.c,v 1.14 2016/05/29 13:51:16 mlelstv Exp $	*/
 
 /*-
  * Copyright (c) 2004,2005,2006,2011 The NetBSD Foundation, Inc.
@@ -40,12 +40,29 @@
 #include <uvm/uvm_pmap.h>
 #endif
 
+static kmutex_t iscsi_cleanup_mtx;
+static kcondvar_t iscsi_cleanup_cv;
+static kcondvar_t iscsi_event_cv;
+static struct lwp *iscsi_cleanproc = NULL;
+
 static uint16_t current_id = 0;	/* Global session ID counter */
 
 /* list of event handlers */
 static event_handler_list_t event_handlers =
 	TAILQ_HEAD_INITIALIZER(event_handlers);
 
+static connection_list_t iscsi_timeout_conn_list =
+	TAILQ_HEAD_INITIALIZER(iscsi_timeout_conn_list);
+
+static ccb_list_t iscsi_timeout_ccb_list =
+	TAILQ_HEAD_INITIALIZER(iscsi_timeout_ccb_list);
+
+static session_list_t iscsi_cleanups_list =
+	TAILQ_HEAD_INITIALIZER(iscsi_cleanups_list);
+
+static connection_list_t iscsi_cleanupc_list =
+	TAILQ_HEAD_INITIALIZER(iscsi_cleanupc_list);
+
 static uint32_t handler_id = 0;	/* Handler ID counter */
 
 /* -------------------------------------------------------------------------- */
@@ -64,11 +81,13 @@ static uint32_t handler_id = 0;	/* Handl
  */
 
 
-STATIC event_handler_t *
+static event_handler_t *
 find_handler(uint32_t id)
 {
 	event_handler_t *curr;
 
+	KASSERT(mutex_owned(&iscsi_cleanup_mtx));
+
 	TAILQ_FOREACH(curr, &event_handlers, link)
 		if (curr->id == id)
 			break;
@@ -85,12 +104,11 @@ find_handler(uint32_t id)
  *          par   The parameter.
  */
 
-STATIC void
+static void
 register_event(iscsi_register_event_parameters_t *par)
 {
 	event_handler_t *handler;
 	int was_empty;
-	int s;
 
 	handler = malloc(sizeof(event_handler_t), M_DEVBUF, M_WAITOK | M_ZERO);
 	if (handler == NULL) {
@@ -101,21 +119,19 @@ register_event(iscsi_register_event_para
 
 	TAILQ_INIT(&handler->events);
 
+	mutex_enter(&iscsi_cleanup_mtx);
 	/* create a unique ID */
-	s = splbio();
 	do {
 		++handler_id;
 	} while (!handler_id || find_handler(handler_id) != NULL);
 	par->event_id = handler->id = handler_id;
 
 	was_empty = TAILQ_FIRST(&event_handlers) == NULL;
-
 	TAILQ_INSERT_TAIL(&event_handlers, handler, link);
+	mutex_exit(&iscsi_cleanup_mtx);
 
-	if (was_empty) {
-		wakeup(&iscsi_cleanupc_list);
-	}
-	splx(s);
+	if (was_empty)
+		iscsi_notify_cleanup();
 
 	par->status = ISCSI_STATUS_SUCCESS;
 	DEB(5, ("Register Event OK, ID %d\n", par->event_id));
@@ -130,27 +146,27 @@ register_event(iscsi_register_event_para
  *          par   The parameter.
  */
 
-STATIC void
+static void
 deregister_event(iscsi_register_event_parameters_t *par)
 {
 	event_handler_t *handler;
 	event_t *evt;
-	int s;
 
+	mutex_enter(&iscsi_cleanup_mtx);
 	handler = find_handler(par->event_id);
 	if (handler == NULL) {
+		mutex_exit(&iscsi_cleanup_mtx);
 		DEB(1, ("Deregister Event ID %d not found\n", par->event_id));
 		par->status = ISCSI_STATUS_INVALID_EVENT_ID;
 		return;
 	}
 
-	s = splbio();
 	TAILQ_REMOVE(&event_handlers, handler, link);
-	splx(s);
+	mutex_exit(&iscsi_cleanup_mtx);
 
 	if (handler->waiter != NULL) {
 		handler->waiter->status = ISCSI_STATUS_EVENT_DEREGISTERED;
-		wakeup(handler->waiter);
+		cv_broadcast(&iscsi_event_cv);
 	}
 
 	while ((evt = TAILQ_FIRST(&handler->events)) != NULL) {
@@ -173,19 +189,23 @@ deregister_event(iscsi_register_event_pa
  *          wait  Wait for event if true
  */
 
-STATIC void
+static void
 check_event(iscsi_wait_event_parameters_t *par, bool wait)
 {
 	event_handler_t *handler;
 	event_t *evt;
+	int rc;
 
+	mutex_enter(&iscsi_cleanup_mtx);
 	handler = find_handler(par->event_id);
 	if (handler == NULL) {
+		mutex_exit(&iscsi_cleanup_mtx);
 		DEBOUT(("Wait Event ID %d not found\n", par->event_id));
 		par->status = ISCSI_STATUS_INVALID_EVENT_ID;
 		return;
 	}
 	if (handler->waiter != NULL) {
+		mutex_exit(&iscsi_cleanup_mtx);
 		DEBOUT(("Wait Event ID %d already waiting\n", par->event_id));
 		par->status = ISCSI_STATUS_EVENT_WAITING;
 		return;
@@ -194,27 +214,27 @@ check_event(iscsi_wait_event_parameters_
 	DEB(99, ("Wait Event ID %d\n", par->event_id));
 
 	do {
-		int s = splbio();
 		evt = TAILQ_FIRST(&handler->events);
 		if (evt != NULL) {
 			TAILQ_REMOVE(&handler->events, evt, link);
-			splx(s);
 		} else {
 			if (!wait) {
-				splx(s);
 				par->status = ISCSI_STATUS_LIST_EMPTY;
 				return;
 			}
 			if (par->status != ISCSI_STATUS_SUCCESS) {
-				splx(s);
 				return;
 			}
 			handler->waiter = par;
-			splx(s);
-			if (tsleep(par, PRIBIO | PCATCH, "iscsievtwait", 0))
+			rc = cv_wait_sig(&iscsi_event_cv, &iscsi_cleanup_mtx);
+			if (rc) {
+				mutex_exit(&iscsi_cleanup_mtx);
+				par->status = ISCSI_STATUS_LIST_EMPTY;
 				return;
+			}
 		}
 	} while (evt == NULL);
+	mutex_exit(&iscsi_cleanup_mtx);
 
 	par->connection_id = evt->connection_id;
 	par->session_id = evt->session_id;
@@ -242,16 +262,16 @@ add_event(iscsi_event_t kind, uint32_t s
 {
 	event_handler_t *curr;
 	event_t *evt;
-	int s;
 
 	DEB(9, ("Add_event kind %d, sid %d, cid %d, reason %d\n",
 		kind, sid, cid, reason));
 
-	s = splbio();
+	mutex_enter(&iscsi_cleanup_mtx);
 	TAILQ_FOREACH(curr, &event_handlers, link) {
-		evt = malloc(sizeof(*evt), M_TEMP, M_WAITOK);
+		evt = malloc(sizeof(*evt), M_TEMP, M_NOWAIT);
 		if (evt == NULL) {
-			panic("iSCSI: add_event failed to alloc memory");
+			DEBOUT(("Cannot allocate event\n"));
+			break;
 		}
 		evt->event_kind = kind;
 		evt->session_id = sid;
@@ -260,11 +280,11 @@ add_event(iscsi_event_t kind, uint32_t s
 
 		TAILQ_INSERT_TAIL(&curr->events, evt, link);
 		if (curr->waiter != NULL) {
-			wakeup(curr->waiter);
 			curr->waiter = NULL;
+			cv_broadcast(&iscsi_event_cv);
 		}
 	}
-	splx(s);
+	mutex_exit(&iscsi_cleanup_mtx);
 }
 
 
@@ -280,15 +300,16 @@ add_event(iscsi_event_t kind, uint32_t s
  *    Note that this will not detect dead handlers if no events are pending,
  *    but we don't care as long as events don't accumulate in the list.
  *
- *    this function must be called at splbio
  */
 
-STATIC void
+static void
 check_event_handlers(void)
 {
 	event_handler_t *curr, *next;
 	event_t *evt;
 
+	KASSERT(mutex_owned(&iscsi_cleanup_mtx));
+
 	for (curr = TAILQ_FIRST(&event_handlers); curr != NULL; curr = next) {
 		next = TAILQ_NEXT(curr, link);
 		evt = TAILQ_FIRST(&curr->events);
@@ -322,7 +343,7 @@ check_event_handlers(void)
  *
  */
 
-STATIC int
+static int
 get_socket(int fdes, struct file **fpp)
 {
 	struct file *fp;
@@ -352,7 +373,7 @@ get_socket(int fdes, struct file **fpp)
  *
  */
 
-STATIC void
+static void
 release_socket(struct file *fp)
 {
 	/* Add the reference */
@@ -375,14 +396,13 @@ session_t *
 find_session(uint32_t id)
 {
 	session_t *curr;
-	int s;
 
-	s = splbio();
+	KASSERT(mutex_owned(&iscsi_cleanup_mtx));
+
 	TAILQ_FOREACH(curr, &iscsi_sessions, sessions)
 		if (curr->id == id) {
 			break;
 		}
-	splx(s);
 	return curr;
 }
 
@@ -400,14 +420,13 @@ connection_t *
 find_connection(session_t *session, uint32_t id)
 {
 	connection_t *curr;
-	int s;
 
-	s = splbio();
+	KASSERT(mutex_owned(&iscsi_cleanup_mtx));
+
 	TAILQ_FOREACH(curr, &session->conn_list, connections)
 		if (curr->id == id) {
 			break;
 		}
-	splx(s);
 	return curr;
 }
 
@@ -427,12 +446,12 @@ void
 kill_connection(connection_t *conn, uint32_t status, int logout, bool recover)
 {
 	session_t *sess = conn->session;
-	int s;
 
 	DEBC(conn, 1, ("Kill_connection: terminating=%d, status=%d, logout=%d, "
 			   "state=%d\n",
 			   conn->terminating, status, logout, conn->state));
 
+	mutex_enter(&iscsi_cleanup_mtx);
 	if (recover &&
 	    !conn->destroy &&
 	    conn->recover > MAX_RECOVERY_ATTEMPTS) {
@@ -443,13 +462,11 @@ kill_connection(connection_t *conn, uint
 
 	if (!recover || conn->destroy) {
 
-		s = splbio();
 		if (conn->in_session) {
 			conn->in_session = FALSE;
 			TAILQ_REMOVE(&sess->conn_list, conn, connections);
 			sess->mru_connection = TAILQ_FIRST(&sess->conn_list);
 		}
-		splx(s);
 
 		if (!conn->destroy) {
 			DEBC(conn, 1, ("Kill_connection setting destroy flag\n"));
@@ -457,14 +474,17 @@ kill_connection(connection_t *conn, uint
 		}
 		/* in case it was already terminated earlier and rcv/send-threads */
 		/* are waiting */
-		wakeup(conn);
+		cv_broadcast(&conn->idle_cv);
 	}
 
 	/* Don't recurse */
 	if (conn->terminating) {
+		mutex_exit(&iscsi_cleanup_mtx);
 		DEBC(conn, 1, ("Kill_connection exiting (already terminating)\n"));
 		return;
 	}
+	conn->terminating = status;
+	mutex_exit(&iscsi_cleanup_mtx);
 
 	if (conn->state == ST_FULL_FEATURE) {
 		sess->active_connections--;
@@ -488,6 +508,7 @@ kill_connection(connection_t *conn, uint
 				logout = LOGOUT_SESSION;
 			}
 			if (!send_logout(conn, conn, logout, FALSE)) {
+				conn->terminating = ISCSI_STATUS_SUCCESS;
 				return;
 			}
 			/*
@@ -499,11 +520,10 @@ kill_connection(connection_t *conn, uint
 		}
 	}
 
-	conn->terminating = status;
 	conn->state = ST_SETTLING;
 
 	/* let send thread take over next step of cleanup */
-	wakeup(&conn->pdus_to_send);
+	cv_broadcast(&conn->conn_cv);
 
 	DEBC(conn, 5, ("kill_connection returns\n"));
 }
@@ -525,19 +545,28 @@ kill_session(session_t *session, uint32_
 {
 	connection_t *curr;
 	ccb_t *ccb;
-	int s;
 
 	DEB(1, ("ISCSI: kill_session %d, status %d, logout %d, recover %d\n",
 			session->id, status, logout, recover));
 
+	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;
+	}
+
 	/*
 	 * 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) {
+		mutex_exit(&iscsi_cleanup_mtx);
 		return;
 	}
+	session->terminating = status;
+	mutex_exit(&iscsi_cleanup_mtx);
 
 	if (recover) {
 		/*
@@ -553,35 +582,31 @@ kill_session(session_t *session, uint32_
 		}
 		/* don't allow the session to disappear when the target */
 		/* requested the logout */
+		session->terminating = ISCSI_STATUS_SUCCESS;
 		return;
 	}
 
 	/* remove from session list */
-	s = splbio();
+	mutex_enter(&iscsi_cleanup_mtx);
 	TAILQ_REMOVE(&iscsi_sessions, session, sessions);
-	splx(s);
 	session->sessions.tqe_next = NULL;
 	session->sessions.tqe_prev = NULL;
+	mutex_exit(&iscsi_cleanup_mtx);
 
 	/* complete any throttled CCBs */
-	s = splbio();
+	mutex_enter(&session->lock);
 	while ((ccb = TAILQ_FIRST(&session->ccbs_throttled)) != NULL) {
 		throttle_ccb(ccb, FALSE);
-		splx(s);
+		mutex_exit(&session->lock);
 		wake_ccb(ccb, ISCSI_STATUS_LOGOUT);
-		s = splbio();
+		mutex_enter(&session->lock);
 	}
-	splx(s);
+	mutex_exit(&session->lock);
 
 	/*
-	 * unmap first to give the system an opportunity to flush its buffers,
-	 * but don't try to unmap if it's a forced termination (connection is dead)
-	 * to avoid waiting for pending commands that can't complete anyway.
+	 * unmap first to give the system an opportunity to flush its buffers
 	 */
-	if (logout >= 0) {
-		unmap_session(session);
-		DEB(5, ("Unmap Returns\n"));
-	}
+	unmap_session(session);
 
 	/* kill all connections */
 	while ((curr = TAILQ_FIRST(&session->conn_list)) != NULL) {
@@ -609,12 +634,12 @@ kill_session(session_t *session, uint32_
  *                <0 on failure, connection is still terminating
  */
 
-STATIC int
+static int
 create_connection(iscsi_login_parameters_t *par, session_t *session,
 				  struct lwp *l)
 {
 	connection_t *connection;
-	int rc, s;
+	int rc;
 
 	DEB(1, ("Create Connection for Session %d\n", session->id));
 
@@ -633,13 +658,14 @@ create_connection(iscsi_login_parameters
 		return EIO;
 	}
 
+	mutex_enter(&iscsi_cleanup_mtx);
 	/* create a unique ID */
 	do {
 		++session->conn_id;
 	} while (!session->conn_id ||
 		 find_connection(session, session->conn_id) != NULL);
-
 	par->connection_id = connection->id = session->conn_id;
+	mutex_exit(&iscsi_cleanup_mtx);
 	DEB(99, ("Connection ID = %d\n", connection->id));
 
 	connection->session = session;
@@ -648,8 +674,13 @@ create_connection(iscsi_login_parameters
 	TAILQ_INIT(&connection->pdus_to_send);
 	TAILQ_INIT(&connection->pdu_pool);
 
-	callout_init(&connection->timeout, 0);
-	callout_setfunc(&connection->timeout, connection_timeout, connection);
+	mutex_init(&connection->lock, MUTEX_DEFAULT, IPL_BIO);
+	cv_init(&connection->conn_cv, "conn");
+	cv_init(&connection->ccb_cv, "ccbwait");
+	cv_init(&connection->idle_cv, "idle");
+
+	callout_init(&connection->timeout, CALLOUT_MPSAFE);
+	callout_setfunc(&connection->timeout, connection_timeout_co, connection);
 	connection->idle_timeout_val = CONNECTION_IDLE_TIMEOUT;
 
 	init_sernum(&connection->StatSN_buf);
@@ -658,6 +689,10 @@ create_connection(iscsi_login_parameters
 	if ((rc = get_socket(par->socket, &connection->sock)) != 0) {
 		DEBOUT(("Invalid socket %d\n", par->socket));
 
+		cv_destroy(&connection->idle_cv);
+		cv_destroy(&connection->ccb_cv);
+		cv_destroy(&connection->conn_cv);
+		mutex_destroy(&connection->lock);
 		free(connection, M_DEVBUF);
 		par->status = ISCSI_STATUS_INVALID_SOCKET;
 		return rc;
@@ -671,19 +706,23 @@ create_connection(iscsi_login_parameters
 	connection->threadobj = l;
 	connection->login_par = par;
 
-	/*DEBOUT (("Creating receive thread\n")); */
-	if ((rc = kthread_create(PRI_NONE, 0, NULL, iscsi_rcv_thread,
+	DEB(5, ("Creating receive thread\n"));
+	if ((rc = kthread_create(PRI_NONE, KTHREAD_MPSAFE, NULL, iscsi_rcv_thread,
 				connection, &connection->rcvproc,
 				"ConnRcv")) != 0) {
 		DEBOUT(("Can't create rcv thread (rc %d)\n", rc));
 
 		release_socket(connection->sock);
+		cv_destroy(&connection->idle_cv);
+		cv_destroy(&connection->ccb_cv);
+		cv_destroy(&connection->conn_cv);
+		mutex_destroy(&connection->lock);
 		free(connection, M_DEVBUF);
 		par->status = ISCSI_STATUS_NO_RESOURCES;
 		return rc;
 	}
-	/*DEBOUT (("Creating send thread\n")); */
-	if ((rc = kthread_create(PRI_NONE, 0, NULL, iscsi_send_thread,
+	DEB(5, ("Creating send thread\n"));
+	if ((rc = kthread_create(PRI_NONE, KTHREAD_MPSAFE, NULL, iscsi_send_thread,
 				connection, &connection->sendproc,
 				"ConnSend")) != 0) {
 		DEBOUT(("Can't create send thread (rc %d)\n", rc));
@@ -702,9 +741,13 @@ create_connection(iscsi_login_parameters
 		closef(connection->sock);
 
 		/* give receive thread time to exit */
-		tsleep(connection, PWAIT, "settle", 2 * hz);
+		kpause("settle", false, 2 * hz, NULL);
 
 		release_socket(connection->sock);
+		cv_destroy(&connection->idle_cv);
+		cv_destroy(&connection->ccb_cv);
+		cv_destroy(&connection->conn_cv);
+		mutex_destroy(&connection->lock);
 		free(connection, M_DEVBUF);
 		par->status = ISCSI_STATUS_NO_RESOURCES;
 		return rc;
@@ -724,14 +767,12 @@ create_connection(iscsi_login_parameters
 		return -1;
 	}
 
-	s = splbio();
 	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;
-	splx(s);
 
 	DEBC(connection, 5, ("Connection created successfully!\n"));
 	return 0;
@@ -752,11 +793,11 @@ create_connection(iscsi_login_parameters
  *                <0 on failure, connection is still terminating
  */
 
-STATIC int
+static int
 recreate_connection(iscsi_login_parameters_t *par, session_t *session,
 					connection_t *connection, struct lwp *l)
 {
-	int rc, s;
+	int rc;
 	ccb_t *ccb;
 	ccb_list_t old_waiting;
 
@@ -801,12 +842,10 @@ recreate_connection(iscsi_login_paramete
 	session->active_connections++;
 
 	TAILQ_INIT(&old_waiting);
-	s = splbio();
 	TAILQ_CONCAT(&old_waiting, &connection->ccbs_waiting, chain);
-	splx(s);
 
 	init_sernum(&connection->StatSN_buf);
-	wakeup(connection);
+	cv_broadcast(&connection->idle_cv);
 
 	if ((rc = send_login(connection)) != 0) {
 		DEBOUT(("Login failed (rc %d)\n", rc));
@@ -823,20 +862,17 @@ recreate_connection(iscsi_login_paramete
 	DEBC(connection, 9, ("Re-Login successful\n"));
 	par->status = ISCSI_STATUS_SUCCESS;
 
-	s = splbio();
 	connection->state = ST_FULL_FEATURE;
 	session->mru_connection = connection;
-	splx(s);
 
 	while ((ccb = TAILQ_FIRST(&old_waiting)) != NULL) {
 		TAILQ_REMOVE(&old_waiting, ccb, chain);
-		s = splbio();
 		suspend_ccb(ccb, TRUE);
-		splx(s);
 
 		rc = send_task_management(connection, ccb, NULL, TASK_REASSIGN);
 		/* if we get an error on reassign, restart the original request */
 		if (rc && ccb->pdu_waiting != NULL) {
+			mutex_enter(&session->lock);
 			if (ccb->CmdSN < session->ExpCmdSN) {
 				pdu_t *pdu = ccb->pdu_waiting;
 
@@ -848,15 +884,16 @@ recreate_connection(iscsi_login_paramete
 					session->CmdSN++;
 				pdu->pdu.p.command.CmdSN = htonl(ccb->CmdSN);
 			}
+			mutex_exit(&session->lock);
 			resend_pdu(ccb);
 		} else {
 			callout_schedule(&ccb->timeout, COMMAND_TIMEOUT);
 		}
 	}
 
-	wakeup(session);
+	cv_broadcast(&session->sess_cv);
 
-	DEBC(connection, 5, ("Connection ReCreated successfully - status %d\n",
+	DEBC(connection, 0, ("Connection ReCreated successfully - status %d\n",
 						 par->status));
 
 	return 0;
@@ -875,7 +912,7 @@ recreate_connection(iscsi_login_paramete
  *    Returns:    0 on success, else an error code.
  */
 
-STATIC int
+static int
 check_login_pars(iscsi_login_parameters_t *par)
 {
 	int i, n;
@@ -947,11 +984,11 @@ check_login_pars(iscsi_login_parameters_
  *          l        IN: The lwp pointer of the caller
  */
 
-STATIC void
-login(iscsi_login_parameters_t *par, struct lwp *l)
+static void
+login(iscsi_login_parameters_t *par, struct lwp *l, device_t dev)
 {
 	session_t *session;
-	int rc, s;
+	int rc;
 
 	DEB(99, ("ISCSI: login\n"));
 
@@ -975,11 +1012,17 @@ login(iscsi_login_parameters_t *par, str
 	TAILQ_INIT(&session->ccb_pool);
 	TAILQ_INIT(&session->ccbs_throttled);
 
+	mutex_init(&session->lock, MUTEX_DEFAULT, IPL_BIO);
+	cv_init(&session->sess_cv, "session");
+	cv_init(&session->ccb_cv, "ccb");
+
+	mutex_enter(&iscsi_cleanup_mtx);
 	/* create a unique ID */
 	do {
 		++current_id;
 	} while (!current_id || find_session(current_id) != NULL);
 	par->session_id = session->id = current_id;
+	mutex_exit(&iscsi_cleanup_mtx);
 
 	create_ccbs(session);
 	session->login_type = par->login_type;
@@ -987,20 +1030,25 @@ login(iscsi_login_parameters_t *par, str
 
 	if ((rc = create_connection(par, session, l)) != 0) {
 		if (rc > 0) {
+			cv_destroy(&session->ccb_cv);
+			cv_destroy(&session->sess_cv);
+			mutex_destroy(&session->lock);
 			free(session, M_DEVBUF);
 		}
 		return;
 	}
 
-	s = splbio();
+	mutex_enter(&iscsi_cleanup_mtx);
 	TAILQ_INSERT_HEAD(&iscsi_sessions, session, sessions);
-	splx(s);
+	mutex_exit(&iscsi_cleanup_mtx);
 
 	/* Session established, map LUNs? */
 	if (par->login_type == ISCSI_LOGINTYPE_MAP) {
 		copyinstr(par->TargetName, session->tgtname,
 		    sizeof(session->tgtname), NULL);
-		if (!map_session(session)) {
+		DEB(1, ("Login: map session %d\n", session->id));
+		if (!map_session(session, dev)) {
+			DEB(1, ("Login: map session %d failed\n", session->id));
 			kill_session(session, ISCSI_STATUS_MAP_FAILED,
 					LOGOUT_SESSION, FALSE);
 			par->status = ISCSI_STATUS_MAP_FAILED;
@@ -1018,18 +1066,21 @@ login(iscsi_login_parameters_t *par, str
  *          par      IN/OUT: The login parameters
  */
 
-STATIC void
+static void
 logout(iscsi_logout_parameters_t *par)
 {
 	session_t *session;
 
 	DEB(5, ("ISCSI: logout session %d\n", par->session_id));
 
+	mutex_enter(&iscsi_cleanup_mtx);
 	if ((session = find_session(par->session_id)) == NULL) {
+		mutex_exit(&iscsi_cleanup_mtx);
 		DEBOUT(("Session %d not found\n", par->session_id));
 		par->status = ISCSI_STATUS_INVALID_SESSION_ID;
 		return;
 	}
+	mutex_exit(&iscsi_cleanup_mtx);
 	/* If the session exists, this always succeeds */
 	par->status = ISCSI_STATUS_SUCCESS;
 
@@ -1046,21 +1097,26 @@ logout(iscsi_logout_parameters_t *par)
  *          l        IN: The lwp pointer of the caller
  */
 
-STATIC void
+static void
 add_connection(iscsi_login_parameters_t *par, struct lwp *l)
 {
 	session_t *session;
 
 	DEB(5, ("ISCSI: add_connection to session %d\n", par->session_id));
 
+	mutex_enter(&iscsi_cleanup_mtx);
 	if ((session = find_session(par->session_id)) == NULL) {
+		mutex_exit(&iscsi_cleanup_mtx);
 		DEBOUT(("Session %d not found\n", par->session_id));
 		par->status = ISCSI_STATUS_INVALID_SESSION_ID;
 		return;
 	}
+	mutex_exit(&iscsi_cleanup_mtx);
 	if ((par->status = check_login_pars(par)) == 0) {
 		create_connection(par, session, l);
 	}
+
+	iscsi_notify_cleanup();
 }
 
 
@@ -1072,7 +1128,7 @@ add_connection(iscsi_login_parameters_t 
  *          par      IN/OUT: The remove parameters
  */
 
-STATIC void
+static void
 remove_connection(iscsi_remove_parameters_t *par)
 {
 	connection_t *conn;
@@ -1081,18 +1137,22 @@ remove_connection(iscsi_remove_parameter
 	DEB(5, ("ISCSI: remove_connection %d from session %d\n",
 			par->connection_id, par->session_id));
 
+	mutex_enter(&iscsi_cleanup_mtx);
 	if ((session = find_session(par->session_id)) == NULL) {
+		mutex_exit(&iscsi_cleanup_mtx);
 		DEBOUT(("Session %d not found\n", par->session_id));
 		par->status = ISCSI_STATUS_INVALID_SESSION_ID;
 		return;
 	}
 
 	if ((conn = find_connection(session, par->connection_id)) == NULL) {
+		mutex_exit(&iscsi_cleanup_mtx);
 		DEBOUT(("Connection %d not found in session %d\n",
 				par->connection_id, par->session_id));
 
 		par->status = ISCSI_STATUS_INVALID_CONNECTION_ID;
 	} else {
+		mutex_exit(&iscsi_cleanup_mtx);
 		kill_connection(conn, ISCSI_STATUS_LOGOUT, LOGOUT_CONNECTION,
 					FALSE);
 		par->status = ISCSI_STATUS_SUCCESS;
@@ -1109,7 +1169,7 @@ remove_connection(iscsi_remove_parameter
  *          l        IN: The lwp pointer of the caller
  */
 
-STATIC void
+static void
 restore_connection(iscsi_login_parameters_t *par, struct lwp *l)
 {
 	session_t *session;
@@ -1118,18 +1178,22 @@ restore_connection(iscsi_login_parameter
 	DEB(1, ("ISCSI: restore_connection %d of session %d\n",
 			par->connection_id, par->session_id));
 
+	mutex_enter(&iscsi_cleanup_mtx);
 	if ((session = find_session(par->session_id)) == NULL) {
+		mutex_exit(&iscsi_cleanup_mtx);
 		DEBOUT(("Session %d not found\n", par->session_id));
 		par->status = ISCSI_STATUS_INVALID_SESSION_ID;
 		return;
 	}
 
 	if ((connection = find_connection(session, par->connection_id)) == NULL) {
+		mutex_exit(&iscsi_cleanup_mtx);
 		DEBOUT(("Connection %d not found in session %d\n",
 				par->connection_id, par->session_id));
 		par->status = ISCSI_STATUS_INVALID_CONNECTION_ID;
 		return;
 	}
+	mutex_exit(&iscsi_cleanup_mtx);
 
 	if ((par->status = check_login_pars(par)) == 0) {
 		recreate_connection(par, session, connection, l);
@@ -1234,7 +1298,7 @@ unmap_databuf(struct proc *p, void *buf,
  *          l        IN: The lwp pointer of the caller
  */
 
-STATIC void
+static void
 io_command(iscsi_iocommand_parameters_t *par, struct lwp *l)
 {
 	uint32_t datalen = par->req.datalen;
@@ -1242,11 +1306,14 @@ io_command(iscsi_iocommand_parameters_t 
 	session_t *session;
 
 	DEB(9, ("ISCSI: io_command, SID=%d, lun=%" PRIu64 "\n", par->session_id, par->lun));
+	mutex_enter(&iscsi_cleanup_mtx);
 	if ((session = find_session(par->session_id)) == NULL) {
+		mutex_exit(&iscsi_cleanup_mtx);
 		DEBOUT(("Session %d not found\n", par->session_id));
 		par->status = ISCSI_STATUS_INVALID_SESSION_ID;
 		return;
 	}
+	mutex_exit(&iscsi_cleanup_mtx);
 
 	par->req.senselen_used = 0;
 	par->req.datalen_used = 0;
@@ -1306,18 +1373,21 @@ io_command(iscsi_iocommand_parameters_t 
  *          par      IN/OUT: The send_targets parameters
  */
 
-STATIC void
+static void
 send_targets(iscsi_send_targets_parameters_t *par)
 {
 	int rc;
 	uint32_t rlen, cplen;
 	session_t *session;
 
+	mutex_enter(&iscsi_cleanup_mtx);
 	if ((session = find_session(par->session_id)) == NULL) {
+		mutex_exit(&iscsi_cleanup_mtx);
 		DEBOUT(("Session %d not found\n", par->session_id));
 		par->status = ISCSI_STATUS_INVALID_SESSION_ID;
 		return;
 	}
+	mutex_exit(&iscsi_cleanup_mtx);
 
 	DEB(9, ("ISCSI: send_targets, rsp_size=%d; Saved list: %p\n",
 			par->response_size, session->target_list));
@@ -1355,7 +1425,7 @@ send_targets(iscsi_send_targets_paramete
  *          par      IN/OUT: The set_node_name parameters
  */
 
-STATIC void
+static void
 set_node_name(iscsi_set_node_name_parameters_t *par)
 {
 
@@ -1392,13 +1462,15 @@ set_node_name(iscsi_set_node_name_parame
  *          par      IN/OUT: The status parameters
  */
 
-STATIC void
+static void
 connection_status(iscsi_conn_status_parameters_t *par)
 {
 	connection_t *conn;
 	session_t *session;
 
+	mutex_enter(&iscsi_cleanup_mtx);
 	if ((session = find_session(par->session_id)) == NULL) {
+		mutex_exit(&iscsi_cleanup_mtx);
 		par->status = ISCSI_STATUS_INVALID_SESSION_ID;
 		return;
 	}
@@ -1410,6 +1482,7 @@ connection_status(iscsi_conn_status_para
 	}
 	par->status = (conn == NULL) ? ISCSI_STATUS_INVALID_CONNECTION_ID :
 					ISCSI_STATUS_SUCCESS;
+	mutex_exit(&iscsi_cleanup_mtx);
 	DEB(9, ("ISCSI: connection_status, session %d connection %d --> %d\n",
 			par->session_id, par->connection_id, par->status));
 }
@@ -1423,7 +1496,7 @@ connection_status(iscsi_conn_status_para
  *          par      IN/OUT: The version parameters
  */
 
-STATIC void
+static void
 get_version(iscsi_get_version_parameters_t *par)
 {
 	par->status = ISCSI_STATUS_SUCCESS;
@@ -1447,10 +1520,14 @@ kill_all_sessions(void)
 {
 	session_t *sess;
 
+	mutex_enter(&iscsi_cleanup_mtx);
 	while ((sess = TAILQ_FIRST(&iscsi_sessions)) != NULL) {
+		mutex_exit(&iscsi_cleanup_mtx);
 		kill_session(sess, ISCSI_STATUS_DRIVER_UNLOAD, LOGOUT_SESSION,
 				FALSE);
+		mutex_enter(&iscsi_cleanup_mtx);
 	}
+	mutex_exit(&iscsi_cleanup_mtx);
 }
 
 /*
@@ -1479,60 +1556,109 @@ handle_connection_error(connection_t *co
 	}
 }
 
+/*
+ * add a connection to the cleanup list
+ */
+void
+add_connection_cleanup(connection_t *conn)
+{
+	mutex_enter(&iscsi_cleanup_mtx);
+	TAILQ_INSERT_TAIL(&iscsi_cleanupc_list, conn, connections);
+	mutex_exit(&iscsi_cleanup_mtx);
+}
+
+/*
+ * callout wrappers for timeouts, the work is done by the cleanup thread
+ */
+void
+connection_timeout_co(void *par)
+{
+	connection_t *conn = par;
+
+	mutex_enter(&iscsi_cleanup_mtx);
+	TAILQ_INSERT_TAIL(&iscsi_timeout_conn_list, conn, tchain);
+	mutex_exit(&iscsi_cleanup_mtx);
+	iscsi_notify_cleanup();
+}
+
+void
+ccb_timeout_co(void *par)
+{
+	ccb_t *ccb = par;
+
+	mutex_enter(&iscsi_cleanup_mtx);
+	TAILQ_INSERT_TAIL(&iscsi_timeout_ccb_list, ccb, tchain);
+	mutex_exit(&iscsi_cleanup_mtx);
+	iscsi_notify_cleanup();
+}
 
 /*
  * iscsi_cleanup_thread
  *    Global thread to handle connection and session cleanup after termination.
  */
 
-void
+static void
 iscsi_cleanup_thread(void *par)
 {
 	int s, rc;
 	connection_t *conn;
+	ccb_t *ccb;
 	session_t *sess, *nxt;
 	uint32_t status;
+#ifdef ISCSI_DEBUG
+	int last_usecount;
+#endif
 
-	s = splbio();
+	mutex_enter(&iscsi_cleanup_mtx);
 	while ((conn = TAILQ_FIRST(&iscsi_cleanupc_list)) != NULL ||
 		iscsi_num_send_threads ||
 		!iscsi_detaching) {
 		if (conn != NULL) {
 			TAILQ_REMOVE(&iscsi_cleanupc_list, conn, connections);
-			splx(s);
+			mutex_exit(&iscsi_cleanup_mtx);
 
 			sess = conn->session;
 			status = conn->terminating;
 
+			/*
+			 * This implies that connection cleanup only runs when
+			 * the send/recv threads have been killed
+			 */
 			DEBC(conn, 5, ("Cleanup: Waiting for threads to exit\n"));
 			while (conn->sendproc || conn->rcvproc)
-				tsleep(conn, PWAIT, "termwait", hz);
+				kpause("termwait", false, hz, NULL);
 
-			while (conn->usecount > 0)
-				tsleep(conn, PWAIT, "finalwait", hz);
+			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;
+				}
+				kpause("finalwait", false, hz, NULL);
+			}
 
 			callout_halt(&conn->timeout, NULL);
 			closef(conn->sock);
+			cv_destroy(&conn->idle_cv);
+			cv_destroy(&conn->ccb_cv);
+			cv_destroy(&conn->conn_cv);
+			mutex_destroy(&conn->lock);
 			free(conn, M_DEVBUF);
 
 			--sess->total_connections;
 
-			s = splbio();
 			TAILQ_FOREACH_SAFE(sess, &iscsi_cleanups_list, sessions, nxt) {
 				if (sess->total_connections != 0)
 					continue;
 
 				TAILQ_REMOVE(&iscsi_cleanups_list, sess, sessions);
-				splx(s);
 
 				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));
-					s = splbio();
 					TAILQ_INSERT_HEAD(&iscsi_cleanups_list, sess, sessions);
-					splx(s);
 				}
 
 				if (sess->target_list != NULL)
@@ -1540,43 +1666,97 @@ iscsi_cleanup_thread(void *par)
 				/* notify event handlers of session shutdown */
 				add_event(ISCSI_SESSION_TERMINATED, sess->id, 0, status);
 				DEB(1, ("Cleanup: session ended %d\n", sess->id));
-				free(sess, M_DEVBUF);
 
-				s = splbio();
+				cv_destroy(&sess->ccb_cv);
+				cv_destroy(&sess->sess_cv);
+				mutex_destroy(&sess->lock);
+				free(sess, M_DEVBUF);
 			}
-			splx(s);
-
 			DEB(5, ("Cleanup: Done\n"));
 
-			s = splbio();
+			mutex_enter(&iscsi_cleanup_mtx);
+
 		} else {
 			/* Go to sleep, but wake up every 30 seconds to
 			 * check for dead event handlers */
-			splx(s);
-			rc = tsleep(&iscsi_cleanupc_list, PWAIT, "cleanup",
+			rc = cv_timedwait(&iscsi_cleanup_cv, &iscsi_cleanup_mtx,
 				(TAILQ_FIRST(&event_handlers)) ? 30 * hz : 0);
-			s = splbio();
+
+			/* handle ccb timeouts */
+			while ((ccb = TAILQ_FIRST(&iscsi_timeout_ccb_list)) != NULL) {
+				TAILQ_REMOVE(&iscsi_timeout_ccb_list, ccb, tchain);
+				mutex_exit(&iscsi_cleanup_mtx);
+				ccb_timeout(ccb);
+				mutex_enter(&iscsi_cleanup_mtx);
+			}
+			/* handle connection timeouts */
+			while ((conn = TAILQ_FIRST(&iscsi_timeout_conn_list)) != NULL) {
+				TAILQ_REMOVE(&iscsi_timeout_conn_list, conn, tchain);
+				mutex_exit(&iscsi_cleanup_mtx);
+				connection_timeout(conn);
+				mutex_enter(&iscsi_cleanup_mtx);
+			}
+
 			/* if timed out, not woken up */
 			if (rc == EWOULDBLOCK)
 				check_event_handlers();
 		}
 	}
-	splx(s);
+	mutex_exit(&iscsi_cleanup_mtx);
 
 	add_event(ISCSI_DRIVER_TERMINATING, 0, 0, ISCSI_STATUS_DRIVER_UNLOAD);
 
 	/*
-     * Wait for all event handlers to deregister, but don't wait more
-     * than 1 minute (assume registering app has died if it takes longer).
+	 * Wait for all event handlers to deregister, but don't wait more
+	 * than 1 minute (assume registering app has died if it takes longer).
 	 */
+	mutex_enter(&iscsi_cleanup_mtx);
 	for (s = 0; TAILQ_FIRST(&event_handlers) != NULL && s < 60; s++)
-		tsleep(&s, PWAIT, "waiteventclr", hz);
+		kpause("waiteventclr", true, hz, &iscsi_cleanup_mtx);
+	mutex_exit(&iscsi_cleanup_mtx);
 
 	iscsi_cleanproc = NULL;
 	DEB(5, ("Cleanup thread exits\n"));
 	kthread_exit(0);
 }
 
+void
+iscsi_init_cleanup()
+{
+
+	mutex_init(&iscsi_cleanup_mtx, MUTEX_DEFAULT, IPL_BIO);
+	cv_init(&iscsi_cleanup_cv, "cleanup");
+	cv_init(&iscsi_event_cv, "iscsievtwait");
+
+	if (kthread_create(PRI_NONE, KTHREAD_MPSAFE, NULL, iscsi_cleanup_thread,
+	    NULL, &iscsi_cleanproc, "iscsi_cleanup") != 0) {
+		panic("Can't create cleanup thread!");
+	}
+}
+
+void
+iscsi_destroy_cleanup()
+{
+	
+	iscsi_detaching = true;
+	mutex_enter(&iscsi_cleanup_mtx);
+	while (iscsi_cleanproc != NULL) {
+		iscsi_notify_cleanup();
+		kpause("detach_wait", false, hz, &iscsi_cleanup_mtx);
+	}
+	mutex_exit(&iscsi_cleanup_mtx);
+
+	cv_destroy(&iscsi_event_cv);
+	cv_destroy(&iscsi_cleanup_cv);
+	mutex_destroy(&iscsi_cleanup_mtx);
+}
+
+void
+iscsi_notify_cleanup()
+{
+	cv_signal(&iscsi_cleanup_cv);
+}
+
 
 /* -------------------------------------------------------------------- */
 
@@ -1585,7 +1765,7 @@ iscsi_cleanup_thread(void *par)
  *    Driver ioctl entry.
  *
  *    Parameter:
- *       dev      The device (ignored)
+ *       file     File structure
  *       cmd      The ioctl Command
  *       addr     IN/OUT: The command parameter
  *       flag     Flags (ignored)
@@ -1596,6 +1776,7 @@ int
 iscsiioctl(struct file *fp, u_long cmd, void *addr)
 {
 	struct lwp *l = curlwp;
+	struct iscsifd *d = fp->f_iscsi;
 
 	DEB(1, ("ISCSI Ioctl cmd = %x\n", (int) cmd));
 
@@ -1605,7 +1786,7 @@ iscsiioctl(struct file *fp, u_long cmd, 
 		break;
 
 	case ISCSI_LOGIN:
-		login((iscsi_login_parameters_t *) addr, l);
+		login((iscsi_login_parameters_t *) addr, l, d->dev);
 		break;
 
 	case ISCSI_ADD_CONNECTION:

Index: src/sys/dev/iscsi/iscsi_main.c
diff -u src/sys/dev/iscsi/iscsi_main.c:1.17 src/sys/dev/iscsi/iscsi_main.c:1.18
--- src/sys/dev/iscsi/iscsi_main.c:1.17	Thu Aug 20 14:40:18 2015
+++ src/sys/dev/iscsi/iscsi_main.c	Sun May 29 13:51:16 2016
@@ -36,6 +36,7 @@
 #include <sys/filedesc.h>
 #include <sys/kmem.h>
 #include <sys/socketvar.h>
+#include <sys/sysctl.h>
 
 #include "ioconf.h"
 
@@ -47,19 +48,11 @@ extern struct cfdriver iscsi_cd;
 int iscsi_debug_level = ISCSI_DEBUG;
 #endif
 
-/* Device Structure */
-iscsi_softc_t *sc = NULL;
+bool iscsi_detaching;
 
 /* the list of sessions */
 session_list_t iscsi_sessions = TAILQ_HEAD_INITIALIZER(iscsi_sessions);
 
-/* connections to clean up */
-connection_list_t iscsi_cleanupc_list = TAILQ_HEAD_INITIALIZER(iscsi_cleanupc_list);
-session_list_t iscsi_cleanups_list = TAILQ_HEAD_INITIALIZER(iscsi_cleanups_list);
-
-bool iscsi_detaching = FALSE;
-struct lwp *iscsi_cleanproc = NULL;
-
 /* the number of active send threads (for cleanup thread) */
 uint32_t iscsi_num_send_threads = 0;
 
@@ -78,6 +71,11 @@ static void iscsi_attach(device_t parent
 static int iscsi_match(device_t, cfdata_t, void *);
 static int iscsi_detach(device_t, int);
 
+struct iscsi_softc {
+	device_t		dev;
+	kmutex_t		lock;
+	TAILQ_HEAD(, iscsifd)	fds;
+};
 
 CFATTACH_DECL_NEW(iscsi, sizeof(struct iscsi_softc), iscsi_match, iscsi_attach,
 			  iscsi_detach, NULL);
@@ -126,15 +124,35 @@ int
 iscsiopen(dev_t dev, int flag, int mode, struct lwp *l)
 {
 	struct iscsifd *d;
+	struct iscsi_softc *sc;
 	struct file *fp;
-	int error, fd;
+	int error, fd, unit;
+
+	unit = minor(dev);
+
+	DEB(99, ("ISCSI Open unit=%d\n",unit));
 
-	DEB(99, ("ISCSI Open\n"));
+	sc = device_lookup_private(&iscsi_cd, unit);
+	if (sc == NULL)
+		return ENXIO;
 
 	if ((error = fd_allocfile(&fp, &fd)) != 0)
 		return error;
 
 	d = kmem_alloc(sizeof(*d), KM_SLEEP);
+	d->dev = sc->dev;
+	d->unit = unit;
+
+	mutex_enter(&sc->lock);
+	if (iscsi_detaching) {
+		mutex_exit(&sc->lock);
+		kmem_free(d, sizeof(*d));
+		DEB(99, ("ISCSI Open aborting\n"));
+		fd_abort(curproc, fp, fd);
+		return ENXIO;
+	}
+	TAILQ_INSERT_TAIL(&sc->fds, d, link);
+	mutex_exit(&sc->lock);
 
 	return fd_clone(fp, fd, flag, &iscsi_fileops, d);
 }
@@ -143,6 +161,17 @@ static int
 iscsiclose(struct file *fp)
 {
 	struct iscsifd *d = fp->f_iscsi;
+	struct iscsi_softc *sc;
+
+	sc = device_lookup_private(&iscsi_cd, d->unit);
+	if (sc == NULL) {
+		DEBOUT(("%s: Cannot find private data\n",__func__));
+		return ENXIO;
+	}
+
+	mutex_enter(&sc->lock);
+	TAILQ_REMOVE(&sc->fds, d, link);
+	mutex_exit(&sc->lock);
 
 	kmem_free(d, sizeof(*d));
 	fp->f_iscsi = NULL;
@@ -208,15 +237,19 @@ iscsiattach(int n)
 static void
 iscsi_attach(device_t parent, device_t self, void *aux)
 {
+	struct iscsi_softc *sc;
 
-	DEBOUT(("ISCSI: iscsi_attach, parent=%p, self=%p, aux=%p\n", parent,
+	DEB(1, ("ISCSI: iscsi_attach, parent=%p, self=%p, aux=%p\n", parent,
 			self, aux));
-	sc = (iscsi_softc_t *) device_private(self);
-	sc->sc_dev = self;
-	if (kthread_create(PRI_NONE, 0, NULL, iscsi_cleanup_thread,
-	    NULL, &iscsi_cleanproc, "Cleanup") != 0) {
-		panic("Can't create cleanup thread!");
-	}
+	sc = (struct iscsi_softc *) device_private(self);
+	sc->dev = self;
+
+	TAILQ_INIT(&sc->fds);
+	mutex_init(&sc->lock, MUTEX_DEFAULT, IPL_NONE);
+
+	iscsi_detaching = false;
+	iscsi_init_cleanup();
+
 	aprint_normal("%s: attached.  major = %d\n", iscsi_cd.cd_name,
 	    cdevsw_lookup_major(&iscsi_cdevsw));
 }
@@ -228,14 +261,24 @@ iscsi_attach(device_t parent, device_t s
 static int
 iscsi_detach(device_t self, int flags)
 {
+	struct iscsi_softc *sc;
 
-	DEBOUT(("ISCSI: detach\n"));
-	kill_all_sessions();
-	iscsi_detaching = TRUE;
-	while (iscsi_cleanproc != NULL) {
-		wakeup(&iscsi_cleanupc_list);
-		tsleep(&iscsi_cleanupc_list, PWAIT, "detach_wait", 20 * hz);
+	DEB(1, ("ISCSI: detach\n"));
+	sc = (struct iscsi_softc *) device_private(self);
+
+	mutex_enter(&sc->lock);
+	if (!TAILQ_EMPTY(&sc->fds)) {
+		mutex_exit(&sc->lock);
+		return EBUSY;
 	}
+	iscsi_detaching = true;
+	mutex_exit(&sc->lock);
+
+	kill_all_sessions();
+	iscsi_destroy_cleanup();
+
+	mutex_destroy(&sc->lock);
+
 	return 0;
 }
 
@@ -299,22 +342,16 @@ getquirks(const char *iqn)
  */
 
 int
-map_session(session_t *session)
+map_session(session_t *session, device_t dev)
 {
 	struct scsipi_adapter *adapt = &session->sc_adapter;
 	struct scsipi_channel *chan = &session->sc_channel;
 	const quirktab_t	*tgt;
 
-	if (sc == NULL) { 
-		/* we haven't gone through the config process */
-		/* (shouldn't happen) */
-		DEBOUT(("Map: No device pointer!\n"));
-		return 0;
-	}
 	/*
 	 * Fill in the scsipi_adapter.
 	 */
-	adapt->adapt_dev = sc->sc_dev;
+	adapt->adapt_dev = dev;
 	adapt->adapt_nchannels = 1;
 	adapt->adapt_request = iscsi_scsipi_request;
 	adapt->adapt_minphys = iscsi_minphys;
@@ -337,7 +374,7 @@ map_session(session_t *session)
 	chan->chan_nluns = 16;		/* ToDo: ??? */
 	chan->chan_id = session->id;
 
-	session->child_dev = config_found(sc->sc_dev, chan, scsiprint);
+	session->child_dev = config_found(dev, chan, scsiprint);
 
 	return session->child_dev != NULL;
 }
@@ -405,10 +442,10 @@ iscsi_scsipi_request(struct scsipi_chann
 		}
 		/*
 		 * NOTE: It appears that XS_CTL_DATA_UIO is not actually used anywhere.
-         *       Since it really would complicate matters to handle offsets
-         *       into scatter-gather lists, and a number of other drivers don't
-         *       handle uio-based data as well, XS_CTL_DATA_UIO isn't
-         *       implemented in this driver (at least for now).
+		 * Since it really would complicate matters to handle offsets
+		 * into scatter-gather lists, and a number of other drivers don't
+		 * handle uio-based data as well, XS_CTL_DATA_UIO isn't
+		 * implemented in this driver (at least for now).
 		 */
 		if (flags & XS_CTL_DATA_UIO) {
 			xs->error = XS_DRIVER_STUFFUP;
@@ -422,7 +459,7 @@ iscsi_scsipi_request(struct scsipi_chann
 		return;
 
 	case ADAPTER_REQ_GROW_RESOURCES:
-		DEBOUT(("ISCSI: scsipi_request GROW_RESOURCES\n"));
+		DEB(5, ("ISCSI: scsipi_request GROW_RESOURCES\n"));
 		return;
 
 	case ADAPTER_REQ_SET_XFER_MODE:
@@ -468,35 +505,36 @@ void
 iscsi_done(ccb_t *ccb)
 {
 	struct scsipi_xfer *xs = ccb->xs;
-	/*DEBOUT (("iscsi_done\n")); */
+	DEB(9, ("iscsi_done\n"));
 
 	if (xs != NULL) {
 		xs->resid = ccb->residual;
 
 		switch (ccb->status) {
 		case ISCSI_STATUS_SUCCESS:
-			xs->error = 0;
+			xs->error = XS_NOERROR;
+			xs->status = SCSI_OK;
 			break;
 
 		case ISCSI_STATUS_CHECK_CONDITION:
 			xs->error = XS_SENSE;
-#ifdef ISCSI_DEBUG
-			{
-				uint8_t *s = (uint8_t *) (&xs->sense);
-				DEB(5, ("Scsipi_done, error=XS_SENSE, sense data=%02x "
-						"%02x %02x %02x...\n",
-						s[0], s[1], s[2], s[3]));
-			}
-#endif
+			xs->status = SCSI_CHECK;
 			break;
 
 		case ISCSI_STATUS_TARGET_BUSY:
 			xs->error = XS_BUSY;
+			xs->status = SCSI_BUSY;
 			break;
 
 		case ISCSI_STATUS_SOCKET_ERROR:
 		case ISCSI_STATUS_TIMEOUT:
 			xs->error = XS_SELTIMEOUT;
+			xs->status = SCSI_BUSY;
+			break;
+
+		case ISCSI_STATUS_NO_RESOURCES:
+			xs->error = XS_BUSY;
+			xs->status = SCSI_QUEUE_FULL;
 			break;
 
 		default:
@@ -505,11 +543,37 @@ iscsi_done(ccb_t *ccb)
 		}
 
 		DEB(99, ("Calling scsipi_done (%p), err = %d\n", xs, xs->error));
+		KERNEL_LOCK(1, curlwp);
 		scsipi_done(xs);
+		KERNEL_UNLOCK_ONE(curlwp);
 		DEB(99, ("scsipi_done returned\n"));
+	} else {
+		DEBOUT(("ISCSI: iscsi_done CCB %p without XS\n", ccb));
 	}
 }
 
+SYSCTL_SETUP(sysctl_iscsi_setup, "ISCSI subtree setup")
+{
+	const struct sysctlnode *node = NULL;
+
+	sysctl_createv(clog, 0, NULL, &node,
+		CTLFLAG_PERMANENT,
+		CTLTYPE_NODE, "iscsi",
+		SYSCTL_DESCR("iscsi controls"),
+		NULL, 0, NULL, 0,
+		CTL_HW, CTL_CREATE, CTL_EOL);
+
+#ifdef ISCSI_DEBUG
+	sysctl_createv(clog, 0, &node, NULL,
+		CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
+		CTLTYPE_INT, "debug",
+		SYSCTL_DESCR("debug level"),
+		NULL, 0,  &iscsi_debug_level, sizeof(iscsi_debug_level),
+		CTL_CREATE, CTL_EOL);
+#endif
+}
+
+
 /* Kernel Module support */
 
 #include <sys/module.h>
@@ -545,6 +609,7 @@ iscsi_modcmd(modcmd_t cmd, void *arg)
 #ifdef _MODULE
 	devmajor_t cmajor = NODEVMAJOR, bmajor = NODEVMAJOR;
 	int error;
+	static struct sysctllog *clog;
 #endif
 
 	switch (cmd) {
@@ -590,12 +655,16 @@ iscsi_modcmd(modcmd_t cmd, void *arg)
 			config_cfdriver_detach(&iscsi_cd);
 			return ENXIO;
 		}
+
+		sysctl_iscsi_setup(&clog);
 #endif
 		return 0;
 		break;
 
 	case MODULE_CMD_FINI:
 #ifdef _MODULE
+		sysctl_teardown(&clog);
+
 		error = config_cfdata_detach(iscsi_cfdata);
 		if (error)
 			return error;

Index: src/sys/dev/iscsi/iscsi_rcv.c
diff -u src/sys/dev/iscsi/iscsi_rcv.c:1.10 src/sys/dev/iscsi/iscsi_rcv.c:1.11
--- src/sys/dev/iscsi/iscsi_rcv.c:1.10	Sat May 30 18:09:31 2015
+++ src/sys/dev/iscsi/iscsi_rcv.c	Sun May 29 13:51:16 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: iscsi_rcv.c,v 1.10 2015/05/30 18:09:31 joerg Exp $	*/
+/*	$NetBSD: iscsi_rcv.c,v 1.11 2016/05/29 13:51:16 mlelstv Exp $	*/
 
 /*-
  * Copyright (c) 2004,2005,2006,2011 The NetBSD Foundation, Inc.
@@ -59,7 +59,9 @@ my_soo_read(connection_t *conn, struct u
 
 	DEBC(conn, 99, ("soo_read req: %zu\n", resid));
 
+	KERNEL_LOCK(1, curlwp);
 	ret = soreceive(so, NULL, u, NULL, NULL, &flags);
+	KERNEL_UNLOCK_ONE(curlwp);
 
 	if (ret || (flags != MSG_DONTWAIT && u->uio_resid)) {
 		DEBC(conn, 1, ("Read failed (ret: %d, req: %zu, out: %zu)\n", ret, resid,
@@ -159,7 +161,7 @@ read_pdu_data(pdu_t *pdu, uint8_t *data,
 	int i, pad;
 	connection_t *conn = pdu->connection;
 
-	DEBOUT(("read_pdu_data: data segment length = %d\n",
+	DEB(1, ("read_pdu_data: data segment length = %d\n",
 		ntoh3(pdu->pdu.DataSegmentLength)));
 	if (!(len = ntoh3(pdu->pdu.DataSegmentLength))) {
 		return 0;
@@ -168,7 +170,8 @@ read_pdu_data(pdu_t *pdu, uint8_t *data,
 	if (pad) {
 		pad = 4 - pad;
 	}
-	assert((data != NULL) || (offset == 0));
+
+	KASSERT(data != NULL || offset == 0);
 
 	if (data == NULL) {
 		/*
@@ -542,7 +545,7 @@ receive_logout_pdu(connection_t *conn, p
 		callout_stop(&conn->timeout);
 
 		/* let send thread take over next step of cleanup */
-		wakeup(&conn->pdus_to_send);
+		cv_broadcast(&conn->conn_cv);
 	}
 
 	return !otherconn;
@@ -990,7 +993,7 @@ receive_pdu(connection_t *conn, pdu_t *p
 {
 	ccb_t *req_ccb;
 	ccb_list_t waiting;
-	int rc, s;
+	int rc;
 	uint32_t MaxCmdSN, digest;
 	session_t *sess = conn->session;
 
@@ -1101,20 +1104,22 @@ receive_pdu(connection_t *conn, pdu_t *p
 	 * Un-throttle - wakeup all CCBs waiting for MaxCmdSN to increase.
 	 * We have to handle wait/nowait CCBs a bit differently.
 	 */
+	mutex_enter(&sess->lock);
 	if (MaxCmdSN != sess->MaxCmdSN) {
 		sess->MaxCmdSN = MaxCmdSN;
-		if (TAILQ_FIRST(&sess->ccbs_throttled) == NULL)
+		if (TAILQ_FIRST(&sess->ccbs_throttled) == NULL) {
+			mutex_exit(&sess->lock);
 			return 0;
+		}
 
 		DEBC(conn, 1, ("Unthrottling - MaxCmdSN = %d\n", MaxCmdSN));
 
-		s = splbio();
 		TAILQ_INIT(&waiting);
 		while ((req_ccb = TAILQ_FIRST(&sess->ccbs_throttled)) != NULL) {
 			throttle_ccb(req_ccb, FALSE);
 			TAILQ_INSERT_TAIL(&waiting, req_ccb, chain);
 		}
-		splx(s);
+		mutex_exit(&sess->lock);
 
 		while ((req_ccb = TAILQ_FIRST(&waiting)) != NULL) {
 			TAILQ_REMOVE(&waiting, req_ccb, chain);
@@ -1122,12 +1127,13 @@ receive_pdu(connection_t *conn, pdu_t *p
 			DEBC(conn, 1, ("Unthrottling - ccb = %p, disp = %d\n",
 					req_ccb, req_ccb->disp));
 
-			if (req_ccb->flags & CCBF_WAITING)
-				wakeup(req_ccb);
-			else
+			if (req_ccb->flags & CCBF_WAITING) {
+				cv_broadcast(&conn->ccb_cv);
+			} else
 				send_command(req_ccb, req_ccb->disp, FALSE, FALSE);
 		}
-	}
+	} else
+		mutex_exit(&sess->lock);
 
 	return 0;
 }
@@ -1185,9 +1191,11 @@ iscsi_rcv_thread(void *par)
 				break;
 			}
 		}
+		mutex_enter(&conn->lock);
 		if (!conn->destroy) {
-			tsleep(conn, PRIBIO, "conn_idle", 30 * hz);
+			cv_timedwait(&conn->idle_cv, &conn->lock, CONNECTION_IDLE_TIMEOUT);
 		}
+		mutex_exit(&conn->lock);
 	} while (!conn->destroy);
 
 	conn->rcvproc = NULL;

Index: src/sys/dev/iscsi/iscsi_send.c
diff -u src/sys/dev/iscsi/iscsi_send.c:1.15 src/sys/dev/iscsi/iscsi_send.c:1.16
--- src/sys/dev/iscsi/iscsi_send.c:1.15	Thu Dec 24 03:41:03 2015
+++ src/sys/dev/iscsi/iscsi_send.c	Sun May 29 13:51:16 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: iscsi_send.c,v 1.15 2015/12/24 03:41:03 knakahara Exp $	*/
+/*	$NetBSD: iscsi_send.c,v 1.16 2016/05/29 13:51:16 mlelstv Exp $	*/
 
 /*-
  * Copyright (c) 2004,2005,2006,2011 The NetBSD Foundation, Inc.
@@ -34,6 +34,7 @@
 #include <sys/filedesc.h>
 #include <sys/socket.h>
 #include <sys/socketvar.h>
+#include <sys/atomic.h>
 
 /*#define LUN_1  1 */
 
@@ -57,11 +58,13 @@ my_soo_write(connection_t *conn, struct 
 	int ret;
 #ifdef ISCSI_DEBUG
 	size_t resid = u->uio_resid;
-
-	assert(resid != 0);
 #endif
 
+	KASSERT(u->uio_resid != 0);
+
+	KERNEL_LOCK(1, curlwp);
 	ret = sosend(so, NULL, u, NULL, NULL, 0, conn->threadobj);
+	KERNEL_UNLOCK_ONE(curlwp);
 
 	DEB(99, ("soo_write done: len = %zu\n", u->uio_resid));
 
@@ -90,8 +93,10 @@ assign_connection(session_t *session, bo
 {
 	connection_t *conn, *next;
 
+	mutex_enter(&session->lock);
 	do {
 		if ((conn = session->mru_connection) == NULL) {
+			mutex_exit(&session->lock);
 			return NULL;
 		}
 		next = conn;
@@ -105,15 +110,17 @@ assign_connection(session_t *session, bo
 
 		if (next->state != ST_FULL_FEATURE) {
 			if (waitok) {
-				tsleep(session, PRIBIO, "iscsi_assign_connection", 0);
+				cv_wait(&session->sess_cv, &session->lock);
 				next = TAILQ_FIRST(&session->conn_list);
 			} else {
+				mutex_exit(&session->lock);
 				return NULL;
 			}
 		} else {
 			session->mru_connection = next;
 		}
 	} while (next != NULL && next->state != ST_FULL_FEATURE);
+	mutex_exit(&session->lock);
 
 	return next;
 }
@@ -138,7 +145,6 @@ reassign_tasks(connection_t *oldconn)
 	pdu_t *opdu;
 	int no_tm = 1;
 	int rc = 1;
-	int s;
 
 	if ((conn = assign_connection(sess, FALSE)) == NULL) {
 		DEB(1, ("Reassign_tasks of Session %d, connection %d failed, "
@@ -163,7 +169,7 @@ reassign_tasks(connection_t *oldconn)
 		if (!no_tm && oldconn->Time2Wait) {
 			DEBC(conn, 1, ("Time2Wait=%d, hz=%d, waiting...\n",
 						   oldconn->Time2Wait, hz));
-			tsleep(&no_tm, PRIBIO, "Time2Wait", oldconn->Time2Wait * hz);
+			kpause("Time2Wait", false, oldconn->Time2Wait * hz, NULL);
 		}
 	}
 
@@ -214,7 +220,7 @@ reassign_tasks(connection_t *oldconn)
 
 		/* fixup reference counts */
 		oldconn->usecount--;
-		conn->usecount++;
+		atomic_inc_uint(&conn->usecount);
 
 		DEBC(conn, 1, ("CCB %p: Copied PDU %p to %p\n",
 					   ccb, opdu, pdu));
@@ -226,9 +232,7 @@ reassign_tasks(connection_t *oldconn)
 		free_pdu(opdu);
 
 		/* put ready CCB into waiting list of new connection */
-		s = splbio();
 		suspend_ccb(ccb, TRUE);
-		splx(s);
 	}
 
 	if (pdu == NULL) {
@@ -248,6 +252,7 @@ reassign_tasks(connection_t *oldconn)
 		}
 		/* if we get an error on reassign, restart the original request */
 		if (no_tm || rc) {
+			mutex_enter(&sess->lock);
 			if (ccb->CmdSN < sess->ExpCmdSN) {
 				pdu = ccb->pdu_waiting;
 
@@ -260,6 +265,7 @@ reassign_tasks(connection_t *oldconn)
 				}
 				pdu->pdu.p.command.CmdSN = htonl(ccb->CmdSN);
 			}
+			mutex_exit(&sess->lock);
 			resend_pdu(ccb);
 		} else {
 			callout_schedule(&ccb->timeout, COMMAND_TIMEOUT);
@@ -287,29 +293,21 @@ iscsi_send_thread(void *par)
 	ccb_t *ccb, *nccb;
 	pdu_t *pdu;
 	struct file *fp;
-	int s;
 
 	sess = conn->session;
 	/* so cleanup thread knows there's someone left */
 	iscsi_num_send_threads++;
 
 	do {
+		mutex_enter(&conn->lock);
 		while (!conn->terminating) {
-			s = splbio();
 			while (!conn->terminating &&
 				(pdu = TAILQ_FIRST(&conn->pdus_to_send)) != NULL) {
 				TAILQ_REMOVE(&conn->pdus_to_send, pdu, send_chain);
 				pdu->flags &= ~PDUF_INQUEUE;
-				splx(s);
+				mutex_exit(&conn->lock);
 
-#ifdef ISCSI_DEBUG
-				if (!pdu->uio.uio_resid) {
-					DEBOUT(("uio.resid = 0 in iscsi_send_thread! pdu=%p\n",
-							pdu));
-					assert(pdu->uio.uio_resid != 0);
-				}
-#endif
-				/*DEB (99,("Send thread woke up, pdu = %x)\n", (int)pdu)); */
+				KASSERT(!pdu->uio.uio_resid);
 
 				/* update ExpStatSN here to avoid discontinuities */
 				/* and delays in updating target */
@@ -317,26 +315,20 @@ iscsi_send_thread(void *par)
 
 				if (conn->HeaderDigest)
 					pdu->pdu.HeaderDigest = gen_digest(&pdu->pdu, BHS_SIZE);
+				DEBC(conn, 99, ("Send thread transmitting data\n"));
 				my_soo_write(conn, &pdu->uio);
 
 				if (pdu->disp <= PDUDISP_FREE) {
 					free_pdu(pdu);
 				} else {
-					pdu->flags &= ~PDUF_BUSY;
-				}
-				s = splbio();
+					pdu->flags &= ~PDUF_BUSY; }
+				mutex_enter(&conn->lock);
 			}
 
-			/*DEB (99,("Send thread done, waiting (conn->terminating = %d)\n", */
-			/* 		conn->terminating)); */
-
-			if (!conn->terminating) {
-				tsleep(&conn->pdus_to_send, PRIBIO,
-						"iscsisend", 0);
-			}
-
-			splx(s);
+			if (!conn->terminating)
+				cv_wait(&conn->conn_cv, &conn->lock);
 		}
+		mutex_exit(&conn->lock);
 
 		/* ------------------------------------------------------------------------
 		 *    Here this thread takes over cleanup of the terminating connection.
@@ -369,15 +361,19 @@ iscsi_send_thread(void *par)
 		}
 
 		/* clean out anything left in send queue */
+		mutex_enter(&conn->lock);
 		while ((pdu = TAILQ_FIRST(&conn->pdus_to_send)) != NULL) {
 			TAILQ_REMOVE(&conn->pdus_to_send, pdu, send_chain);
 			pdu->flags &= ~(PDUF_INQUEUE | PDUF_BUSY);
+			mutex_exit(&conn->lock);
 			/* if it's not attached to a waiting CCB, free it */
 			if (pdu->owner == NULL ||
 			    pdu->owner->pdu_waiting != pdu) {
 				free_pdu(pdu);
 			}
+			mutex_enter(&conn->lock);
 		}
+		mutex_exit(&conn->lock);
 
 		/* If there's another connection available, transfer pending tasks */
 		if (sess->active_connections &&
@@ -386,7 +382,7 @@ iscsi_send_thread(void *par)
 			reassign_tasks(conn);
 		} else if (!conn->destroy && conn->Time2Wait) {
 			DEBC(conn, 1, ("Time2Wait\n"));
-			tsleep(&s, PRIBIO, "Time2Wait", conn->Time2Wait * hz);
+			kpause("Time2Wait", false, conn->Time2Wait * hz, NULL);
 			DEBC(conn, 1, ("Time2Wait\n"));
 		}
 		/* notify event handlers of connection shutdown */
@@ -396,32 +392,31 @@ iscsi_send_thread(void *par)
 				  sess->id, conn->id, conn->terminating);
 
 		DEBC(conn, 1, ("Waiting for conn_idle\n"));
+		mutex_enter(&conn->lock);
 		if (!conn->destroy)
-			tsleep(conn, PRIBIO, "conn_idle", 30 * hz);
+			cv_timedwait(&conn->idle_cv, &conn->lock, CONNECTION_IDLE_TIMEOUT);
+		mutex_exit(&conn->lock);
 		DEBC(conn, 1, ("Waited for conn_idle, destroy = %d\n", conn->destroy));
 
 	} while (!conn->destroy);
 
 	/* wake up anyone waiting for a PDU */
-	wakeup(&conn->pdu_pool);
+	cv_broadcast(&conn->conn_cv);
 
 	/* wake up any waiting CCBs */
 	while ((ccb = TAILQ_FIRST(&conn->ccbs_waiting)) != NULL) {
+		KASSERT(ccb->disp >= CCBDISP_NOWAIT);
 		wake_ccb(ccb, conn->terminating);
 		/* NOTE: wake_ccb will remove the CCB from the queue */
 	}
 
-	s = splbio();
 	if (conn->in_session) {
 		conn->in_session = FALSE;
 		TAILQ_REMOVE(&sess->conn_list, conn, connections);
 		sess->mru_connection = TAILQ_FIRST(&sess->conn_list);
 	}
 
-	TAILQ_INSERT_TAIL(&iscsi_cleanupc_list, conn, connections);
-	splx(s);
-
-	wakeup(&iscsi_cleanupc_list);
+	add_connection_cleanup(conn);
 
 	conn->sendproc = NULL;
 	DEBC(conn, 1, ("Send thread exits\n"));
@@ -448,7 +443,6 @@ send_pdu(ccb_t *ccb, pdu_t *pdu, ccb_dis
 {
 	connection_t *conn = pdu->connection;
 	ccb_disp_t prev_cdisp = 0;
-	int s;
 
 	if (ccb != NULL) {
 		prev_cdisp = ccb->disp;
@@ -463,7 +457,7 @@ send_pdu(ccb_t *ccb, pdu_t *pdu, ccb_dis
 	DEBC(conn, 10, ("Send_pdu: ccb=%p, pcd=%d, cdsp=%d, pdu=%p, pdsp=%d\n",
 			ccb, prev_cdisp, cdisp, pdu, pdisp));
 
-	s = splbio();
+	mutex_enter(&conn->lock);
 	if (pdisp == PDUDISP_WAIT) {
 		ccb->pdu_waiting = pdu;
 
@@ -480,8 +474,9 @@ send_pdu(ccb_t *ccb, pdu_t *pdu, ccb_dis
 		TAILQ_INSERT_HEAD(&conn->pdus_to_send, pdu, send_chain);
 	else
 		TAILQ_INSERT_TAIL(&conn->pdus_to_send, pdu, send_chain);
+	mutex_exit(&conn->lock);
 
-	wakeup(&conn->pdus_to_send);
+	cv_broadcast(&conn->conn_cv);
 
 	if (cdisp != CCBDISP_NOWAIT) {
 		callout_schedule(&ccb->timeout, COMMAND_TIMEOUT);
@@ -489,10 +484,16 @@ send_pdu(ccb_t *ccb, pdu_t *pdu, ccb_dis
 		if (prev_cdisp <= CCBDISP_NOWAIT)
 			suspend_ccb(ccb, TRUE);
 
-		if (cdisp == CCBDISP_WAIT)
-			tsleep(ccb, PWAIT, "sendpdu", 0);
+		mutex_enter(&conn->lock);
+		while (ccb->disp == CCBDISP_WAIT) {
+			DEBC(conn, 15, ("Send_pdu: ccb=%p cdisp=%d waiting\n",
+				ccb, ccb->disp));
+			cv_wait(&conn->ccb_cv, &conn->lock);
+			DEBC(conn, 15, ("Send_pdu: ccb=%p cdisp=%d returned\n",
+				ccb, ccb->disp));
+		}
+		mutex_exit(&conn->lock);
 	}
-	splx(s);
 }
 
 
@@ -509,15 +510,14 @@ resend_pdu(ccb_t *ccb)
 {
 	connection_t *conn = ccb->connection;
 	pdu_t *pdu = ccb->pdu_waiting;
-	int s;
 
-	s = splbio ();
+	mutex_enter(&conn->lock);
 	if (pdu == NULL || (pdu->flags & PDUF_BUSY)) {
-		splx (s);
+		mutex_exit(&conn->lock);
 		return;
 	}
 	pdu->flags |= PDUF_BUSY;
-	splx (s);
+	mutex_exit(&conn->lock);
 
 	/* restore UIO and IOVEC */
 	pdu->uio = pdu->save_uio;
@@ -525,8 +525,7 @@ resend_pdu(ccb_t *ccb)
 
 	DEBC(conn, 8, ("ReSend_pdu ccb=%p, pdu=%p\n", ccb, pdu));
 
-	s = splbio ();
-
+	mutex_enter(&conn->lock);
 	/* Enqueue for sending */
 	pdu->flags |= PDUF_INQUEUE;
 
@@ -536,9 +535,9 @@ resend_pdu(ccb_t *ccb)
 		TAILQ_INSERT_TAIL(&conn->pdus_to_send, pdu, send_chain);
 	}
 	callout_schedule(&ccb->timeout, COMMAND_TIMEOUT);
-	splx (s);
+	mutex_exit(&conn->lock);
 
-	wakeup(&conn->pdus_to_send);
+	cv_broadcast(&conn->conn_cv);
 }
 
 
@@ -1278,7 +1277,7 @@ send_data_out(connection_t *conn, pdu_t 
 	uint32_t totlen, len, offs, sn;
 	pdu_t *tx_pdu;
 
-	assert(conn->max_transfer != 0);
+	KASSERT(conn->max_transfer != 0);
 
 	if (rx_pdu) {
 		offs = ntohl(rx_pdu->pdu.p.r2t.BufferOffset);
@@ -1294,7 +1293,7 @@ send_data_out(connection_t *conn, pdu_t 
 
 		tx_pdu = get_pdu(conn, waitok);
 		if (tx_pdu == NULL) {
-			DEBOUT(("No PDU in send_data_out\n"));
+			DEBC(conn, 5, ("No PDU in send_data_out\n"));
 
 			tx_ccb->disp = disp;
 			tx_ccb->status = ISCSI_STATUS_NO_RESOURCES;
@@ -1348,9 +1347,8 @@ send_command(ccb_t *ccb, ccb_disp_t disp
 	session_t *sess = ccb->session;
 	pdu_t *ppdu;
 	pdu_header_t *pdu;
-	int s;
 
-	s = splbio();
+	mutex_enter(&sess->lock);
 	while (/*CONSTCOND*/ISCSI_THROTTLING_ENABLED &&
 	    /*CONSTCOND*/!ISCSI_SERVER_TRUSTED &&
 	    !sn_a_le_b(sess->CmdSN, sess->MaxCmdSN)) {
@@ -1361,18 +1359,23 @@ send_command(ccb_t *ccb, ccb_disp_t disp
 		throttle_ccb(ccb, TRUE);
 
 		if (!waitok) {
-			splx(s);
+			DEBOUT(("Throttling send_command, ccb = %p\n",ccb));
 			return;
 		}
 
-		tsleep(ccb, PWAIT, "waitMaxCmd", 0);
+		cv_wait(&sess->ccb_cv, &sess->lock);
+
+		DEBOUT(("Resuming send_command, ccb = %p\n",ccb));
 
 		throttle_ccb(ccb, FALSE);
 		ccb->flags &= ~CCBF_WAITING;
 	}
-	splx(s);
+	mutex_exit(&sess->lock);
+
 	ppdu = get_pdu(conn, FALSE);
 	if (ppdu == NULL) {
+		DEBOUT(("No PDU for send_command, ccb = %p\n",ccb));
+		ccb->disp = disp;
 		wake_ccb(ccb, ISCSI_STATUS_NO_RESOURCES);
 		return;
 	}
@@ -1401,6 +1404,9 @@ send_command(ccb_t *ccb, ccb_disp_t disp
 		}
 	}
 
+	/* currently ignoring tag type and id */
+	pdu->Flags |= ATTR_SIMPLE;
+
 	if (!totlen)
 		pdu->Flags |= FLAG_FINAL;
 
@@ -1412,11 +1418,11 @@ send_command(ccb_t *ccb, ccb_disp_t disp
 	ccb->residual = 0;
 	ccb->flags |= CCBF_REASSIGN;
 
-	s = splbio();
+	mutex_enter(&sess->lock);
 	ccb->CmdSN = sess->CmdSN;
 	if (!immed)
 		sess->CmdSN++;
-	splx(s);
+	mutex_exit(&sess->lock);
 
 	pdu->p.command.CmdSN = htonl(ccb->CmdSN);
 
@@ -1473,8 +1479,7 @@ send_run_xfer(session_t *session, struct
 	ccb = get_ccb(conn, waitok);
 	if (ccb == NULL) {
 		xs->error = XS_BUSY;
-		xs->status = SCSI_QUEUE_FULL;
-		DEBC(conn, 0, ("No CCB in run_xfer\n"));
+		DEBC(conn, 5, ("No CCB in run_xfer, %d in use.\n", conn->usecount));
 		scsipi_done(xs);
 		return;
 	}
@@ -1579,13 +1584,12 @@ send_io_command(session_t *session, uint
  *    before those time out.
  *
  *    Parameter:
- *          par      The connection
+ *          conn     The connection
  */
 
 void
-connection_timeout(void *par)
+connection_timeout(connection_t *conn)
 {
-	connection_t *conn = (connection_t *) par;
 
 	if (++conn->num_timeouts > MAX_CONN_TIMEOUTS)
 		handle_connection_error(conn, ISCSI_STATUS_TIMEOUT, NO_LOGOUT);
@@ -1602,17 +1606,19 @@ connection_timeout(void *par)
  *    Handle timeout of a sent command.
  *
  *    Parameter:
- *          par      The CCB
+ *          ccb      The CCB
  */
 
 void
-ccb_timeout(void *par)
+ccb_timeout(ccb_t *ccb)
 {
-	ccb_t *ccb = (ccb_t *) par;
 	connection_t *conn = ccb->connection;
 
 	ccb->total_tries++;
 
+	DEBC(conn, 0, ("ccb_timeout: num=%d total=%d disp=%d\n",
+		ccb->num_timeouts+1, ccb->total_tries, ccb->disp));
+
 	if (++ccb->num_timeouts > MAX_CCB_TIMEOUTS ||
 		ccb->total_tries > MAX_CCB_TRIES ||
 		ccb->disp <= CCBDISP_FREE ||
@@ -1631,3 +1637,4 @@ ccb_timeout(void *par)
 		callout_schedule(&ccb->timeout, COMMAND_TIMEOUT);
 	}
 }
+

Index: src/sys/dev/iscsi/iscsi_text.c
diff -u src/sys/dev/iscsi/iscsi_text.c:1.9 src/sys/dev/iscsi/iscsi_text.c:1.10
--- src/sys/dev/iscsi/iscsi_text.c:1.9	Sat May 30 16:12:34 2015
+++ src/sys/dev/iscsi/iscsi_text.c	Sun May 29 13:51:16 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: iscsi_text.c,v 1.9 2015/05/30 16:12:34 joerg Exp $	*/
+/*	$NetBSD: iscsi_text.c,v 1.10 2016/05/29 13:51:16 mlelstv Exp $	*/
 
 /*-
  * Copyright (c) 2005,2006,2011 The NetBSD Foundation, Inc.
@@ -34,6 +34,9 @@
 #include <sys/md5.h>
 #include <sys/cprng.h>
 
+/* define to send T_BIGNUM in hex format instead of base64 */
+/* #define ISCSI_HEXBIGNUMS */
+
 #define isdigit(x) ((x) >= '0' && (x) <= '9')
 #define toupper(x) ((x) & ~0x20)
 
@@ -107,12 +110,6 @@ typedef enum
 /* maximum known key */
 #define MAX_KEY   K_TargetPortalGroupTag
 
-
-#undef DEBOUT
-#define DEBOUT(x)	printf x
-
-
-
 /* value types */
 typedef enum
 {						/* Value is... */
@@ -636,7 +633,22 @@ my_strcpy(uint8_t *dest, const uint8_t *
 STATIC unsigned
 put_bignumval(negotiation_parameter_t *par, uint8_t *buf)
 {
+#ifdef ISCSI_HEXBIGNUMS
+	int k, c;
+
+	my_strcpy(buf, "0x");
+	for (k=0; k<par->list_num; ++k) {
+		c = par->val.sval[k] >> 4;
+		buf[2+2*k] = c < 10 ? '0' + c : 'a' + (c-10);
+		c = par->val.sval[k] & 0xf;
+		buf[2+2*k+1] = c < 10 ? '0' + c : 'a' + (c-10);
+	}
+	buf[2+2*k] = '\0';
+
+	return 2+2*par->list_num;
+#else
 	return base64_encode(par->val.sval, par->list_num, buf);
+#endif
 }
 
 /*
@@ -817,7 +829,11 @@ parameter_size(negotiation_parameter_t *
 
 		case T_BIGNUM:
 			/* list_num holds value size */
+#ifdef ISCSI_HEXBIGNUMS
+			size += 2 + 2*par->list_num;
+#else
 			size += base64_enclen(par->list_num);
+#endif
 			i = par->list_num;
 			break;
 
@@ -839,11 +855,13 @@ parameter_size(negotiation_parameter_t *
 			break;
 
 		case T_RANGE:
-			assert((i + 1) < par->list_num);
-			size += snprintf(buf, sizeof(buf), "%d~%d",
-				par->val.nval[i],
-							par->val.nval[i + 1]);
-			i++;
+			if (i+1 < par->list_num) {
+				size += snprintf(buf, sizeof(buf), "%d~%d",
+					par->val.nval[i],
+					par->val.nval[i + 1]);
+				i++;
+			} else
+				DEBOUT(("Incomplete range parameter\n"));
 			break;
 
 		case T_SESS:

Index: src/sys/dev/iscsi/iscsi_utils.c
diff -u src/sys/dev/iscsi/iscsi_utils.c:1.8 src/sys/dev/iscsi/iscsi_utils.c:1.9
--- src/sys/dev/iscsi/iscsi_utils.c:1.8	Sat May 30 18:12:09 2015
+++ src/sys/dev/iscsi/iscsi_utils.c	Sun May 29 13:51:16 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: iscsi_utils.c,v 1.8 2015/05/30 18:12:09 joerg Exp $	*/
+/*	$NetBSD: iscsi_utils.c,v 1.9 2016/05/29 13:51:16 mlelstv Exp $	*/
 
 /*-
  * Copyright (c) 2004,2005,2006,2008 The NetBSD Foundation, Inc.
@@ -34,13 +34,14 @@
 #include <sys/buf.h>
 #include <sys/socketvar.h>
 #include <sys/bswap.h>
+#include <sys/atomic.h>
 
 
 #ifdef ISCSI_DEBUG
 
 /* debug helper routine */
 void
-dump(void *buff, int len)
+iscsi_hexdump(void *buff, int len)
 {
 	uint8_t *bp = (uint8_t *) buff;
 	int i;
@@ -219,23 +220,23 @@ get_ccb(connection_t *conn, bool waitok)
 {
 	ccb_t *ccb;
 	session_t *sess = conn->session;
-	int s;
 
+	mutex_enter(&sess->lock);
 	do {
-		s = splbio();
 		ccb = TAILQ_FIRST(&sess->ccb_pool);
-		if (ccb != NULL)
-			TAILQ_REMOVE(&sess->ccb_pool, ccb, chain);
-		splx(s);
-
 		DEB(100, ("get_ccb: ccb = %p, waitok = %d\n", ccb, waitok));
-		if (ccb == NULL) {
+
+		if (ccb != NULL) {
+			TAILQ_REMOVE(&sess->ccb_pool, ccb, chain);
+		} else {
 			if (!waitok || conn->terminating) {
+				mutex_exit(&sess->lock);
 				return NULL;
 			}
-			tsleep(&sess->ccb_pool, PWAIT, "get_ccb", 0);
+			cv_wait(&sess->ccb_cv, &sess->lock);
 		}
 	} while (ccb == NULL);
+	mutex_exit(&sess->lock);
 
 	ccb->flags = 0;
 	ccb->xs = NULL;
@@ -245,7 +246,11 @@ get_ccb(connection_t *conn, bool waitok)
 	ccb->ITT = (ccb->ITT & 0xffffff) | (++sess->itt_id << 24);
 	ccb->disp = CCBDISP_NOWAIT;
 	ccb->connection = conn;
-	conn->usecount++;
+	atomic_inc_uint(&conn->usecount);
+
+	DEBC(conn, 5, (
+		"get_ccb: ccb = %p, usecount = %d\n",
+		ccb, conn->usecount));
 
 	return ccb;
 }
@@ -262,14 +267,21 @@ free_ccb(ccb_t *ccb)
 {
 	session_t *sess = ccb->session;
 	pdu_t *pdu;
-	int s;
+
+	DEBC(ccb->connection, 5, (
+		"free_ccb: ccb = %p, usecount = %d\n",
+		ccb, ccb->connection->usecount-1));
 
 	KASSERT((ccb->flags & CCBF_THROTTLING) == 0);
 	KASSERT((ccb->flags & CCBF_WAITQUEUE) == 0);
 
-	ccb->connection->usecount--;
+	atomic_dec_uint(&ccb->connection->usecount);
 	ccb->connection = NULL;
 
+	if (ccb->disp > CCBDISP_NOWAIT) {
+		DEBOUT(("Freeing CCB with disp %d\n",ccb->disp));
+	}
+
 	ccb->disp = CCBDISP_UNUSED;
 
 	/* free temporary data */
@@ -285,11 +297,11 @@ free_ccb(ccb_t *ccb)
 		free_pdu(pdu);
 	}
 
-	s = splbio();
+	mutex_enter(&sess->lock);
 	TAILQ_INSERT_TAIL(&sess->ccb_pool, ccb, chain);
-	splx(s);
+	mutex_exit(&sess->lock);
 
-	wakeup(&sess->ccb_pool);
+	cv_broadcast(&sess->ccb_cv);
 }
 
 /*
@@ -314,10 +326,10 @@ create_ccbs(session_t *sess)
 		ccb->ITT = i | sid;
 		ccb->session = sess;
 
-		callout_init(&ccb->timeout, 0);
-		callout_setfunc(&ccb->timeout, ccb_timeout, ccb);
+		callout_init(&ccb->timeout, CALLOUT_MPSAFE);
+		callout_setfunc(&ccb->timeout, ccb_timeout_co, ccb);
 
-		/*DEB (9, ("Create_ccbs: ccb %x itt %x\n", ccb, ccb->ITT)); */
+		DEB(9, ("Create_ccbs: ccb %p itt %x\n", ccb, ccb->ITT));
 		TAILQ_INSERT_HEAD(&sess->ccb_pool, ccb, chain);
 	}
 }
@@ -353,6 +365,8 @@ throttle_ccb(ccb_t *ccb, bool yes)
 {
 	session_t *sess;
 
+	KASSERT(mutex_owned(&sess->lock));
+
 	sess = ccb->session;
 	if (yes) {
 		KASSERT((ccb->flags & CCBF_THROTTLING) == 0);
@@ -381,7 +395,6 @@ wake_ccb(ccb_t *ccb, uint32_t status)
 {
 	ccb_disp_t disp;
 	connection_t *conn;
-	int s;
 
 	conn = ccb->connection;
 
@@ -392,11 +405,11 @@ wake_ccb(ccb_t *ccb, uint32_t status)
 
 	callout_stop(&ccb->timeout);
 
-	s = splbio();
+	mutex_enter(&conn->lock);
 	disp = ccb->disp;
 	if (disp <= CCBDISP_NOWAIT ||
 		(disp == CCBDISP_DEFER && conn->state <= ST_WINDING_DOWN)) {
-		splx(s);
+		mutex_exit(&conn->lock);
 		return;
 	}
 
@@ -406,7 +419,7 @@ wake_ccb(ccb_t *ccb, uint32_t status)
 	/* change the disposition so nobody tries this again */
 	ccb->disp = CCBDISP_BUSY;
 	ccb->status = status;
-	splx(s);
+	mutex_exit(&conn->lock);
 
 	switch (disp) {
 	case CCBDISP_FREE:
@@ -414,7 +427,7 @@ wake_ccb(ccb_t *ccb, uint32_t status)
 		break;
 
 	case CCBDISP_WAIT:
-		wakeup(ccb);
+		cv_broadcast(&conn->ccb_cv);
 		break;
 
 	case CCBDISP_SCSIPI:
@@ -451,22 +464,24 @@ pdu_t *
 get_pdu(connection_t *conn, bool waitok)
 {
 	pdu_t *pdu;
-	int s;
 
+	mutex_enter(&conn->lock);
 	do {
-		s = splbio();
 		pdu = TAILQ_FIRST(&conn->pdu_pool);
 		if (pdu != NULL)
 			TAILQ_REMOVE(&conn->pdu_pool, pdu, chain);
-		splx(s);
 
 		DEB(100, ("get_pdu_c: pdu = %p, waitok = %d\n", pdu, waitok));
+
 		if (pdu == NULL) {
-			if (!waitok || conn->terminating)
+			if (!waitok || conn->terminating) {
+				mutex_exit(&conn->lock);
 				return NULL;
-			tsleep(&conn->pdu_pool, PWAIT, "get_pdu_c", 0);
+			}
+			cv_wait(&conn->conn_cv, &conn->lock);
 		}
 	} while (pdu == NULL);
+	mutex_exit(&conn->lock);
 
 	memset(pdu, 0, sizeof(pdu_t));
 	pdu->connection = conn;
@@ -487,29 +502,27 @@ free_pdu(pdu_t *pdu)
 {
 	connection_t *conn = pdu->connection;
 	pdu_disp_t pdisp;
-	int s;
 
 	if (PDUDISP_UNUSED == (pdisp = pdu->disp))
 		return;
 	pdu->disp = PDUDISP_UNUSED;
 
+	mutex_enter(&conn->lock);
 	if (pdu->flags & PDUF_INQUEUE) {
 		TAILQ_REMOVE(&conn->pdus_to_send, pdu, send_chain);
 		pdu->flags &= ~PDUF_INQUEUE;
 	}
-
-	if (pdisp == PDUDISP_SIGNAL)
-		wakeup(pdu);
+	mutex_exit(&conn->lock);
 
 	/* free temporary data in this PDU */
 	if (pdu->temp_data)
 		free(pdu->temp_data, M_TEMP);
 
-	s = splbio();
+	mutex_enter(&conn->lock);
 	TAILQ_INSERT_TAIL(&conn->pdu_pool, pdu, chain);
-	splx(s);
+	mutex_exit(&conn->lock);
 
-	wakeup(&conn->pdu_pool);
+	cv_broadcast(&conn->conn_cv);
 }
 
 /*

Reply via email to