diff --git a/Makefile b/Makefile
index b2ba0c3..16ef1df 100644
--- a/Makefile
+++ b/Makefile
@@ -58,6 +58,9 @@ clean:
 install: install_kernel install_programs install_doc install_etc \
 	install_initd install_iname install_iface
 
+install_usr: install_programs install_doc install_etc \
+	install_initd install_iname install_iface
+
 install_programs:  $(PROGRAMS)
 	$(INSTALL) -d $(DESTDIR)$(sbindir)
 	$(INSTALL) -m 755 $^ $(DESTDIR)$(sbindir)
@@ -113,4 +116,9 @@ install_iname:
 		echo "***************************************************" ; \
 	fi
 
+depend:
+	for dir in usr utils utils/fwparam_ibft; do \
+		$(MAKE) -C $$dir $@; \
+	done
+
 # vim: ft=make tw=72 sw=4 ts=4:
diff --git a/README b/README
index 59878ce..87b3ee1 100644
--- a/README
+++ b/README
@@ -439,12 +439,14 @@ iscsiadm -m node -p ip:port -I iface0 --op=delete
 
 	To specify a IPv6 address the following can be used:
 
-	    ./iscsiadm -m node -T -p 2001:c90::211:9ff:feb8:a9e9 -l
+	    ./iscsiadm -m node -T iqn.2005-03.com.max \
+					-p 2001:c90::211:9ff:feb8:a9e9 -l
 
 	The above command would use the default port, 3260. To specify a
 	port use the following:
 
-	    ./iscsiadm -m node -T -p [2001:c90::211:9ff:feb8:a9e9]:3260 -l
+	    ./iscsiadm -m node -T iqn.2005-03.com.max \
+				-p [2001:c90::211:9ff:feb8:a9e9]:3260 -l
 
     - iSCSI Login to a specific portal through the NIC setup as iface0:
 
@@ -714,6 +716,9 @@ running:
 
 iscsiadm -m discovery -t isns
 
+*** Warning *** iSNS support is experimental in this release. The command
+line options for it will change in future releases.
+
 Both commands will print out the list of all discovered targets and their
 portals:
 
diff --git a/etc/initd/initd.redhat b/etc/initd/initd.redhat
index c591534..c269eeb 100644
--- a/etc/initd/initd.redhat
+++ b/etc/initd/initd.redhat
@@ -73,7 +73,7 @@ case "$1" in
 			RETVAL=$?
 			;;
 	condrestart)
-			[ -f /var/lock/subsys/iscsi ] && restart
+			[ -f /var/lock/subsys/open-iscsi ] && restart
 			;;
 	*)
 			echo $"Usage: $0 {start|stop|restart|status|condrestart}"
diff --git a/kernel/2.6.13-compat.patch b/kernel/2.6.13-compat.patch
index e319924..17baa9c 100644
--- a/kernel/2.6.13-compat.patch
+++ b/kernel/2.6.13-compat.patch
@@ -42,7 +42,7 @@ diff -Naurp open-iscsi.diffbase/kernel/iscsi_compat.h open-iscsi.test/kernel/isc
 +
 +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,13)
 +
-+#define netlink_kernel_create(uint, groups, input, mod) \
++#define netlink_kernel_create(uint, groups, input, cb_mutex, mod) \
 +	netlink_kernel_create(uint, input)
 +
 +#define gfp_t unsigned
diff --git a/kernel/2.6.14-and-2.6.15-compat.patch b/kernel/2.6.14-and-2.6.15-compat.patch
index e319924..17baa9c 100644
--- a/kernel/2.6.14-and-2.6.15-compat.patch
+++ b/kernel/2.6.14-and-2.6.15-compat.patch
@@ -42,7 +42,7 @@ diff -Naurp open-iscsi.diffbase/kernel/iscsi_compat.h open-iscsi.test/kernel/isc
 +
 +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,13)
 +
-+#define netlink_kernel_create(uint, groups, input, mod) \
++#define netlink_kernel_create(uint, groups, input, cb_mutex, mod) \
 +	netlink_kernel_create(uint, input)
 +
 +#define gfp_t unsigned
diff --git a/kernel/2.6.20-21_compat.patch b/kernel/2.6.20-21_compat.patch
new file mode 100644
index 0000000..28113c3
--- /dev/null
+++ b/kernel/2.6.20-21_compat.patch
@@ -0,0 +1,41 @@
+---
+ kernel/iscsi_2.6.22_compat.h  |    6 ++++++
+ kernel/iscsi_tcp.h            |    1 +
+ kernel/scsi_transport_iscsi.c |    1 +
+ 3 files changed, 8 insertions(+)
+
+Index: kernel/iscsi_2.6.22_compat.h
+===================================================================
+--- /dev/null
++++ kernel/iscsi_2.6.22_compat.h
+@@ -0,0 +1,6 @@
++#include <linux/version.h>
++
++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,21)
++#define netlink_kernel_create(uint, groups, input, cb_mutex, mod) \
++	netlink_kernel_create(uint, groups, input, mod)
++#endif
+Index: kernel/iscsi_tcp.h
+===================================================================
+--- kernel/iscsi_tcp.h
++++ kernel/iscsi_tcp.h
+@@ -52,6 +52,7 @@
+ #define ISCSI_SG_TABLESIZE		SG_ALL
+ #define ISCSI_TCP_MAX_CMD_LEN		16
+ 
++#include "iscsi_2.6.22_compat.h"
+ struct crypto_hash;
+ struct socket;
+ 
+Index: kernel/scsi_transport_iscsi.c
+===================================================================
+--- kernel/scsi_transport_iscsi.c
++++ kernel/scsi_transport_iscsi.c
+@@ -29,6 +29,7 @@
+ #include <scsi/scsi_transport.h>
+ #include "scsi_transport_iscsi.h"
+ #include "iscsi_if.h"
++#include "iscsi_2.6.22_compat.h"
+ 
+ #define ISCSI_SESSION_ATTRS 15
+ #define ISCSI_CONN_ATTRS 11
diff --git a/kernel/Makefile b/kernel/Makefile
index 199241b..80a4fdb 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -52,7 +52,8 @@ all: kernel_check
 14to15_patch=2.6.14-and-2.6.15-compat.patch
 16to18_patch=2.6.16-18_compat.patch
 19_patch=2.6.19_compat.patch
-all_patches=13_patch 14to15_patch 16to18_patch 19_patch
+20to21_patch=2.6.20-21_compat.patch
+all_patches=13_patch 14to15_patch 16to18_patch 19_patch 20to21_patch
 cur_patched=cur_patched
 
 ## fun stuff for maintaining multiple versions
@@ -81,9 +82,11 @@ linux_2_6_18: has_16to18_patch
 
 linux_2_6_19: has_19_patch
 
-linux_2_6_20: $(unpatch_code)
+linux_2_6_20: has_20to21_patch
 
-linux_2_6_21: $(unpatch_code)
+linux_2_6_21: has_20to21_patch
+
+linux_2_6_22: $(unpatch_code)
 
 do_unpatch_code:
 	echo "Un-patching source code for use with linux-2.6.20 and up ..."
@@ -129,6 +132,15 @@ has_19_patch: $(19_patch)
 	cp $(19_patch) $@
 	ln -s $@ $(cur_patched)
 
+has_20to21_patch: $(20to21_patch)
+	echo "Patching source code for linux-2.6.20-21 ..."
+	if [ -e $(cur_patched) ]; then \
+		make -C . clean; \
+	fi
+	patch -p1 < $(20to21_patch)
+	cp $(20to21_patch) $@
+	ln -s $@ $(cur_patched)
+
 # ============ END code for kernel_check and source patching =================
 
 clean: $(unpatch_code)
diff --git a/kernel/scsi_transport_iscsi.c b/kernel/scsi_transport_iscsi.c
index 29730a1..5bca8a9 100644
--- a/kernel/scsi_transport_iscsi.c
+++ b/kernel/scsi_transport_iscsi.c
@@ -1524,7 +1524,7 @@ static __init int iscsi_transport_init(void)
 		goto unregister_conn_class;
 
 	nls = netlink_kernel_create(NETLINK_ISCSI, 1, iscsi_if_rx,
-			THIS_MODULE);
+			NULL, THIS_MODULE);
 	if (!nls) {
 		err = -ENOBUFS;
 		goto unregister_session_class;
diff --git a/usr/Makefile b/usr/Makefile
index db33ed1..192faab 100644
--- a/usr/Makefile
+++ b/usr/Makefile
@@ -38,7 +38,7 @@ ISCSI_LIB_SRCS = util.o io.o auth.o login.o log.o md5.o sha1.o iscsi_sysfs.o idb
 # sources shared between iscsid and iscsiadm
 COMMON_SRCS =  $(ISCSI_LIB_SRCS)
 # core initiator files
-INITIATOR_SRCS = initiator.o queue.o actor.o mgmt_ipc.o isns.o transport.o
+INITIATOR_SRCS = initiator.o actor.o mgmt_ipc.o isns.o transport.o
 
 all: $(PROGRAMS)
 
@@ -53,4 +53,9 @@ iscsistart: $(IPC_OBJ) $(ISCSI_LIB_SRCS) $(INITIATOR_SRCS) iscsistart.o \
 	$(CC) $(CFLAGS) -static $^ -o $@
 
 clean:
-	rm -f *.o $(PROGRAMS)
+	rm -f *.o $(PROGRAMS) .depend
+
+depend:
+	gcc $(CFLAGS) -M `ls *.c` > .depend
+
+-include .depend
diff --git a/usr/actor.c b/usr/actor.c
index 90b12e2..b487632 100644
--- a/usr/actor.c
+++ b/usr/actor.c
@@ -16,26 +16,25 @@
  *
  * See the file COPYING included with this distribution for more details.
  */
-
-#include <search.h>
 #include <inttypes.h>
 #include "actor.h"
 #include "log.h"
+#include "list.h"
 
-static struct qelem pend_list;
-static struct qelem poll_list;
-static struct qelem actor_list;
-static volatile uint32_t previous_time;
+static LIST_HEAD(pend_list);
+static LIST_HEAD(poll_list);
+static LIST_HEAD(actor_list);
+static volatile uint64_t previous_time;
 static volatile uint32_t scheduler_loops;
 static volatile int poll_in_progress;
 static volatile uint64_t actor_jiffies = 0;
 
 #define actor_diff(_time1, _time2) ({ \
-        uint32_t __ret; \
+        uint64_t __ret; \
         if ((_time2) >= (_time1)) \
            __ret = (_time2) - (_time1); \
         else \
-           __ret = (0xffffffff - (_time1)) + (_time2); \
+           __ret = ((~0ULL) - (_time1)) + (_time2); \
         __ret; \
 })
 
@@ -61,19 +60,12 @@ actor_init(void)
 	poll_in_progress = 0;
 	previous_time = 0;
 	scheduler_loops = 0;
-	pend_list.q_forw = &pend_list;
-	pend_list.q_back = &pend_list;
-	actor_list.q_forw = &actor_list;
-	actor_list.q_back = &actor_list;
-	poll_list.q_forw = &poll_list;
-	poll_list.q_back = &poll_list;
 }
 
 void
 actor_new(actor_t *thread, void (*callback)(void *), void *data)
 {
-	thread->item.q_forw = &thread->item;
-	thread->item.q_back = &thread->item;
+	INIT_LIST_HEAD(&thread->list);
 	thread->state = ACTOR_NOTSCHEDULED;
 	thread->callback = callback;
 	thread->data = data;
@@ -89,7 +81,7 @@ actor_delete(actor_t *thread)
 	case ACTOR_WAITING:
 	case ACTOR_POLL_WAITING:
 		log_debug(1, "deleting a scheduled/waiting thread!");
-		remque(&thread->item);
+		list_del_init(&thread->list);
 		break;
 	default:
 		break;
@@ -101,57 +93,67 @@ static void
 actor_schedule_private(actor_t *thread, uint32_t ttschedule)
 {
 	uint64_t delay_time, current_time;
-	struct qelem *next_item;
 	actor_t *next_thread;
 
 	delay_time = ACTOR_MS_TO_TICKS(ttschedule);
 	current_time = ACTOR_TICKS;
 
-	log_debug(7, "thread %08lx schedule: delay %" PRIu64 " state %d",
-		(long)thread, delay_time, thread->state);
+	log_debug(7, "thread %p schedule: delay %" PRIu64 " state %d",
+		thread, delay_time, thread->state);
 
 	/* convert ttscheduled msecs in 10s of msecs by dividing for now.
 	 * later we will change param to 10s of msecs */
 	switch(thread->state) {
 	case ACTOR_WAITING:
 		log_error("rescheduling a waiting thread!");
+		list_del(&thread->list);
 	case ACTOR_NOTSCHEDULED:
+		INIT_LIST_HEAD(&thread->list);
 		/* if ttschedule is 0, put in scheduled queue and change
 		 * state to scheduled, else add current time to ttschedule and
 		 * insert in the queue at the correct point */
 		if (delay_time == 0) {
 			if (poll_in_progress) {
 				thread->state = ACTOR_POLL_WAITING;
-				insque(&thread->item, poll_list.q_back);
+				list_add_tail(&thread->list, &poll_list);
 			} else {
 				thread->state = ACTOR_SCHEDULED;
-				insque(&thread->item, actor_list.q_back);
+				list_add_tail(&thread->list, &actor_list);
 			}
-		}
-		else {
+		} else {
 			thread->state = ACTOR_WAITING;
 			thread->ttschedule = delay_time;
 			thread->scheduled_at = current_time;
 
 			/* insert new entry in sort order */
-			next_item = pend_list.q_forw;
-			while (next_item != &pend_list) {
-				next_thread = (actor_t *)next_item;
+			list_for_each_entry(next_thread, &pend_list, list) {
+				log_debug(7, "thread %p %" PRIu64 " %"PRIu64,
+					next_thread,
+					next_thread->scheduled_at +
+					next_thread->ttschedule,
+					current_time + delay_time);
 
 				if (time_after(next_thread->scheduled_at +
 						       next_thread->ttschedule,
-						current_time + delay_time))
-					break;
-				next_item = next_item->q_forw;
+						current_time + delay_time)) {
+					list_add(&thread->list,
+						 &next_thread->list);
+					goto done;
+				}
 			}
 
-			insque(&thread->item, next_item->q_back);
+			list_add_tail(&thread->list, &pend_list);
 		}
+done:
 		break;
 	case ACTOR_POLL_WAITING:
 	case ACTOR_SCHEDULED:
 		// don't do anything
 		break;
+	case ACTOR_INVALID:
+		log_error("BUG: Trying to schedule a thread that has not been "
+			  "setup. Ignoring sched.");
+		break;
 	}
 
 }
@@ -174,7 +176,7 @@ int
 actor_timer_mod(actor_t *thread, uint32_t timeout, void *data)
 {
 	if (thread->state == ACTOR_WAITING) {
-		remque(&thread->item);
+		list_del_init(&thread->list);
 		thread->data = data;
 		actor_schedule_private(thread, timeout);
 		return 1;
@@ -185,9 +187,9 @@ actor_timer_mod(actor_t *thread, uint32_t timeout, void *data)
 void
 actor_check(uint64_t current_time)
 {
-	while (pend_list.q_forw != &pend_list) {
-		actor_t *thread = (actor_t *)pend_list.q_forw;
+	struct actor *thread, *tmp;
 
+	list_for_each_entry_safe(thread, tmp, &pend_list, list) {
 		if (actor_diff_time(thread, current_time)) {
 			log_debug(7, "thread %08lx wait some more",
 				(long)thread);
@@ -196,22 +198,22 @@ actor_check(uint64_t current_time)
 		}
 
 		/* it is time to schedule this entry */
-		remque(&thread->item);
+		list_del_init(&thread->list);
 
 		log_debug(2, "thread %08lx was scheduled at %" PRIu64 ":"
 			"%" PRIu64 ", curtime %" PRIu64 " q_forw %p "
 			"&pend_list %p",
 			(long)thread, thread->scheduled_at, thread->ttschedule,
-			current_time, pend_list.q_forw, &pend_list);
+			current_time, pend_list.next, &pend_list);
 
 		if (poll_in_progress) {
 			thread->state = ACTOR_POLL_WAITING;
-			insque(&thread->item, poll_list.q_back);
+			list_add_tail(&thread->list, &poll_list);
 			log_debug(7, "thread %08lx now in poll_list",
 				(long)thread);
 		} else {
 			thread->state = ACTOR_SCHEDULED;
-			insque(&thread->item, actor_list.q_back);
+			list_add_tail(&thread->list, &actor_list);
 			log_debug(7, "thread %08lx now in actor_list",
 				(long)thread);
 		}
@@ -222,6 +224,7 @@ void
 actor_poll(void)
 {
 	uint64_t current_time;
+	struct actor *thread;
 
 	/* check that there are no any concurrency */
 	if (poll_in_progress) {
@@ -245,11 +248,14 @@ actor_poll(void)
 
 	/* the following code to check in the main data path */
 	poll_in_progress = 1;
-	while (actor_list.q_forw != &actor_list) {
-		actor_t *thread = (actor_t *)actor_list.q_forw;
+	while (!list_empty(&actor_list)) {
+		thread = list_entry(actor_list.next, struct actor, list);
+		list_del_init(&thread->list);
+
 		if (thread->state != ACTOR_SCHEDULED)
-			log_debug(1, "actor_list: thread state corrupted!");
-		remque(&thread->item);
+			log_error("actor_list: thread state corrupted! "
+				  "Thread with state %d in actor list.",
+				  thread->state);
 		thread->state = ACTOR_NOTSCHEDULED;
 		log_debug(7, "exec thread %08lx callback", (long)thread);
 		thread->callback(thread->data);
@@ -257,13 +263,16 @@ actor_poll(void)
 	}
 	poll_in_progress = 0;
 
-	while (poll_list.q_forw != &poll_list) {
-		actor_t *thread = (actor_t *)poll_list.q_forw;
+	while (!list_empty(&poll_list)) {
+		thread = list_entry(poll_list.next, struct actor, list);
+		list_del_init(&thread->list);
+
 		if (thread->state != ACTOR_POLL_WAITING)
-			log_debug(1, "poll_list: thread state corrupted!");
-		remque(&thread->item);
+			log_error("poll_list: thread state corrupted!"
+				  "Thread with state %d in poll list.",
+				  thread->state);
 		thread->state = ACTOR_SCHEDULED;
-		insque(&thread->item, actor_list.q_back);
+		list_add_tail(&thread->list, &actor_list);
 		log_debug(7, "thread %08lx removed from poll_list",
 			(long)thread);
 	}
diff --git a/usr/actor.h b/usr/actor.h
index 88c69d6..7a71d42 100644
--- a/usr/actor.h
+++ b/usr/actor.h
@@ -20,11 +20,13 @@
 #define ACTOR_H
 
 #include "types.h"
+#include "list.h"
 
 #define ACTOR_RESOLUTION	250	/* in millis */
 #define ACTOR_MAX_LOOPS		1
 
 typedef enum actor_state_e {
+    ACTOR_INVALID,
     ACTOR_WAITING,
     ACTOR_SCHEDULED,
     ACTOR_NOTSCHEDULED,
@@ -32,12 +34,12 @@ typedef enum actor_state_e {
 } actor_state_e;
 
 typedef struct actor {
-    struct qelem item; /* must be first element in the struct */
-    actor_state_e state;
-    void *data;
-    void (*callback)(void * );
-    uint64_t scheduled_at;
-    uint64_t ttschedule;
+	struct list_head list;
+	actor_state_e state;
+	void *data;
+	void (*callback)(void * );
+	uint64_t scheduled_at;
+	uint64_t ttschedule;
 } actor_t;
 
 extern void actor_new(actor_t *thread, void (*callback)(void *), void * data);
diff --git a/usr/idbm.c b/usr/idbm.c
index dcdae56..f63e5b4 100644
--- a/usr/idbm.c
+++ b/usr/idbm.c
@@ -24,13 +24,10 @@
 #include <unistd.h>
 #include <stdlib.h>
 #include <string.h>
-#include <fcntl.h>
 #include <errno.h>
-#include <stdarg.h>
 #include <dirent.h>
 #include <sys/stat.h>
 #include <sys/file.h>
-#include <sys/types.h>
 
 #include "idbm.h"
 #include "log.h"
@@ -103,6 +100,29 @@
 	_n++; \
 } while(0)
 
+/*
+ * from linux kernel
+ */
+static char *strstrip(char *s)
+{
+	size_t size;
+	char *end;
+
+	size = strlen(s);
+	if (!size)
+		return s;
+
+	end = s + size - 1;
+	while (end >= s && isspace(*end))
+		end--;
+	*(end + 1) = '\0';
+
+	while (*s && isspace(*s))
+		s++;
+
+	return s;
+}
+
 static char *get_global_string_param(char *pathname, const char *key)
 {
 	FILE *f = NULL;
@@ -119,30 +139,21 @@ static char *get_global_string_param(char *pathname, const char *key)
 	if ((f = fopen(pathname, "r"))) {
 		while ((line = fgets(buffer, sizeof (buffer), f))) {
 
-			while (line && isspace(c = *line))
-				line++;
+			line = strstrip(line);
 
 			if (strncmp(line, key, len) == 0) {
 				char *end = line + len;
 
-				/* the name is everything up to the first
-				 * bit of whitespace
+				/*
+				 * make sure ther is something after the
+				 * key.
 				 */
-				while (*end && (!isspace(c = *end)))
-					end++;
-
-				if (isspace(c = *end))
-					*end = '\0';
-
-				if (end > line + len)
+				if (strlen(end))
 					name = strdup(line + len);
 			}
 		}
 		fclose(f);
-		if (!name)
-			log_error("an %s is required, but was not found in %s",
-				  key, pathname);
-		else
+		if (name)
 			log_debug(5, "%s=%s", key, name);
 	} else
 		log_error("can't open %s configuration file %s", key, pathname);
@@ -152,7 +163,13 @@ static char *get_global_string_param(char *pathname, const char *key)
 
 char *get_iscsi_initiatorname(char *pathname)
 {
-	return get_global_string_param(pathname, "InitiatorName=");
+	char *name;
+
+	name = get_global_string_param(pathname, "InitiatorName=");
+	if (!name)
+		log_error("An InitiatorName= is required, but was not "
+			  "found in %s", pathname);
+	return name;
 }
 
 char *get_iscsi_initiatoralias(char *pathname)
@@ -538,7 +555,7 @@ idbm_recinfo_config(recinfo_t *info, FILE *f)
 	char value[VALUE_MAXVAL];
 	char *line, *nl, buffer[2048];
 	int line_number = 0;
-	int c, i;
+	int c = 0, i;
 
 	fseek(f, 0, SEEK_SET);
 
@@ -549,27 +566,16 @@ idbm_recinfo_config(recinfo_t *info, FILE *f)
 		if (!line)
 			continue;
 
-		/* skip leading whitespace */
-		while (isspace(c = *line))
-			line++;
-
-		/* strip trailing whitespace, including the newline.
-		 * anything that needs the whitespace must be quoted.
-		 */
 		nl = line + strlen(line) - 1;
-		if (*nl == '\n') {
-			do {
-				*nl = '\0';
-				nl--;
-			} while (isspace(c = *nl));
-		} else {
-			log_warning("config file line %d too long",
+		if (*nl != '\n') {
+			log_warning("Config file line %d too long.",
 			       line_number);
 			continue;
 		}
 
+		line = strstrip(line);
 		/* process any non-empty, non-comment lines */
-		if (!*line || *line == '#')
+		if (!*line || *line == '\0' || *line ==  '\n' || *line == '#')
 			continue;
 
 		/* parse name */
@@ -827,7 +833,7 @@ struct iface_rec *iface_alloc(char *ifname, int *err)
 	return iface;
 }
 
-int iface_conf_read(struct iface_rec *iface)
+static int __iface_conf_read(struct iface_rec *iface)
 {
 	char *iface_conf;
 	recinfo_t *info;
@@ -874,7 +880,17 @@ free_conf:
 	return rc;
 }
 
-int iface_conf_delete(struct iface_rec *iface)
+int iface_conf_read(idbm_t *db, struct iface_rec *iface)
+{
+	int rc;
+
+	idbm_lock(db);
+	rc = __iface_conf_read(iface);
+	idbm_unlock(db);
+	return rc;
+}
+
+int iface_conf_delete(idbm_t *db, struct iface_rec *iface)
 {
 	char *iface_conf;
 	int rc = 0;
@@ -884,13 +900,16 @@ int iface_conf_delete(struct iface_rec *iface)
 		return ENOMEM;
 
 	sprintf(iface_conf, "%s/%s", IFACE_CONFIG_DIR, iface->name);
+	idbm_lock(db);
 	if (unlink(iface_conf))
 		rc = errno;
+	idbm_unlock(db);
+
 	free(iface_conf);
 	return rc;
 }
 
-int iface_conf_write(struct iface_rec *iface)
+int iface_conf_write(idbm_t *db, struct iface_rec *iface)
 {
 	char *iface_conf;
 	FILE *f;
@@ -907,14 +926,18 @@ int iface_conf_write(struct iface_rec *iface)
 		goto free_conf;
 	}
 
+	idbm_lock(db);
 	idbm_print(PRINT_TYPE_IFACE, iface, 1, f);
+	idbm_unlock(db);
+
 	fclose(f);
 free_conf:
 	free(iface_conf);
 	return rc;
 }
 
-int iface_conf_update(struct db_set_param *param, struct iface_rec *iface)
+int iface_conf_update(idbm_t *db, struct db_set_param *param,
+		       struct iface_rec *iface)
 {
 	recinfo_t *info;
 	int rc = 0;
@@ -930,7 +953,7 @@ int iface_conf_update(struct db_set_param *param, struct iface_rec *iface)
 		goto free_info;
 	}
 
-	rc = iface_conf_write(iface);
+	rc = iface_conf_write(db, iface);
 free_info:
 	free(info);
 	return rc;
@@ -968,7 +991,7 @@ static void idbm_read_def_ifaces(struct list_head *ifaces)
 			continue;
 		}
 
-		err = iface_conf_read(iface);
+		err = __iface_conf_read(iface);
 		if (err) {
 			log_error("Could not read def iface %s (err %d)",
 				  iface->name, err);
@@ -1131,7 +1154,7 @@ static int __iface_setup_host_bindings(void *data, struct host_info *info)
 		strcpy(iface.hwaddress, info->iface.hwaddress);
 		strcpy(iface.transport_name, info->iface.transport_name);
 		sprintf(iface.name, "iface%d", id);
-		if (iface_conf_write(&iface))
+		if (iface_conf_write(db, &iface))
 			log_error("Could not write iface conf for %s %s",
 				  iface.name, iface.hwaddress);
 			/* fall through - will not be persistent */
@@ -1308,7 +1331,7 @@ int iface_for_each_iface(idbm_t *db, void *data, int *nr_found, iface_op_fn *fn)
 		}
 
 		idbm_lock(db);
-		err = iface_conf_read(iface);
+		err = __iface_conf_read(iface);
 		idbm_unlock(db);
 		if (err) {
 			log_error("Could not read def iface %s (err %d)",
@@ -2023,7 +2046,7 @@ idbm_discovery_write(idbm_t *db, discovery_rec_t *rec)
 	portal = malloc(PATH_MAX);
 	if (!portal) {
 		log_error("Could not alloc portal\n");
-		return -ENOMEM;
+		return ENOMEM;
 	}
 
 	idbm_lock(db);
@@ -2106,8 +2129,8 @@ static int setup_disc_to_node_link(char *disc_portal, node_rec_t *rec)
 			}
 		}
 
-		snprintf(disc_portal, PATH_MAX, "%s/%s,%d/%s,%s,%d,%d,%s",
-			 ISNS_CONFIG_DIR, rec->disc_address, rec->disc_port,
+		snprintf(disc_portal, PATH_MAX, "%s/%s,%s,%d,%d,%s",
+			 ISNS_CONFIG_DIR,
 			 rec->name, rec->conn[0].address,
 			 rec->conn[0].port, rec->tpgt, rec->iface.name);
 		break;
@@ -2224,6 +2247,15 @@ int idbm_add_nodes(idbm_t *db, node_rec_t *newrec, discovery_rec_t *drec,
 		}
 	} else {
 		list_for_each_entry(iface, ifaces, list) {
+			if (strcmp(iface->name, DEFAULT_IFACENAME) &&
+			    !iface_is_bound(iface)) {
+				log_error("iface %s is not bound. Will not "
+					  "bind node to it. Iface settings "
+					  iface_fmt, iface->name,
+					  iface_str(iface));
+				continue;
+			}
+
 			iface_copy(&newrec->iface, iface);
 			rc = idbm_add_node(db, newrec, drec);
 			if (rc)
@@ -2528,11 +2560,21 @@ idbm_init(char *configfile)
 {
 	idbm_t *db;
 
+	/* make sure root db dir is there */
+	if (access(ISCSI_CONFIG_ROOT, F_OK) != 0) {
+		if (mkdir(ISCSI_CONFIG_ROOT, 0660) != 0) {
+			log_error("Could not make %s %d\n", ISCSI_CONFIG_ROOT,
+				   errno);
+			return NULL;
+		}
+	}
+
 	db = malloc(sizeof(idbm_t));
 	if (!db) {
 		log_error("out of memory on idbm allocation");
 		return NULL;
 	}
+
 	memset(db, 0, sizeof(idbm_t));
 	db->configfile = strdup(configfile);
 	return db;
diff --git a/usr/idbm.h b/usr/idbm.h
index 5d809f2..722dd32 100644
--- a/usr/idbm.h
+++ b/usr/idbm.h
@@ -130,7 +130,7 @@ extern int iface_is_bound(struct iface_rec *iface);
 extern int iface_match_bind_info(struct iface_rec *pattern,
 				  struct iface_rec *iface);
 extern struct iface_rec *iface_alloc(char *ifname, int *err);
-extern int iface_conf_read(struct iface_rec *iface);
+extern int iface_conf_read(idbm_t *db, struct iface_rec *iface);
 extern void iface_init(struct iface_rec *iface);
 extern int iface_is_bound_by_hwaddr(struct iface_rec *iface);
 extern int iface_is_bound_by_netdev(struct iface_rec *iface);
@@ -142,11 +142,10 @@ extern int iface_print_flat(void *data, struct iface_rec *iface);
 extern void iface_setup_host_bindings(idbm_t *db);
 extern int iface_get_by_bind_info(idbm_t *db, struct iface_rec *pattern,
 				 struct iface_rec *out_rec);
-extern int iface_conf_update(struct db_set_param *set_param,
+extern int iface_conf_update(idbm_t *db, struct db_set_param *set_param,
 			     struct iface_rec *iface);
-extern int iface_conf_write(struct iface_rec *iface);
-extern int iface_conf_delete(struct iface_rec *iface);
-extern int iface_conf_read(struct iface_rec *iface);
+extern int iface_conf_write(idbm_t *db, struct iface_rec *iface);
+extern int iface_conf_delete(idbm_t *db, struct iface_rec *iface);
 
 #define iface_fmt "[hw=%s,ip=%s,net_if=%s,iscsi_if=%s]"
 #define iface_str(_iface) \
diff --git a/usr/initiator.c b/usr/initiator.c
index 4831ad8..f5cbcb9 100644
--- a/usr/initiator.c
+++ b/usr/initiator.c
@@ -20,20 +20,10 @@
  */
 
 #include <unistd.h>
-#include <search.h>
 #include <string.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <netdb.h>
 #include <errno.h>
-#include <fcntl.h>
-#include <dirent.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/param.h>
-#include <sys/wait.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
 
 #include "initiator.h"
 #include "transport.h"
@@ -47,8 +37,7 @@
 #include "iscsi_sysfs.h"
 #include "iscsi_settings.h"
 
-static void __session_mainloop(void *data);
-static void __conn_error_handle(iscsi_session_t*, iscsi_conn_t*);
+static void iscsi_login_timedout(void *data);
 
 #define DEFAULT_TIME2WAIT 2
 
@@ -69,65 +58,75 @@ __padding(unsigned int param)
 	return param + pad;
 }
 
-static int
-__recvpool_alloc(iscsi_conn_t *conn)
+static int iscsi_conn_context_alloc(iscsi_conn_t *conn)
 {
 	int i;
 
-	for (i = 0; i < RECVPOOL_MAX; i++) {
-		conn->recvpool[i] = calloc(1, ipc->ctldev_bufmax);
-		if (!conn->recvpool[i]) {
+	for (i = 0; i < CONTEXT_POOL_MAX; i++) {
+		conn->context_pool[i] = calloc(1,
+					   sizeof(struct iscsi_conn_context) +
+					   ipc->ctldev_bufmax);
+		if (!conn->context_pool[i]) {
 			int j;
 			for (j = 0; j < i; j++)
-				free(conn->recvpool[j]);
-			return -ENOMEM;
+				free(conn->context_pool[j]);
+			return ENOMEM;
 		}
+		conn->context_pool[i]->conn = conn;
 	}
 
 	return 0;
 }
 
-static void
-__recvpool_free(iscsi_conn_t *conn)
+static void iscsi_conn_context_free(iscsi_conn_t *conn)
 {
 	int i;
 
-	for (i = 0; i < RECVPOOL_MAX; i++) {
-		if (!conn->recvpool[i]) {
-			log_error("recvpool leak: %d bytes",
-				  ipc->ctldev_bufmax);
-		} else
-			free(conn->recvpool[i]);
+	for (i = 0; i < CONTEXT_POOL_MAX; i++) {
+		if (!conn->context_pool[i])
+			continue;
+
+		if (conn->context_pool[i]->allocated)
+			/* missing flush on shutdown */
+			log_error("BUG: context_pool leak");
+		free(conn->context_pool[i]);
 	}
 }
 
-void* recvpool_get(iscsi_conn_t *conn, int ev_size)
+struct iscsi_conn_context *iscsi_conn_context_get(iscsi_conn_t *conn,
+						  int ev_size)
 {
+	struct iscsi_conn_context *conn_context;
 	int i;
 
 	if (ev_size > ipc->ctldev_bufmax)
 		return NULL;
 
-	for (i = 0; i < RECVPOOL_MAX; i++) {
-		if (conn->recvpool[i]) {
-			void *handle = conn->recvpool[i];
-			conn->recvpool[i] = NULL;
-			return handle;
+	for (i = 0; i < CONTEXT_POOL_MAX; i++) {
+		if (!conn->context_pool[i])
+			continue;
+
+		if (!conn->context_pool[i]->allocated) {
+			conn_context = conn->context_pool[i];
+
+			memset(&conn_context->actor, 0,
+				sizeof(struct actor));
+			conn_context->allocated = 1;
+			/* some callers abuse this pointer */
+			conn_context->data = conn_context +
+					sizeof(struct iscsi_conn_context);
+			log_debug(7, "get conn context %p",
+				  &conn_context->actor);
+			return conn_context;
 		}
 	}
 	return NULL;
 }
 
-void recvpool_put(iscsi_conn_t *conn, void *handle)
+void iscsi_conn_context_put(struct iscsi_conn_context *conn_context)
 {
-	int i;
-
-	for (i = 0; i < RECVPOOL_MAX; i++) {
-		if (!conn->recvpool[i]) {
-			conn->recvpool[i] = handle;
-			break;
-		}
-	}
+	log_debug(7, "put conn context %p", &conn_context->actor);
+	conn_context->allocated = 0;
 }
 
 static void session_online_devs(int host_no, int sid)
@@ -326,7 +325,7 @@ setup_portal(iscsi_conn_t *conn, conn_rec_t *conn_rec)
 	if (resolve_address(conn_rec->address, port, &conn->saddr)) {
 		log_error("cannot resolve host name %s",
 			  conn_rec->address);
-		return -EINVAL;
+		return EINVAL;
 	}
 	conn->failback_saddr = conn->saddr;
 
@@ -343,9 +342,9 @@ __session_conn_create(iscsi_session_t *session, int cid)
 	conn_rec_t *conn_rec = &session->nrec.conn[cid];
 	int err;
 
-	if (__recvpool_alloc(conn)) {
-		log_error("cannot allocate recvpool for conn cid %d", cid);
-		return -ENOMEM;
+	if (iscsi_conn_context_alloc(conn)) {
+		log_error("cannot allocate context_pool for conn cid %d", cid);
+		return ENOMEM;
 	}
 
 	conn->socket_fd = -1;
@@ -430,81 +429,16 @@ __session_conn_create(iscsi_session_t *session, int cid)
 }
 
 static void
-__send_pdu_timedout(void *data)
-{
-	queue_task_t *qtask = data;
-	iscsi_conn_t *conn = qtask->conn;
-	iscsi_session_t *session = conn->session;
-
-	if (conn->send_pdu_in_progress) {
-		/*
-		 * redirect timeout processing to __session_conn_timer()
-		 */
-		queue_produce(session->queue, EV_CONN_TIMER, qtask, 0, NULL);
-		actor_schedule(&session->mainloop);
-		log_debug(7, "send_pdu timer timedout!");
-	}
-}
-
-static void
-__send_pdu_timer_add(struct iscsi_conn *conn, int timeout)
-{
-	if (conn->state == STATE_IN_LOGIN) {
-		iscsi_login_context_t *c = &conn->login_context;
-		conn->send_pdu_in_progress = 1;
-		actor_timer(&conn->send_pdu_timer, timeout*1000,
-			    __send_pdu_timedout, c->qtask);
-		log_debug(7, "send_pdu timer added %d secs", timeout);
-	}
-}
-
-static void
-__send_pdu_timer_remove(struct iscsi_conn *conn)
-{
-	if (conn->send_pdu_in_progress) {
-		actor_delete(&conn->send_pdu_timer);
-		conn->send_pdu_in_progress = 0;
-		log_debug(7, "send_pdu timer removed");
-	}
-}
-
-
-static void
-session_stop_conn_timers(iscsi_session_t *session, int cid)
-{
-	iscsi_conn_t *conn = &session->conn[cid];
-
-	__send_pdu_timer_remove(conn);
-	actor_delete(&conn->connect_timer);
-}
-
-static void
 session_release(iscsi_session_t *session)
 {
 	log_debug(2, "Releasing session %p", session);
 
 	if (session->target_alias)
 		free(session->target_alias);
-	__recvpool_free(&session->conn[0]);
-	actor_delete(&session->mainloop);
-	queue_destroy(session->queue);
+	iscsi_conn_context_free(&session->conn[0]);
 	free(session);
 }
 
-static void
-session_put(iscsi_session_t *session)
-{
-	session->refcount--;
-	if (session->refcount == 0)
-		session_release(session);
-}
-
-static void
-session_get(iscsi_session_t *session)
-{
-	session->refcount++;
-}
-
 static iscsi_session_t*
 __session_create(node_rec_t *rec, struct iscsi_transport *t)
 {
@@ -518,7 +452,6 @@ __session_create(node_rec_t *rec, struct iscsi_transport *t)
 	log_debug(2, "Allocted session %p", session);
 
 	INIT_LIST_HEAD(&session->list);
-	session_get(session);
 	/* opened at daemon load time (iscsid.c) */
 	session->ctrl_fd = control_fd;
 	session->t = t;
@@ -527,18 +460,6 @@ __session_create(node_rec_t *rec, struct iscsi_transport *t)
 	/* save node record. we might need it for redirection */
 	memcpy(&session->nrec, rec, sizeof(node_rec_t));
 
-	/* initalize per-session queue */
-	session->queue = queue_create(4, 4, NULL, session);
-	if (session->queue == NULL) {
-		log_error("can not create session's queue");
-		free(session);
-		return NULL;
-	}
-
-	/* initalize per-session event processor */
-	actor_new(&session->mainloop, __session_mainloop, session);
-	actor_schedule(&session->mainloop);
-
 	/* session's operational parameters */
 	session->initial_r2t_en = rec->session.iscsi.InitialR2T;
 	session->imm_data_en = rec->session.iscsi.ImmediateData;
@@ -610,20 +531,21 @@ __session_create(node_rec_t *rec, struct iscsi_transport *t)
 	return session;
 }
 
-static void iscsi_queue_flush(queue_t *queue)
+static void iscsi_flush_context_pool(struct iscsi_session *session)
 {
-	unsigned char item_buf[sizeof(queue_item_t) + EVENT_PAYLOAD_MAX];
-	queue_item_t *item = (queue_item_t *)(void *)item_buf;
-
-	/* flush queue by consuming all enqueued items */
-	while (queue_consume(queue, EVENT_PAYLOAD_MAX,
-			     item) != QUEUE_IS_EMPTY) {
-		uintptr_t recv_handle = *(uintptr_t *)queue_item_data(item);
-
-		log_debug(7, "item %p(%d) data size %d flushed", item,
-			  item->event_type, item->data_size);
-		if (item->data_size)
-			recvpool_put(item->context, (void*)recv_handle);
+	struct iscsi_conn_context *conn_context;
+	struct iscsi_conn *conn = &session->conn[0];
+	int i;
+
+	for (i = 0; i < CONTEXT_POOL_MAX; i++) {
+		conn_context = conn->context_pool[i];
+		if (!conn_context)
+			continue;
+
+		if (conn_context->allocated) {
+			actor_delete(&(conn->context_pool[i]->actor));
+			iscsi_conn_context_put(conn_context);
+		}
 	}
 }
 
@@ -632,19 +554,15 @@ __session_destroy(iscsi_session_t *session)
 {
 	log_debug(1, "destroying session\n");
 	list_del(&session->list);
-	iscsi_queue_flush(session->queue);
-	session_put(session);
+	iscsi_flush_context_pool(session);
+	session_release(session);
 }
 
 static void
-__conn_noop_out_delete(iscsi_conn_t *conn)
+conn_delete_timers(iscsi_conn_t *conn)
 {
-	if (conn->noop_out_interval) {
-		actor_delete(&conn->noop_out_timer);
-		actor_delete(&conn->noop_out_timeout_timer);
-		log_debug(3, "conn noop out timer %p stopped\n",
-				&conn->noop_out_timer);
-	}
+	actor_delete(&conn->login_timer);
+	actor_delete(&conn->nop_out_timer);
 }
 
 static void
@@ -654,7 +572,7 @@ session_conn_cleanup(queue_task_t *qtask, mgmt_ipc_err_e err)
 	iscsi_session_t *session = conn->session;
 
 	mgmt_ipc_write_rsp(qtask, err);
-	session_stop_conn_timers(session, conn->id);
+	conn_delete_timers(conn);
 	__session_destroy(session);
 }
 
@@ -664,10 +582,6 @@ __session_conn_shutdown(iscsi_conn_t *conn, queue_task_t *qtask,
 {
 	iscsi_session_t *session = conn->session;
 
-	__conn_noop_out_delete(conn);
-	actor_delete(&conn->connect_timer);
-	iscsi_queue_flush(session->queue);
-
 	if (ipc->destroy_conn(session->t->handle, session->id,
 		conn->id)) {
 		log_error("can not safely destroy connection %d", conn->id);
@@ -700,96 +614,6 @@ session_conn_shutdown(iscsi_conn_t *conn, queue_task_t *qtask,
 	return __session_conn_shutdown(conn, qtask, err);
 }
 
-static int
-__send_nopin_rsp(iscsi_conn_t *conn, struct iscsi_nopin *rhdr, char *data)
-{
-	struct iscsi_nopout hdr;
-
-	memset(&hdr, 0, sizeof(struct iscsi_nopout));
-	hdr.opcode = ISCSI_OP_NOOP_OUT | ISCSI_OP_IMMEDIATE;
-	hdr.flags = ISCSI_FLAG_CMD_FINAL;
-	hdr.dlength[0] = rhdr->dlength[0];
-	hdr.dlength[1] = rhdr->dlength[1];
-	hdr.dlength[2] = rhdr->dlength[2];
-	memcpy(hdr.lun, rhdr->lun, 8);
-	hdr.ttt = rhdr->ttt;
-	hdr.itt = ISCSI_RESERVED_TAG;
-
-	return iscsi_io_send_pdu(conn, (struct iscsi_hdr*)&hdr,
-	       ISCSI_DIGEST_NONE, data, ISCSI_DIGEST_NONE, 0);
-}
-
-static int
-__send_nopout(iscsi_conn_t *conn)
-{
-	struct iscsi_nopout hdr;
-
-	memset(&hdr, 0, sizeof(struct iscsi_nopout));
-	hdr.opcode = ISCSI_OP_NOOP_OUT | ISCSI_OP_IMMEDIATE;
-	hdr.flags = ISCSI_FLAG_CMD_FINAL;
-	hdr.itt = 0;  /* XXX: let kernel send_pdu set for us*/
-	hdr.ttt = ISCSI_RESERVED_TAG;
-	/* we have hdr.lun reserved, and no data */
-	return iscsi_io_send_pdu(conn, (struct iscsi_hdr*)&hdr,
-		ISCSI_DIGEST_NONE, NULL, ISCSI_DIGEST_NONE, 0);
-}
-
-void
-__conn_noop_out_timeout(void *data)
-{
-	iscsi_conn_t *conn = (iscsi_conn_t*)data;
-	iscsi_session_t *session = conn->session;
-
-	log_warning("Nop-out timedout after %d seconds on connection %d:%d "
-		    "state (%d). Dropping session.", conn->noop_out_timeout,
-		    session->id, conn->id, conn->state);
-	/* XXX: error handle */
-	__conn_error_handle(session, conn);
-}
-
-void
-__conn_noop_out(void *data)
-{
-	iscsi_conn_t *conn = (iscsi_conn_t*)data;
-
-	if (conn->noop_out_timeout_timer.state == ACTOR_NOTSCHEDULED) {
-		__send_nopout(conn);
-
-		actor_timer(&conn->noop_out_timeout_timer,
-				conn->noop_out_timeout*1000,
-				__conn_noop_out_timeout, conn);
-		log_debug(3, "noop out timeout timer %p start, timeout %d\n",
-			 &conn->noop_out_timeout_timer, conn->noop_out_timeout);
-	}
-}
-
-static void
-__connect_timedout(void *data)
-{
-	queue_task_t *qtask = data;
-	iscsi_conn_t *conn = qtask->conn;
-	iscsi_session_t *session = conn->session;
-
-	if (conn->state == STATE_XPT_WAIT) {
-		/* flush any polls or other events queued */
-		iscsi_queue_flush(session->queue);
-		log_debug(3, "__connect_timedout queue EV_CONN_TIMER\n");
-		queue_produce(session->queue, EV_CONN_TIMER, qtask, 0, NULL);
-		actor_schedule(&session->mainloop);
-	}
-}
-
-static void
-queue_delayed_reopen(queue_task_t *qtask, int delay)
-{
-	iscsi_conn_t *conn = qtask->conn;
-
-	log_debug(4, "Requeue reopen attempt in %d secs\n", delay);
-	actor_delete(&conn->connect_timer);
-	actor_timer(&conn->connect_timer, delay * 1000,
-		    __connect_timedout, qtask);
-}
-
 static void
 reset_iscsi_params(iscsi_conn_t *conn)
 {
@@ -818,11 +642,28 @@ reset_iscsi_params(iscsi_conn_t *conn)
 	session->erl = rec->session.iscsi.ERL;
 }
 
-static int
+static void
+queue_delayed_reopen(queue_task_t *qtask, int delay)
+{
+	iscsi_conn_t *conn = qtask->conn;
+
+	log_debug(4, "Requeue reopen attempt in %d secs\n", delay);
+
+	/*
+ 	 * iscsi_login_eh can handle the login resched as
+ 	 * if it were login time out
+ 	 */
+	actor_delete(&conn->login_timer);
+	actor_timer(&conn->login_timer, delay * 1000,
+		    iscsi_login_timedout, qtask);
+}
+
+static void
 __session_conn_reopen(iscsi_conn_t *conn, queue_task_t *qtask, int do_stop)
 {
 	int rc, delay;
 	iscsi_session_t *session = conn->session;
+	struct iscsi_conn_context *conn_context;
 
 	log_debug(1, "re-opening session %d (reopen_cnt %d)", session->id,
 			session->reopen_cnt);
@@ -831,11 +672,8 @@ __session_conn_reopen(iscsi_conn_t *conn, queue_task_t *qtask, int do_stop)
 	qtask->conn = conn;
 
 	/* flush stale polls or errors queued */
-	iscsi_queue_flush(session->queue);
-	actor_delete(&conn->connect_timer);
-	__conn_noop_out_delete(conn);
-
-	__send_pdu_timer_remove(conn);
+	iscsi_flush_context_pool(session);
+	conn_delete_timers(conn);
 	conn->state = STATE_XPT_WAIT;
 
 	if (do_stop) {
@@ -854,6 +692,15 @@ __session_conn_reopen(iscsi_conn_t *conn, queue_task_t *qtask, int do_stop)
 	if (session->time2wait)
 		goto queue_reopen;
 
+	conn_context = iscsi_conn_context_get(conn, 0);
+	if (!conn_context) {
+		/* while reopening the recv pool should be full */
+		log_error("BUG: __session_conn_reopen could not get conn "
+			  "context for recv.");
+		goto queue_reopen;
+	}
+	conn_context->data = qtask;
+
 	rc = conn->session->t->template->ep_connect(conn, 1);
 	if (rc < 0 && errno != EINPROGRESS) {
 		char serv[NI_MAXSERV];
@@ -865,31 +712,27 @@ __session_conn_reopen(iscsi_conn_t *conn, queue_task_t *qtask, int do_stop)
 
 		log_error("cannot make a connection to %s:%s (%d)",
 			  conn->host, serv, errno);
+		iscsi_conn_context_put(conn_context);
 		goto queue_reopen;
 	}
 
-	queue_produce(session->queue, EV_CONN_POLL, qtask, 0, NULL);
-	actor_schedule(&session->mainloop);
-
-	actor_timer(&conn->connect_timer, conn->login_timeout*1000,
-		    __connect_timedout, qtask);
-
-	return 0;
+	iscsi_sched_conn_context(conn_context, conn, 0, EV_CONN_POLL);
+	log_debug(3, "Setting login timer %p timeout %d", &conn->login_timer,
+		  conn->login_timeout);
+	actor_timer(&conn->login_timer, conn->login_timeout * 1000,
+		    iscsi_login_timedout, qtask);
+	return; 
 
 queue_reopen:
-	if (session->time2wait) {
-		rc = 0;
+	if (session->time2wait)
 		delay = session->time2wait;
-	} else {
-		rc = -1;
+	else
 		delay = DEFAULT_TIME2WAIT;
-	}
 	session->time2wait = 0;
 	queue_delayed_reopen(qtask, delay);
-	return rc;
 }
 
-static int
+static void
 session_conn_reopen(iscsi_conn_t *conn, queue_task_t *qtask, int do_stop)
 {
 	iscsi_session_t *session = conn->session;
@@ -903,28 +746,259 @@ session_conn_reopen(iscsi_conn_t *conn, queue_task_t *qtask, int do_stop)
 	memset(&conn->saddr, 0, sizeof(struct sockaddr_storage));
 	conn->saddr = conn->failback_saddr;
 
-	return __session_conn_reopen(conn, qtask, do_stop);
+	__session_conn_reopen(conn, qtask, do_stop);
 }
 
-static int
-iscsi_login_redirect(iscsi_conn_t *conn)
+static void
+__conn_error_handle(iscsi_session_t *session, iscsi_conn_t *conn)
+{
+	int i;
+
+	switch (conn->state) {
+	case STATE_IN_LOGOUT:
+		/* logout was requested by user */
+		if (conn->logout_qtask) {
+			session_conn_shutdown(conn, conn->logout_qtask,
+					      MGMT_IPC_OK);
+			return;
+		}
+		/* logout was from eh - fall down to cleanup */
+	case STATE_LOGGED_IN:
+		/* mark failed connection */
+		conn->state = STATE_CLEANUP_WAIT;
+
+		if (session->erl > 0) {
+			/* check if we still have some logged in connections */
+			for (i=0; i<ISCSI_CONN_MAX; i++) {
+				if (session->conn[i].state == STATE_LOGGED_IN) {
+					break;
+				}
+			}
+			if (i != ISCSI_CONN_MAX) {
+				/* FIXME: re-assign leading connection
+				 *        for ERL>0 */
+			}
+
+			break;
+		}
+
+		/* mark all connections as failed */
+		for (i=0; i<ISCSI_CONN_MAX; i++) {
+			if (session->conn[i].state == STATE_LOGGED_IN)
+				session->conn[i].state = STATE_CLEANUP_WAIT;
+		}
+		session->r_stage = R_STAGE_SESSION_REOPEN;
+		break;
+	case STATE_IN_LOGIN:
+		if (session->r_stage == R_STAGE_SESSION_REOPEN) {
+			queue_task_t *qtask;
+
+			if (session->sync_qtask)
+				qtask = session->sync_qtask;
+			else
+				qtask = &session->reopen_qtask;
+
+			session_conn_reopen(conn, qtask, STOP_CONN_RECOVER);
+			return;
+		}
+
+		log_debug(1, "ignoring conn error in login. "
+			  "let it timeout");
+		return;
+	case STATE_XPT_WAIT:
+		log_debug(1, "ignoring conn error in XPT_WAIT. "
+			  "let connection fail on its own");
+		return;
+	case STATE_CLEANUP_WAIT:
+		log_debug(1, "ignoring conn error in CLEANUP_WAIT. "
+			  "let connection stop");
+		return;
+	default:
+		log_debug(8, "invalid state %d\n", conn->state);
+		return;
+	}
+
+	if (session->r_stage == R_STAGE_SESSION_REOPEN) {
+		session_conn_reopen(conn, &session->reopen_qtask,
+				    STOP_CONN_RECOVER);
+		return;
+	}
+}
+
+static void session_conn_error(void *data)
+{
+	struct iscsi_conn_context *conn_context = data;
+	enum iscsi_err error = *(enum iscsi_err *)conn_context->data;
+	iscsi_conn_t *conn = conn_context->conn;
+	iscsi_session_t *session = conn->session;
+
+	log_warning("Kernel reported iSCSI connection %d:%d error (%d) "
+		    "state (%d)", session->id, conn->id, error,
+		    conn->state);
+	iscsi_conn_context_put(conn_context);
+	__conn_error_handle(session, conn);
+}
+
+static void iscsi_login_eh(struct iscsi_conn *conn, struct queue_task *qtask,
+			   mgmt_ipc_err_e err)
+{
+	struct iscsi_session *session = conn->session;
+
+	log_debug(3, "iscsi_login_eh");
+	/*
+	 * Flush polls and other events
+	 */
+	iscsi_flush_context_pool(conn->session);
+
+	switch (conn->state) {
+	case STATE_XPT_WAIT:
+		switch (session->r_stage) {
+		case R_STAGE_NO_CHANGE:
+			session->t->template->ep_disconnect(conn);
+			log_debug(6, "conn_timer popped at XPT_WAIT: login");
+			/* timeout during initial connect.
+			 * clean connection. write ipc rsp */
+			session_conn_cleanup(qtask, err);
+			break;
+		case R_STAGE_SESSION_REDIRECT:
+			log_debug(6, "conn_timer popped at XPT_WAIT: "
+				  "login redirect");
+			/* timeout during initial redirect connect
+			 * clean connection. write ipc rsp */
+			__session_conn_shutdown(conn, qtask, err);
+			break;
+		case R_STAGE_SESSION_REOPEN:
+			log_debug(6, "conn_timer popped at XPT_WAIT: reopen");
+			/* timeout during reopen connect. try again */
+			session_conn_reopen(conn, qtask, 0);
+			break;
+		case R_STAGE_SESSION_CLEANUP:
+			__session_conn_shutdown(conn, qtask, err);
+			break;
+		default:
+			break;
+		}
+
+		break;
+	case STATE_IN_LOGIN:
+		switch (session->r_stage) {
+		case R_STAGE_NO_CHANGE:
+		case R_STAGE_SESSION_REDIRECT:
+			log_debug(6, "conn_timer popped at IN_LOGIN: cleanup");
+			/*
+			 * send pdu timeout during initial connect or
+			 * initial redirected connect. Clean connection
+			 * and write rsp.
+			 */
+			session_conn_shutdown(conn, qtask, err);
+			break;
+		case R_STAGE_SESSION_REOPEN:
+			log_debug(6, "conn_timer popped at IN_LOGIN: reopen");
+			session_conn_reopen(conn, qtask, STOP_CONN_RECOVER);
+			break;
+		case R_STAGE_SESSION_CLEANUP:
+			session_conn_shutdown(conn, qtask,
+					      MGMT_IPC_ERR_PDU_TIMEOUT);
+			break;
+		default:
+			break;
+		}
+
+		break;
+	default:
+		log_error("Ignoring login error %d in conn state %d.\n",
+			  err, conn->state);
+		break;
+	}
+}
+
+static void iscsi_login_timedout(void *data)
+{
+	struct queue_task *qtask = data;
+	struct iscsi_conn *conn = qtask->conn;
+
+	switch (conn->state) {
+	case STATE_XPT_WAIT:
+		iscsi_login_eh(conn, qtask, MGMT_IPC_ERR_TRANS_TIMEOUT);
+		break;
+	case STATE_IN_LOGIN:
+		iscsi_login_eh(conn, qtask, MGMT_IPC_ERR_PDU_TIMEOUT);
+		break;
+	default:
+		iscsi_login_eh(conn, qtask, MGMT_IPC_ERR_INTERNAL);
+		break;
+	}
+}
+
+static void iscsi_login_redirect(iscsi_conn_t *conn)
 {
 	iscsi_session_t *session = conn->session;
 	iscsi_login_context_t *c = &conn->login_context;
 
 	log_debug(3, "login redirect ...\n");
 
-	iscsi_queue_flush(session->queue);
-
 	if (session->r_stage == R_STAGE_NO_CHANGE)
 		session->r_stage = R_STAGE_SESSION_REDIRECT;
 
-	if (__session_conn_reopen(conn, c->qtask, STOP_CONN_RECOVER)) {
-		log_error("redirct __session_conn_reopen failed\n");
-		return 1;
-	}
+	__session_conn_reopen(conn, c->qtask, STOP_CONN_RECOVER);
+}
 
-	return 0;
+static int
+__send_nopin_rsp(iscsi_conn_t *conn, struct iscsi_nopin *rhdr, char *data)
+{
+	struct iscsi_nopout hdr;
+
+	memset(&hdr, 0, sizeof(struct iscsi_nopout));
+	hdr.opcode = ISCSI_OP_NOOP_OUT | ISCSI_OP_IMMEDIATE;
+	hdr.flags = ISCSI_FLAG_CMD_FINAL;
+	hdr.dlength[0] = rhdr->dlength[0];
+	hdr.dlength[1] = rhdr->dlength[1];
+	hdr.dlength[2] = rhdr->dlength[2];
+	memcpy(hdr.lun, rhdr->lun, 8);
+	hdr.ttt = rhdr->ttt;
+	hdr.itt = ISCSI_RESERVED_TAG;
+
+	return iscsi_io_send_pdu(conn, (struct iscsi_hdr*)&hdr,
+	       ISCSI_DIGEST_NONE, data, ISCSI_DIGEST_NONE, 0);
+}
+
+static int
+__send_nopout(iscsi_conn_t *conn)
+{
+	struct iscsi_nopout hdr;
+
+	memset(&hdr, 0, sizeof(struct iscsi_nopout));
+	hdr.opcode = ISCSI_OP_NOOP_OUT | ISCSI_OP_IMMEDIATE;
+	hdr.flags = ISCSI_FLAG_CMD_FINAL;
+	hdr.itt = 0;  /* XXX: let kernel send_pdu set for us*/
+	hdr.ttt = ISCSI_RESERVED_TAG;
+	/* we have hdr.lun reserved, and no data */
+	return iscsi_io_send_pdu(conn, (struct iscsi_hdr*)&hdr,
+		ISCSI_DIGEST_NONE, NULL, ISCSI_DIGEST_NONE, 0);
+}
+
+static void conn_nop_out_timeout(void *data)
+{
+	iscsi_conn_t *conn = (iscsi_conn_t*)data;
+	iscsi_session_t *session = conn->session;
+
+	log_warning("Nop-out timedout after %d seconds on connection %d:%d "
+		    "state (%d). Dropping session.", conn->noop_out_timeout,
+		    session->id, conn->id, conn->state);
+	/* XXX: error handle */
+	__conn_error_handle(session, conn);
+}
+
+static void conn_send_nop_out(void *data)
+{
+	iscsi_conn_t *conn = (iscsi_conn_t*)data;
+
+	__send_nopout(conn);
+
+	actor_timer(&conn->nop_out_timer, conn->noop_out_timeout*1000,
+		    conn_nop_out_timeout, conn);
+	log_debug(3, "noop out timeout timer %p start, timeout %d\n",
+		 &conn->nop_out_timer, conn->noop_out_timeout);
 }
 
 static void
@@ -1010,6 +1084,7 @@ setup_full_feature_phase(iscsi_conn_t *conn)
 	iscsi_login_context_t *c = &conn->login_context;
 	int i, rc;
 	uint32_t one = 1, zero = 0;
+	conn_login_status_e login_status;
 	struct hostparam {
 		int param;
 		int type;
@@ -1165,20 +1240,34 @@ setup_full_feature_phase(iscsi_conn_t *conn)
 	};
 
 	/* almost! entered full-feature phase */
-	if (__login_response_status(conn, c->ret) != CONN_LOGIN_SUCCESS) {
-		__session_conn_shutdown(conn, c->qtask,
-				        MGMT_IPC_ERR_LOGIN_FAILURE);
-		return;
+	login_status = __login_response_status(conn, c->ret);
+	switch (login_status) {
+	case CONN_LOGIN_FAILED:
+		goto failed;
+	case CONN_LOGIN_RETRY:
+		goto retry;
+	default:
+		; /* success - fall through */
 	}
 
 	/* check the login status */
-	if (__check_iscsi_status_class(session, conn->id, c->status_class,
-				      c->status_detail) != CONN_LOGIN_SUCCESS) {
-		__session_conn_shutdown(conn, c->qtask,
-				        MGMT_IPC_ERR_LOGIN_FAILURE);
+	login_status = __check_iscsi_status_class(session, conn->id,
+						  c->status_class,
+						  c->status_detail);
+	switch (login_status) {
+	case CONN_LOGIN_FAILED:
+		goto failed;
+	case CONN_LOGIN_IMM_REDIRECT_RETRY:
+		iscsi_login_redirect(conn);
 		return;
+	case CONN_LOGIN_IMM_RETRY:
+	case CONN_LOGIN_RETRY:
+		goto retry;
+	default:
+		; /* success - fall through */
 	}
 
+	actor_delete(&conn->login_timer);
 	/* Entered full-feature phase! */
 	for (i = 0; i < MAX_SESSION_PARAMS; i++) {
 		if (conn->id != 0 && !conntbl[i].conn_only)
@@ -1195,8 +1284,8 @@ setup_full_feature_phase(iscsi_conn_t *conn)
 				  conntbl[i].param, session->id, conn->id,
 				  rc, errno);
 
-			__session_conn_shutdown(conn, c->qtask,
-					       MGMT_IPC_ERR_LOGIN_FAILURE);
+			iscsi_login_eh(conn, c->qtask,
+				       MGMT_IPC_ERR_LOGIN_FAILURE);
 			return;
 		}
 
@@ -1208,8 +1297,8 @@ setup_full_feature_phase(iscsi_conn_t *conn)
 		if (__iscsi_host_set_param(session->t, session->hostno,
 					   hosttbl[i].param, hosttbl[i].value,
 					   hosttbl[i].type)) {
-			__session_conn_shutdown(conn, c->qtask,
-					       MGMT_IPC_ERR_LOGIN_FAILURE);
+			iscsi_login_eh(conn, c->qtask,
+				       MGMT_IPC_ERR_LOGIN_FAILURE);
 			return;
 		}
 
@@ -1219,10 +1308,9 @@ setup_full_feature_phase(iscsi_conn_t *conn)
 
 	if (ipc->start_conn(session->t->handle, session->id, conn->id,
 			    &rc) || rc) {
-		__session_conn_shutdown(conn, c->qtask,
-				       MGMT_IPC_ERR_INTERNAL);
 		log_error("can't start connection %d:%d retcode %d (%d)",
 			  session->id, conn->id, rc, errno);
+		iscsi_login_eh(conn, c->qtask, MGMT_IPC_ERR_INTERNAL);
 		return;
 	}
 
@@ -1256,19 +1344,29 @@ setup_full_feature_phase(iscsi_conn_t *conn)
 
 	/* noop_out */
 	if (conn->noop_out_interval) {
-		actor_timer(&conn->noop_out_timer, conn->noop_out_interval*1000,
-			   __conn_noop_out, conn);
-		actor_new(&conn->noop_out_timeout_timer,
-			  __conn_noop_out_timeout, conn);
+		actor_timer(&conn->nop_out_timer, conn->noop_out_interval*1000,
+			   conn_send_nop_out, conn);
 		log_debug(3, "noop out timer %p start\n",
-			  &conn->noop_out_timer);
+			  &conn->nop_out_timer);
 	}
+	return;
+
+retry:
+	session->r_stage = R_STAGE_SESSION_REOPEN;
+	session_conn_reopen(conn, c->qtask, STOP_CONN_RECOVER);
+	return;
+
+failed:
+	iscsi_login_eh(conn, c->qtask, MGMT_IPC_ERR_LOGIN_FAILURE);
+	return;	
 }
 
-static void iscsi_logout_timeout(void *data)
+static void iscsi_logout_timedout(void *data)
 {
-	iscsi_conn_t *conn = data;
+	struct iscsi_conn_context *conn_context = data;
+	struct iscsi_conn *conn = conn_context->conn;
 
+	iscsi_conn_context_put(conn_context); 
 	/*
 	 * assume we were in STATE_IN_LOGOUT or there
 	 * was some nasty error
@@ -1280,10 +1378,11 @@ static void iscsi_logout_timeout(void *data)
 static int iscsi_send_logout(iscsi_conn_t *conn)
 {
 	struct iscsi_logout hdr;
+	struct iscsi_conn_context *conn_context;
 
 	if (conn->state == STATE_IN_LOGOUT ||
 	    conn->state != STATE_LOGGED_IN)
-		return -EINVAL;
+		return EINVAL;
 
 	memset(&hdr, 0, sizeof(struct iscsi_logout));
 	hdr.opcode = ISCSI_OP_LOGOUT | ISCSI_OP_IMMEDIATE;
@@ -1293,12 +1392,21 @@ static int iscsi_send_logout(iscsi_conn_t *conn)
 
 	if (!iscsi_io_send_pdu(conn, (struct iscsi_hdr*)&hdr,
 			       ISCSI_DIGEST_NONE, NULL, ISCSI_DIGEST_NONE, 0))
-		return -EIO;
+		return EIO;
 	conn->state = STATE_IN_LOGOUT;
 
-	actor_timer(&conn->logout_timer, conn->logout_timeout * 1000,
-		    iscsi_logout_timeout, conn);
-	log_debug(3, "logout timeout timer %p start\n", &conn->logout_timer);
+	conn_context = iscsi_conn_context_get(conn, 0);
+	if (!conn_context)
+		/* unbounded logout */
+		log_warning("Could not allocate conn context for logout.");
+	else {
+		iscsi_sched_conn_context(conn_context, conn,
+					 conn->logout_timeout,
+					 EV_CONN_LOGOUT_TIMER);
+		log_debug(3, "logout timeout timer %u\n",
+			  conn->logout_timeout * 1000);
+	}
+
 	return 0;
 }
 
@@ -1306,10 +1414,10 @@ static void iscsi_recv_nop_in(iscsi_conn_t *conn, struct iscsi_hdr *hdr)
 {
 	if (hdr->ttt == ISCSI_RESERVED_TAG) {
 		/* noop out rsp */
-		actor_delete(&conn->noop_out_timeout_timer);
+		actor_delete(&conn->nop_out_timer);
 		/* schedule a new ping */
-		actor_timer(&conn->noop_out_timer, conn->noop_out_interval*1000,
-			    __conn_noop_out, conn);
+		actor_timer(&conn->nop_out_timer, conn->noop_out_interval*1000,
+			    conn_send_nop_out, conn);
 	} else /*  noop in req */
 		if (!__send_nopin_rsp(conn, (struct iscsi_nopin*)hdr,
 				      conn->data)) {
@@ -1372,15 +1480,15 @@ static void iscsi_recv_async_msg(iscsi_conn_t *conn, struct iscsi_hdr *hdr)
 	}
 }
 
-static void
-__session_conn_recv_pdu(queue_item_t *item)
+void session_conn_recv_pdu(void *data)
 {
-	iscsi_conn_t *conn = item->context;
+	struct iscsi_conn_context *conn_context = data;
+	iscsi_conn_t *conn = conn_context->conn;
 	iscsi_session_t *session = conn->session;
 	iscsi_login_context_t *c;
 	struct iscsi_hdr hdr;
 
-	conn->recv_handle = *(uintptr_t*)queue_item_data(item);
+	conn->recv_context = conn_context;
 
 	switch (conn->state) {
 	case STATE_IN_LOGIN:
@@ -1388,10 +1496,11 @@ __session_conn_recv_pdu(queue_item_t *item)
 
 		if (iscsi_login_rsp(session, c)) {
 			log_debug(1, "login_rsp ret (%d)", c->ret);
-			if (c->ret != LOGIN_REDIRECT ||
-			    iscsi_login_redirect(conn))
-				__session_conn_shutdown(conn, c->qtask,
-					MGMT_IPC_ERR_LOGIN_FAILURE);
+			if (c->ret == LOGIN_REDIRECT)
+				iscsi_login_redirect(conn);
+			else
+				iscsi_login_eh(conn, c->qtask,
+					       MGMT_IPC_ERR_LOGIN_FAILURE);
 			return;
 		}
 
@@ -1399,8 +1508,8 @@ __session_conn_recv_pdu(queue_item_t *item)
 			/* more nego. needed! */
 			conn->state = STATE_IN_LOGIN;
 			if (iscsi_login_req(session, c)) {
-				__session_conn_shutdown(conn, c->qtask,
-						MGMT_IPC_ERR_LOGIN_FAILURE);
+				iscsi_login_eh(conn, c->qtask,
+					       MGMT_IPC_ERR_LOGIN_FAILURE);
 				return;
 			}
 		} else
@@ -1432,57 +1541,54 @@ __session_conn_recv_pdu(queue_item_t *item)
 		}
 		break;
 	case STATE_XPT_WAIT:
-		recvpool_put(conn, (void *)conn->recv_handle);
+		iscsi_conn_context_put(conn_context);
 		log_debug(1, "ignoring incomming PDU in XPT_WAIT. "
 			  "let connection re-establish or fail");
 		break;
 	case STATE_CLEANUP_WAIT:
-		recvpool_put(conn, (void *)conn->recv_handle);
+		iscsi_conn_context_put(conn_context);
 		log_debug(1, "ignoring incomming PDU in XPT_WAIT. "
 			  "let connection cleanup");
 		break;
 	default:
-		recvpool_put(conn, (void *)conn->recv_handle);
+		iscsi_conn_context_put(conn_context);
 		log_error("Invalid state. Dropping PDU.\n");
 	}
 }
 
-static void
-setup_kernel_io_callouts(iscsi_conn_t *conn)
-{
-	conn->kernel_io = 1;
-	conn->send_pdu_begin = ipc->send_pdu_begin;
-	conn->send_pdu_end = ipc->send_pdu_end;
-	conn->recv_pdu_begin = ipc->recv_pdu_begin;
-	conn->recv_pdu_end = ipc->recv_pdu_end;
-	conn->send_pdu_timer_add = __send_pdu_timer_add;
-	conn->send_pdu_timer_remove = __send_pdu_timer_remove;
-}
-
-static void
-__session_conn_poll(queue_item_t *item)
+static void session_conn_poll(void *data)
 {
+	struct iscsi_conn_context *conn_context = data;
+	iscsi_conn_t *conn = conn_context->conn;
+	struct iscsi_session *session = conn->session;
 	mgmt_ipc_err_e err = MGMT_IPC_OK;
-	queue_task_t *qtask = item->context;
-	iscsi_conn_t *conn = qtask->conn;
+	queue_task_t *qtask = conn_context->data;
 	iscsi_login_context_t *c = &conn->login_context;
-	iscsi_session_t *session = conn->session;
 	int rc;
 
+	iscsi_conn_context_put(conn_context);
+
 	if (conn->state != STATE_XPT_WAIT)
 		return;
 
 	rc = session->t->template->ep_poll(conn, 1);
 	if (rc == 0) {
+		log_debug(4, "poll not connected %d", rc);
 		/* timedout: Poll again. */
-		queue_produce(session->queue, EV_CONN_POLL, qtask, 0, NULL);
-		actor_schedule(&session->mainloop);
+		conn_context = iscsi_conn_context_get(conn, 0);
+		if (!conn_context) {
+			/* while polling the recv pool should be full */
+			log_error("BUG: session_conn_poll could not get conn "
+				  "context.");
+			iscsi_login_eh(conn, qtask, MGMT_IPC_ERR_INTERNAL);
+			return;
+		}
+		conn_context->data = qtask;
+		iscsi_sched_conn_context(conn_context, conn, 0, EV_CONN_POLL);
 	} else if (rc > 0) {
 		/* connected! */
 		memset(c, 0, sizeof(iscsi_login_context_t));
 
-		actor_delete(&conn->connect_timer);
-
 		/* do not allocate new connection in case of reopen */
 		if (session->r_stage == R_STAGE_NO_CHANGE) {
 			if (conn->id == 0 &&
@@ -1521,14 +1627,12 @@ __session_conn_poll(queue_item_t *item)
 			log_error("can't bind conn %d:%d to session %d, "
 				  "retcode %d (%d)", session->id, conn->id,
 				   session->id, rc, errno);
-			err = MGMT_IPC_ERR_INTERNAL;
-			goto c_cleanup;
+			iscsi_login_eh(conn, qtask, MGMT_IPC_ERR_LOGIN_FAILURE);
+			return;
 		}
 		log_debug(3, "bound iSCSI connection %d:%d to session %d",
 			  session->id, conn->id, session->id);
 
-		setup_kernel_io_callouts(conn);
-
 		c->qtask = qtask;
 		c->cid = conn->id;
 		c->buffer = conn->data;
@@ -1537,35 +1641,22 @@ __session_conn_poll(queue_item_t *item)
 		set_exp_statsn(conn);
 
 		if (iscsi_login_begin(session, c)) {
-			err = MGMT_IPC_ERR_LOGIN_FAILURE;
-			goto c_cleanup;
+			iscsi_login_eh(conn, qtask, MGMT_IPC_ERR_LOGIN_FAILURE);
+			return;
 		}
 
 		conn->state = STATE_IN_LOGIN;
 		if (iscsi_login_req(session, c)) {
-			err = MGMT_IPC_ERR_LOGIN_FAILURE;
-			goto c_cleanup;
+			iscsi_login_eh(conn, qtask, MGMT_IPC_ERR_LOGIN_FAILURE);
+			return;
 		}
-	} else if (session->r_stage == R_STAGE_NO_CHANGE) {
-		/*
-		 * poll failed during the initial connect. Give up
-		 */
-		/* error during connect */
-		err = MGMT_IPC_ERR_TRANS_FAILURE;
-		goto cleanup;
-	} else
-		/*
-		 * poll failed on reopen
-		 */
-		queue_delayed_reopen(qtask, DEFAULT_TIME2WAIT);
+	} else {
+		log_debug(4, "poll error %d", rc);
+		iscsi_login_eh(conn, qtask, MGMT_IPC_ERR_LOGIN_FAILURE);
+	}
+
 	return;
 
-c_cleanup:
-	if (ipc->destroy_conn(session->t->handle, session->id,
-                conn->id)) {
-		log_error("can not safely destroy connection %d:%d",
-			  session->id, conn->id);
-	}
 s_cleanup:
 	if (ipc->destroy_session(session->t->handle, session->id)) {
 		log_error("can not safely destroy session %d", session->id);
@@ -1575,210 +1666,38 @@ cleanup:
 	session_conn_cleanup(qtask, err);
 }
 
-static void
-__session_conn_timer(queue_item_t *item)
+void iscsi_sched_conn_context(struct iscsi_conn_context *conn_context,
+			      struct iscsi_conn *conn, unsigned long tmo,
+			      int event)
 {
-	queue_task_t *qtask = item->context;
-	iscsi_conn_t *conn = qtask->conn;
-	iscsi_session_t *session = conn->session;
-
-	switch (conn->state) {
-	case STATE_XPT_WAIT:
-		switch (session->r_stage) {
-		case R_STAGE_NO_CHANGE:
-			session->t->template->ep_disconnect(conn);
-			log_debug(6, "conn_timer popped at XPT_WAIT: login");
-			/* timeout during initial connect.
-			 * clean connection. write ipc rsp */
-			session_conn_cleanup(qtask,
-					     MGMT_IPC_ERR_TRANS_TIMEOUT);
-			break;
-		case R_STAGE_SESSION_REDIRECT:
-			log_debug(6, "conn_timer popped at XPT_WAIT: "
-				  "login redirect");
-			/* timeout during initial redirect connect
-			 * clean connection. write ipc rsp */
-			__session_conn_shutdown(conn, qtask,
-						MGMT_IPC_ERR_TRANS_TIMEOUT);
-			break;
-		case R_STAGE_SESSION_REOPEN:
-			log_debug(6, "conn_timer popped at XPT_WAIT: reopen");
-			/* timeout during reopen connect. try again */
-			session_conn_reopen(conn, qtask, 0);
-			break;
-		case R_STAGE_SESSION_CLEANUP:
-			__session_conn_shutdown(conn, qtask,
-					        MGMT_IPC_ERR_TRANS_TIMEOUT);
-			break;
-		default:
-			break;
-		}
-
+	log_debug(7, "sched conn context %p event %d, tmo %lu",
+		  &conn_context->actor, event, tmo);
+
+	conn_context->conn = conn;
+	switch (event) {
+	case EV_CONN_RECV_PDU:
+		actor_new(&conn_context->actor, session_conn_recv_pdu,
+			  conn_context);
+		actor_schedule(&conn_context->actor);
 		break;
-	case STATE_IN_LOGIN:
-		switch (session->r_stage) {
-		case R_STAGE_NO_CHANGE:
-		case R_STAGE_SESSION_REDIRECT:
-			log_debug(6, "conn_timer popped at IN_LOGIN: cleanup");
-			/*
-			 * send pdu timeout during initial connect or
-			 * initial redirected connect. Clean connection
-			 * and write rsp.
-			 */
-			session_conn_shutdown(conn, qtask,
-					      MGMT_IPC_ERR_PDU_TIMEOUT);
-			break;
-		case R_STAGE_SESSION_REOPEN:
-			log_debug(6, "conn_timer popped at IN_LOGIN: reopen");
-			session_conn_reopen(conn, qtask, STOP_CONN_RECOVER);
-			break;
-		case R_STAGE_SESSION_CLEANUP:
-			session_conn_shutdown(conn, qtask,
-					      MGMT_IPC_ERR_PDU_TIMEOUT);
-			break;
-		default:
-			break;
-		}
-
+	case EV_CONN_ERROR:
+		actor_new(&conn_context->actor, session_conn_error,
+			  conn_context);
+		actor_schedule(&conn_context->actor);
 		break;
-	default:
-		log_debug(8, "ignoring timeout in conn state %d\n",
-			  conn->state);
+	case EV_CONN_POLL:
+		actor_new(&conn_context->actor, session_conn_poll,
+			  conn_context);
+		actor_schedule(&conn_context->actor);
 		break;
-	}
-}
-
-static void
-__conn_error_handle(iscsi_session_t *session, iscsi_conn_t *conn)
-{
-	int i;
-
-	switch (conn->state) {
-	case STATE_IN_LOGOUT:
-		actor_delete(&conn->logout_timer);
-		/* logout was requested by user */
-		if (conn->logout_qtask) {
-			session_conn_shutdown(conn, conn->logout_qtask,
-					      MGMT_IPC_OK);
-			return;
-		}
-		/* logout was from eh - fall down to cleanup */
-	case STATE_LOGGED_IN:
-		/* mark failed connection */
-		conn->state = STATE_CLEANUP_WAIT;
-
-		if (session->erl > 0) {
-			/* check if we still have some logged in connections */
-			for (i=0; i<ISCSI_CONN_MAX; i++) {
-				if (session->conn[i].state == STATE_LOGGED_IN) {
-					break;
-				}
-			}
-			if (i != ISCSI_CONN_MAX) {
-				/* FIXME: re-assign leading connection
-				 *        for ERL>0 */
-			}
-
-			break;
-		}
-
-		/* mark all connections as failed */
-		for (i=0; i<ISCSI_CONN_MAX; i++) {
-			if (session->conn[i].state == STATE_LOGGED_IN)
-				session->conn[i].state = STATE_CLEANUP_WAIT;
-		}
-		session->r_stage = R_STAGE_SESSION_REOPEN;
+	case EV_CONN_LOGOUT_TIMER:
+		actor_timer(&conn_context->actor, tmo * 1000,
+			    iscsi_logout_timedout, conn_context);
 		break;
-	case STATE_IN_LOGIN:
-		if (session->r_stage == R_STAGE_SESSION_REOPEN) {
-			queue_task_t *qtask;
-
-			if (session->sync_qtask)
-				qtask = session->sync_qtask;
-			else
-				qtask = &session->reopen_qtask;
-
-			session_conn_reopen(conn, qtask, STOP_CONN_RECOVER);
-			return;
-		}
-
-		log_debug(1, "ignoring conn error in login. "
-			  "let it timeout");
-		return;
-	case STATE_XPT_WAIT:
-		log_debug(1, "ignoring conn error in XPT_WAIT. "
-			  "let connection fail on its own");
-		return;
-	case STATE_CLEANUP_WAIT:
-		log_debug(1, "ignoring conn error in CLEANUP_WAIT. "
-			  "let connection stop");
-		return;
 	default:
-		log_debug(8, "invalid state %d\n", conn->state);
+		log_error("Invalid event type %d.", event);
 		return;
 	}
-
-	if (session->r_stage == R_STAGE_SESSION_REOPEN) {
-		session_conn_reopen(conn, &session->reopen_qtask,
-				    STOP_CONN_RECOVER);
-		return;
-	}
-}
-
-static void
-__session_conn_error(queue_item_t *item)
-{
-	uintptr_t recv_handle = *(uintptr_t *)queue_item_data(item);
-	enum iscsi_err error = *(enum iscsi_err *)recv_handle;
-	iscsi_conn_t *conn = item->context;
-	iscsi_session_t *session = conn->session;
-
-	log_warning("Kernel reported iSCSI connection %d:%d error (%d) "
-		    "state (%d)", session->id, conn->id, error,
-		    conn->state);
-	__conn_error_handle(session, conn);
-	recvpool_put(conn, (void*)recv_handle);
-}
-
-static void
-__session_mainloop(void *data)
-{
-	iscsi_session_t *session = data;
-	unsigned char item_buf[sizeof(queue_item_t) + EVENT_PAYLOAD_MAX];
-	queue_item_t *item = (queue_item_t *)(void *)item_buf;
-	int count = session->queue->count, i;
-
-	/*
-	 * grab a reference in case one of these events destroys
-	 * the session
-	 */
-	session_get(session);
-	for (i = 0; i < count; i++) {
-		if (queue_consume(session->queue, EVENT_PAYLOAD_MAX,
-				  item) == QUEUE_IS_EMPTY) {
-			log_debug(4, "%d items flushed while mainloop "
-				  "was processing", count - i);
-			break;
-		}
-
-		switch (item->event_type) {
-		case EV_CONN_RECV_PDU:
-			__session_conn_recv_pdu(item);
-			break;
-		case EV_CONN_POLL:
-			__session_conn_poll(item);
-			break;
-		case EV_CONN_TIMER:
-			__session_conn_timer(item);
-			break;
-		case EV_CONN_ERROR:
-			__session_conn_error(item);
-			break;
-		default:
-			break;
-		}
-	}
-	session_put(session);
 }
 
 iscsi_session_t*
@@ -1838,6 +1757,7 @@ session_login_task(node_rec_t *rec, queue_task_t *qtask)
 	iscsi_session_t *session;
 	iscsi_conn_t *conn;
 	struct iscsi_transport *t;
+	struct iscsi_conn_context *conn_context;
 
 	t = get_transport_by_name(rec->iface.transport_name);
 	if (!t)
@@ -1904,6 +1824,16 @@ session_login_task(node_rec_t *rec, queue_task_t *qtask)
 	conn = &session->conn[0];
 	qtask->conn = conn;
 
+	conn_context = iscsi_conn_context_get(conn, 0);
+	if (!conn_context) {
+		/* while logging the recv pool should be full */
+		log_error("BUG: session_login_task could not get conn "
+			  "context for poll.");
+		__session_destroy(session);
+		return MGMT_IPC_ERR_INTERNAL;
+	}
+	conn_context->data = qtask;
+
 	rc = session->t->template->ep_connect(conn, 1);
 	if (rc < 0 && errno != EINPROGRESS) {
 		char serv[NI_MAXSERV];
@@ -1915,17 +1845,16 @@ session_login_task(node_rec_t *rec, queue_task_t *qtask)
 
 		log_error("cannot make a connection to %s:%s (%d)",
 			 conn->host, serv, errno);
-		session_stop_conn_timers(session, 0);
 		__session_destroy(session);
 		return MGMT_IPC_ERR_TRANS_FAILURE;
 	}
-
 	conn->state = STATE_XPT_WAIT;
-	queue_produce(session->queue, EV_CONN_POLL, qtask, 0, NULL);
-	actor_schedule(&session->mainloop);
 
-	actor_timer(&conn->connect_timer, conn->login_timeout*1000,
-		    __connect_timedout, qtask);
+	iscsi_sched_conn_context(conn_context, conn, 0, EV_CONN_POLL);
+	log_debug(3, "Setting login timer %p timeout %d", &conn->login_timer,
+		 conn->login_timeout);
+	actor_timer(&conn->login_timer, conn->login_timeout * 1000,
+		    iscsi_login_timedout, qtask);
 
 	qtask->rsp.command = MGMT_IPC_SESSION_LOGIN;
 	qtask->rsp.err = MGMT_IPC_OK;
@@ -1939,10 +1868,9 @@ sync_conn(iscsi_session_t *session, uint32_t cid)
 	iscsi_conn_t *conn;
 
 	if (__session_conn_create(session, cid))
-		return -ENOMEM;
+		return ENOMEM;
 	conn = &session->conn[cid];
 
-	setup_kernel_io_callouts(conn);
 	/* TODO: must export via sysfs so we can pick this up */
 	conn->state = STATE_CLEANUP_WAIT;
 	return 0;
@@ -1977,10 +1905,8 @@ iscsi_sync_session(node_rec_t *rec, queue_task_t *qtask, uint32_t sid)
 
 	err = sync_conn(session, 0);
 	if (err) {
-		if (err == -ENOMEM)
+		if (err == ENOMEM)
 			err = MGMT_IPC_ERR_NOMEM;
-		else if (err == -ENODEV)
-			err = MGMT_IPC_ERR_NOT_FOUND;
 		else
 			err = MGMT_IPC_ERR_INVAL;
 		goto destroy_session;
diff --git a/usr/initiator.h b/usr/initiator.h
index 70b5581..489433c 100644
--- a/usr/initiator.h
+++ b/usr/initiator.h
@@ -30,7 +30,6 @@
 #include "mgmt_ipc.h"
 #include "config.h"
 #include "actor.h"
-#include "queue.h"
 #include "list.h"
 
 #define ISCSI_CONFIG_ROOT	"/etc/iscsi/"
@@ -66,18 +65,13 @@ enum iscsi_login_status {
 };
 
 typedef enum iscsi_event_e {
-	EV_UNKNOWN			= 0,
-	EV_CONN_RECV_PDU		= 1,
-	EV_CONN_POLL			= 2,
-	EV_CONN_TIMER			= 3,
-	EV_CONN_ERROR			= 4,
+	EV_UNKNOWN,
+	EV_CONN_RECV_PDU,
+	EV_CONN_POLL,
+	EV_CONN_ERROR,
+	EV_CONN_LOGOUT_TIMER,
 } iscsi_event_e;
 
-typedef struct iscsi_event {
-	queue_item_t item;
-	char payload[EVENT_PAYLOAD_MAX];
-} iscsi_event_t;
-
 struct queue_task;
 
 typedef struct iscsi_login_context {
@@ -100,49 +94,24 @@ typedef struct iscsi_login_context {
 
 struct iscsi_session;
 struct iscsi_conn;
-
-typedef void (*send_pdu_begin_f) (uint64_t transport_handle, uint32_t sid,
-                                  uint32_t cid, int hdr_size, int data_size);
-typedef int (*send_pdu_end_f) (uint64_t transport_handle, uint32_t sid,
-			       uint32_t cid, int *retcode);
-typedef int (*recv_pdu_begin_f) (uint64_t transport_handle,
-				 uintptr_t recv_handle, uintptr_t *pdu_handle,
-				 int *pdu_size);
-typedef int (*recv_pdu_end_f) (uint64_t transport_handle, uintptr_t conn_handle,
-                             uintptr_t pdu_handle);
-typedef void (*send_pdu_timer_add_f)(struct iscsi_conn *conn, int timeout);
-typedef void (*send_pdu_timer_remove_f)(struct iscsi_conn *conn);
+struct iscsi_conn_context;
 
 /* daemon's connection structure */
 typedef struct iscsi_conn {
-	struct qelem item; /* must stay at the top */
 	uint32_t id;
-	uintptr_t recv_handle;
 	struct iscsi_session *session;
 	iscsi_login_context_t login_context;
+	struct iscsi_conn_context *recv_context;
 	struct queue_task *logout_qtask;
 	char data[ISCSI_DEF_MAX_RECV_SEG_LEN];
 	char host[NI_MAXHOST];	/* scratch */
 	iscsi_conn_state_e state;
-	actor_t connect_timer;
-	actor_t send_pdu_timer;
-	actor_t logout_timer;
-
-	actor_t noop_out_timer;
-	actor_t noop_out_timeout_timer;
-
-	int send_pdu_in_progress;
 
-	int kernel_io;
-	send_pdu_begin_f send_pdu_begin;
-	send_pdu_end_f send_pdu_end;
-	recv_pdu_begin_f recv_pdu_begin;
-	recv_pdu_end_f recv_pdu_end;
-	send_pdu_timer_add_f send_pdu_timer_add;
-	send_pdu_timer_remove_f send_pdu_timer_remove;
+	actor_t login_timer;
+	actor_t nop_out_timer;
 
-#define RECVPOOL_MAX	32
-	void* recvpool[RECVPOOL_MAX];
+#define CONTEXT_POOL_MAX 32
+	struct iscsi_conn_context *context_pool[CONTEXT_POOL_MAX];
 
 	/* login state machine */
 	int current_stage;
@@ -185,11 +154,22 @@ typedef struct iscsi_conn {
 	uint32_t max_xmit_dlength;	/* the value declared by the target */
 } iscsi_conn_t;
 
+struct iscsi_conn_context {
+	struct actor actor;
+	struct iscsi_conn *conn;
+	int allocated;
+	void *data;
+};
+
 typedef struct queue_task {
 	iscsi_conn_t *conn;
 	iscsiadm_req_t req;
 	iscsiadm_rsp_t rsp;
 	int mgmt_ipc_fd;
+	int allocated : 1;
+	/* Newer request types include a
+	 * variable-length payload */
+	void *payload;
 } queue_task_t;
 
 struct iscsi_transport_template;
@@ -200,7 +180,6 @@ typedef struct iscsi_session {
 	struct list_head list;
 	uint32_t id;
 	uint32_t hostno;
-	int refcount;
 	char netdev[IFNAMSIZ];
 	struct iscsi_transport *t;
 	node_rec_t nrec; /* copy of original Node record in database */
@@ -257,12 +236,6 @@ typedef struct iscsi_session {
 
 	/* sync up fields */
 	queue_task_t *sync_qtask;
-
-	/* session's processing */
-	actor_t mainloop;
-	queue_t *queue;
-	queue_t *splice_queue;
-
 } iscsi_session_t;
 
 /*
@@ -363,8 +336,12 @@ extern int session_logout_task(iscsi_session_t *session, queue_task_t *qtask);
 extern iscsi_session_t *session_find_by_sid(int sid);
 extern iscsi_session_t *session_find_by_rec(node_rec_t *rec);
 extern int session_is_running(node_rec_t *rec);
-extern void* recvpool_get(iscsi_conn_t *conn, int ev_size);
-extern void recvpool_put(iscsi_conn_t *conn, void *handle);
+extern struct iscsi_conn_context *iscsi_conn_context_get(iscsi_conn_t *conn,
+						   int ev_size);
+extern void iscsi_conn_context_put(struct iscsi_conn_context *conn_context);
+extern void iscsi_sched_conn_context(struct iscsi_conn_context *context,
+				     struct iscsi_conn *conn, unsigned long tmo,
+				     int event);
 extern mgmt_ipc_err_e iscsi_sync_session(node_rec_t *rec, queue_task_t
 					 *tsk, uint32_t sid);
 extern mgmt_ipc_err_e iscsi_host_send_targets(queue_task_t *qtask,
diff --git a/usr/io.c b/usr/io.c
index bb06411..2928f3f 100644
--- a/usr/io.c
+++ b/usr/io.c
@@ -25,13 +25,8 @@
 #include <unistd.h>
 #include <fcntl.h>
 #include <ifaddrs.h>
-#include <sys/uio.h>
 #include <sys/poll.h>
 #include <sys/ioctl.h>
-#include <sys/param.h>
-#include <sys/socket.h>
-#include <net/if.h>
-#include <netinet/in.h>
 #include <netinet/tcp.h>
 #include <arpa/inet.h>
 
@@ -39,10 +34,8 @@
 #include "iscsi_proto.h"
 #include "initiator.h"
 #include "iscsi_ipc.h"
-#include "iscsi_sysfs.h"
 #include "log.h"
 #include "transport.h"
-#include "iscsi_settings.h"
 #include "idbm.h"
 
 #define LOG_CONN_CLOSED(conn) \
@@ -569,7 +562,7 @@ iscsi_io_send_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr,
 	/* set a timeout, since the socket calls may take a long time
 	 * to timeout on their own
 	 */
-	if (!conn->kernel_io) {
+	if (!ipc) {
 		memset(&action, 0, sizeof (struct sigaction));
 		memset(&old, 0, sizeof (struct sigaction));
 		action.sa_sigaction = NULL;
@@ -632,18 +625,16 @@ iscsi_io_send_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr,
 	else
 		pad_bytes = 0;
 
-	if (conn->kernel_io) {
-		conn->send_pdu_begin(session->t->handle, session->id,
-			conn->id, end - header,
-			ntoh24(hdr->dlength) + pad_bytes);
-		conn->send_pdu_timer_add(conn, timeout);
-	}
+	if (ipc)
+		ipc->send_pdu_begin(session->t->handle, session->id,
+				    conn->id, end - header,
+				    ntoh24(hdr->dlength) + pad_bytes);
 
 	while (header < end) {
 		vec[0].iov_base = header;
 		vec[0].iov_len = end - header;
 
-		if (!conn->kernel_io)
+		if (!ipc)
 			rc = writev(session->ctrl_fd, vec, 1);
 		else
 			rc = ipc->writev(0, vec, 1);
@@ -671,7 +662,7 @@ iscsi_io_send_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr,
 		vec[1].iov_base = (void *) &pad;
 		vec[1].iov_len = pad_bytes;
 
-		if (!conn->kernel_io)
+		if (!ipc)
 			rc = writev(session->ctrl_fd, vec, 2);
 		else
 			rc = ipc->writev(0, vec, 2);
@@ -695,10 +686,9 @@ iscsi_io_send_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr,
 		}
 	}
 
-	if (conn->kernel_io) {
-		if (conn->send_pdu_end(session->t->handle, session->id,
-				       conn->id, &rc)) {
-			conn->send_pdu_timer_remove(conn);
+	if (ipc) {
+		if (ipc->send_pdu_end(session->t->handle, session->id,
+				      conn->id, &rc)) {
 			ret = 0;
 			goto done;
 		}
@@ -707,7 +697,7 @@ iscsi_io_send_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr,
 	ret = 1;
 
       done:
-	if (!conn->kernel_io) {
+	if (!ipc) {
 		alarm(0);
 		sigaction(SIGALRM, &old, NULL);
 		timedout = 0;
@@ -732,8 +722,6 @@ iscsi_io_recv_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr,
 	char *end = data + max_data_length;
 	struct sigaction action;
 	struct sigaction old;
-	uintptr_t pdu_handle;
-	int pdu_size;
 	iscsi_session_t *session = conn->session;
 
 	memset(data, 0, max_data_length);
@@ -741,7 +729,7 @@ iscsi_io_recv_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr,
 	/* set a timeout, since the socket calls may take a long
 	 * time to timeout on their own
 	 */
-	if (!conn->kernel_io) {
+	if (!ipc) {
 		memset(&action, 0, sizeof (struct sigaction));
 		memset(&old, 0, sizeof (struct sigaction));
 		action.sa_sigaction = NULL;
@@ -751,8 +739,7 @@ iscsi_io_recv_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr,
 		timedout = 0;
 		alarm(timeout);
 	} else {
-		if (conn->recv_pdu_begin(session->ctrl_fd,
-				conn->recv_handle, &pdu_handle, &pdu_size)) {
+		if (ipc->recv_pdu_begin(conn)) {
 			failed = 1;
 			goto done;
 		}
@@ -760,7 +747,7 @@ iscsi_io_recv_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr,
 
 	/* read a response header */
 	do {
-		if (!conn->kernel_io)
+		if (!ipc)
 			rlen = read(session->ctrl_fd, header,
 					sizeof (*hdr) - h_bytes);
 		else
@@ -817,7 +804,7 @@ iscsi_io_recv_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr,
 	/* read the rest into our buffer */
 	d_bytes = 0;
 	while (d_bytes < dlength) {
-		if (!conn->kernel_io)
+		if (!ipc)
 			rlen = read(session->ctrl_fd, data + d_bytes,
 					dlength - d_bytes);
 		else
@@ -844,7 +831,7 @@ iscsi_io_recv_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr,
 	/* handle PDU data padding.
 	 * data is padded in case of kernel_io */
 	pad = dlength % PAD_WORD_LEN;
-	if (pad && !conn->kernel_io) {
+	if (pad && !ipc) {
 		int pad_bytes = pad = PAD_WORD_LEN - pad;
 		char bytes[PAD_WORD_LEN];
 
@@ -900,16 +887,14 @@ iscsi_io_recv_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr,
 	}
 
 done:
-	if (!conn->kernel_io) {
+	if (!ipc) {
 		alarm(0);
 		sigaction(SIGALRM, &old, NULL);
 	} else {
 		/* finalyze receive transaction */
-		if (conn->recv_pdu_end(session->ctrl_fd, (uintptr_t)conn,
-				pdu_handle)) {
+		if (ipc->recv_pdu_end(conn)) {
 			failed = 1;
 		}
-		conn->send_pdu_timer_remove(conn);
 	}
 
 	if (timedout || failed) {
diff --git a/usr/iscsi_ipc.h b/usr/iscsi_ipc.h
index 87324db..2172174 100644
--- a/usr/iscsi_ipc.h
+++ b/usr/iscsi_ipc.h
@@ -33,6 +33,8 @@ enum {
 	ISCSI_STRING,
 };
 
+struct iscsi_conn;
+
 /**
  * struct iscsi_ipc - Open-iSCSI Interface for Kernel IPC
  *
@@ -102,11 +104,9 @@ struct iscsi_ipc {
 
 	int (*writev) (enum iscsi_uevent_e type, struct iovec *iovp, int count);
 
-	int (*recv_pdu_begin) (uint64_t transport_handle, uintptr_t recv_handle,
-			       uintptr_t *pdu_handle, int *pdu_size);
+	int (*recv_pdu_begin) (struct iscsi_conn *conn);
 
-	int (*recv_pdu_end) (uint64_t transport_handle, uintptr_t conn_handle,
-			     uintptr_t pdu_handle);
+	int (*recv_pdu_end) (struct iscsi_conn *conn);
 };
 
 #endif /* ISCSI_IPC_H */
diff --git a/usr/iscsi_settings.h b/usr/iscsi_settings.h
index f1d7a1e..4bb7064 100644
--- a/usr/iscsi_settings.h
+++ b/usr/iscsi_settings.h
@@ -3,7 +3,7 @@
  * in the RFC. See iscsi_proto.h for those.
  */
 /* timeouts in seconds */
-#define DEF_LOGIN_TIMEO		15
+#define DEF_LOGIN_TIMEO		30
 #define DEF_LOGOUT_TIMEO	15
 #define DEF_NOOP_OUT_INTERVAL	10
 #define DEF_NOOP_OUT_TIMEO	15
diff --git a/usr/iscsi_sysfs.c b/usr/iscsi_sysfs.c
index c614192..65d463c 100644
--- a/usr/iscsi_sysfs.c
+++ b/usr/iscsi_sysfs.c
@@ -21,26 +21,13 @@
 #include <ctype.h>
 #include <string.h>
 #include <errno.h>
-#include <search.h>
 #include <dirent.h>
-#include <pwd.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <sys/stat.h>
-#include <sys/un.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <arpa/inet.h>
-#include <ifaddrs.h>
-#include <linux/types.h>
-#include <linux/unistd.h>
 
 #include "log.h"
 #include "initiator.h"
 #include "transport.h"
 #include "version.h"
 #include "iscsi_sysfs.h"
-#include "list.h"
 #include "iscsi_settings.h"
 
 #define ISCSI_TRANSPORT_DIR	"/sys/class/iscsi_transport"
@@ -64,7 +51,7 @@ int read_sysfs_file(char *filename, void *value, char *format)
 	file = fopen(filename, "r");
 	if (file) {
 		line = fgets(buffer, sizeof(buffer), file);
-		if (line)
+		if (line && strcmp(line, "<NULL>"))
 			sscanf(buffer, format, value);
 		else {
 			log_debug(5, "Could not read %s.\n", filename);
diff --git a/usr/iscsi_sysfs.h b/usr/iscsi_sysfs.h
index 827b7c1..ea6337d 100644
--- a/usr/iscsi_sysfs.h
+++ b/usr/iscsi_sysfs.h
@@ -17,7 +17,6 @@
 #ifndef ISCSI_SYSFS_H
 #define ISCSI_SYSFS_H
 
-#include <search.h>
 #include <sys/types.h>
 
 struct iscsi_session;
diff --git a/usr/iscsiadm.c b/usr/iscsiadm.c
index 1e706b9..911edf8 100644
--- a/usr/iscsiadm.c
+++ b/usr/iscsiadm.c
@@ -19,20 +19,14 @@
  * See the file COPYING included with this distribution for more details.
  */
 
-#include <ctype.h>
 #include <errno.h>
-#include <fcntl.h>
 #include <getopt.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <unistd.h>
 #include <string.h>
 #include <signal.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
 #include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/un.h>
 
 #include "initiator.h"
 #include "iscsiadm.h"
@@ -285,6 +279,7 @@ __logout_by_startup(void *data, struct session_info *info)
 	node_rec_t rec;
 	int rc = 0;
 
+	memset(&rec, 0, sizeof(node_rec_t));
 	if (iface_get_by_bind_info(db, &info->iface, &rec.iface)) {
 		/*
 		 * If someone removed the /etc/iscsi/ifaces file
@@ -409,7 +404,7 @@ logout_portal(void *data, struct session_info *info)
 
 static struct node_rec *
 create_node_record(idbm_t *db, char *targetname, int tpgt, char *ip, int port,
-		   struct iface_rec *iface)
+		   struct iface_rec *iface, int verbose)
 {
 	struct node_rec *rec;
 
@@ -429,10 +424,30 @@ create_node_record(idbm_t *db, char *targetname, int tpgt, char *ip, int port,
 		strncpy(rec->conn[0].address, ip, NI_MAXHOST);
 	memset(&rec->iface, 0, sizeof(struct iface_rec));
 	if (iface) {
-		if (iface_get_by_bind_info(db, iface, &rec->iface)) {
-			log_error("Could not find iface info for %s.",
-				  iface->name);
-			goto free_rec;
+		if (!strlen(iface->name)) {
+			if (iface_get_by_bind_info(db, iface, &rec->iface)) {
+				if (verbose)
+					log_error("Could not find iface info.");
+				goto free_rec;
+			}
+		} else if (!strcmp(iface->name, DEFAULT_IFACENAME))
+			/*
+ 			 * default is a special name and should not be used by
+			 * a real iface
+			 */
+			iface_init(&rec->iface);
+		else {
+			/*
+			 * the initial iface update will not be able to find
+			 * by bind info because there is none.
+			 */
+			iface_copy(&rec->iface, iface);
+			if (iface_conf_read(db, &rec->iface)) {
+				if (verbose)
+					log_error("Could not read iface info "
+						  "for %s.", iface->name);
+				goto free_rec;
+			}
 		}
 	}
 	return rec;
@@ -823,7 +838,6 @@ static void print_sessions_tree(idbm_t *db, struct list_head *list, int level)
 	struct session_info *curr, *prev = NULL, *tmp;
 	struct iscsi_transport *t;
 	struct iface_rec iface;
-	int rc;
 
 	list_for_each_entry(curr, list, list) {
 		if (!prev || strcmp(prev->targetname, curr->targetname)) {
@@ -857,6 +871,7 @@ static void print_sessions_tree(idbm_t *db, struct list_head *list, int level)
 		printf("\t\tInterface:\n");
 		printf("\t\t**********\n");
 		if (iface_is_bound(&curr->iface)) {
+			memset(&iface, 0, sizeof(struct iface_rec));
 			if (iface_get_by_bind_info(db, &curr->iface, &iface))
 				printf("\t\tIface Name: %s\n", UNKNOWN_VALUE);
 			else
@@ -1098,7 +1113,7 @@ static int add_static_rec(idbm_t *db, int *found, char *targetname, int tpgt,
 	strncpy(rec->conn[0].address, ip, NI_MAXHOST);
 
 	if (iface) {
-		rc = iface_conf_read(iface);
+		rc = iface_conf_read(db, iface);
 		if (rc) {
 			log_error("Could not read iface %s. Error %d",
 				  iface->name, rc);
@@ -1228,7 +1243,7 @@ do_sendtargets(idbm_t *db, discovery_rec_t *drec, struct list_head *ifaces,
 
 	/* we allow users to mix hw and sw iscsi so we have to sort it out */
 	list_for_each_entry_safe(iface, tmp, ifaces, list) {
-		rc = iface_conf_read(iface);
+		rc = iface_conf_read(db, iface);
 		if (rc) {
 			log_error("Could not read iface info for %s. "
 				  "Make sure a iface config with the file "
@@ -1324,6 +1339,8 @@ static void catch_sigint( int signo ) {
 	exit(1);
 }
 
+/* TODO merge with initiator.c implementation */
+/* And add locking */
 static int check_for_session_through_iface(struct node_rec *rec)
 {
 	int nr_found = 0;
@@ -1332,27 +1349,6 @@ static int check_for_session_through_iface(struct node_rec *rec)
 	return 0;
 }
 
-static struct node_rec *setup_rec_from_iface(struct iface_rec *iface)
-{
-	struct node_rec *rec;
-
-	rec = calloc(1, sizeof(*rec));
-	if (!rec) {
-		log_error("Could not not allocate memory to create node "
-			  "record.");
-		return NULL;
-	}
-
-	rec->tpgt = -1;
-	rec->conn[0].port = -1;
-	iface_copy(&rec->iface, iface);
-	if (iface_conf_read(&rec->iface)) {
-		free(rec);
-		rec = NULL;
-	}
-	return rec;
-}
-
 static int exec_iface_op(idbm_t *db, int op, int do_show, int info_level,
 			 struct iface_rec *iface, char *name, char *value)
 {
@@ -1368,8 +1364,8 @@ static int exec_iface_op(idbm_t *db, int op, int do_show, int info_level,
 			return EINVAL;
 		}
 
-		rec = setup_rec_from_iface(iface);
-		if (rec) {
+		rec = create_node_record(db, NULL, -1, NULL, -1, iface, 0);
+		if (rec && iface_is_bound(&rec->iface)) {
 			if (check_for_session_through_iface(rec)) {
 				rc = EBUSY;
 				goto new_fail;
@@ -1378,7 +1374,7 @@ static int exec_iface_op(idbm_t *db, int op, int do_show, int info_level,
 		}
 
 		iface_init(iface);
-		rc = iface_conf_write(iface);
+		rc = iface_conf_write(db, iface);
 		if (rc)
 			goto new_fail;
 		printf("New interface %s added\n", iface->name);
@@ -1393,23 +1389,25 @@ new_fail:
 			return EINVAL;
 		}
 
-		rec = setup_rec_from_iface(iface);
+		rec = create_node_record(db, NULL, -1, NULL, -1, iface, 1);
 		if (!rec) {
 			rc = EINVAL;
 			goto delete_fail;
 		}
 
-		if (check_for_session_through_iface(rec)) {
-			rc = EBUSY;
-			goto delete_fail;
-		}
+		if (iface_is_bound(&rec->iface)) {
+			if (check_for_session_through_iface(rec)) {
+				rc = EBUSY;
+				goto delete_fail;
+			}
 
-		/* delete node records using it first */
-		rc = __for_each_rec(db, 0, rec, NULL, idbm_delete_node);
-		if (rc && rc != ENODEV)
-			goto delete_fail;
+			/* delete node records using it first */
+			rc = __for_each_rec(db, 0, rec, NULL, idbm_delete_node);
+			if (rc && rc != ENODEV)
+				goto delete_fail;
+		}
 
-		rc = iface_conf_delete(iface);
+		rc = iface_conf_delete(db, iface);
 		if (rc)
 			goto delete_fail;
 
@@ -1427,58 +1425,60 @@ delete_fail:
 			break;
 		}
 
-		rec = setup_rec_from_iface(iface);
+		rec = create_node_record(db, NULL, -1, NULL, -1, iface, 1);
 		if (!rec) {
 			rc = EINVAL;
 			goto update_fail;
 		}
 
-		if (check_for_session_through_iface(rec)) {
-			rc = EINVAL;
-			goto update_fail;
-		}
+		if (iface_is_bound(&rec->iface)) {
+			if (check_for_session_through_iface(rec)) {
+				rc = EINVAL;
+				goto update_fail;
+			}
 
-		if (!strcmp(name, "iface.iscsi_ifacename")) {
-			log_error("Can not update iface.iscsi_ifacename. "
-				  "Delete it, and then create a new one.");
-			rc = EINVAL;
-			break;
-		}
+			if (!strcmp(name, "iface.iscsi_ifacename")) {
+				log_error("Can not update "
+					  "iface.iscsi_ifacename. Delete it, "
+					  "and then create a new one.");
+				rc = EINVAL;
+				break;
+			}
 
-		if (iface_is_bound_by_hwaddr(&rec->iface) &&
-		    !strcmp(name, "iface.net_ifacename")) {
-			log_error("Can not update interface binding from "
-				  "hwaddress to net_ifacename. ");
-			log_error("You must delete the interface and create "
-				  "a new one");
-			rc = EINVAL;
-			break;
-		}
+			if (iface_is_bound_by_hwaddr(&rec->iface) &&
+			    !strcmp(name, "iface.net_ifacename")) {
+				log_error("Can not update interface binding "
+					  "from hwaddress to net_ifacename. ");
+				log_error("You must delete the interface and "
+					  "create a new one");
+				rc = EINVAL;
+				break;
+			}
 
-		if (iface_is_bound_by_netdev(&rec->iface) &&
-		    !strcmp(name, "iface.hwaddress")) {
-			log_error("Can not update interface binding from "
-				  "net_ifacename to hwaddress. ");
-			log_error("You must delete the interface and create "
-				  "a new one");
-			rc = EINVAL;
-			break;
+			if (iface_is_bound_by_netdev(&rec->iface) &&
+			    !strcmp(name, "iface.hwaddress")) {
+				log_error("Can not update interface binding "
+					  "from net_ifacename to hwaddress. ");
+				log_error("You must delete the interface and "
+					  "create a new one");
+				rc = EINVAL;
+				break;
+			}
 		}
-
 		set_param.db = db;
 		set_param.name = name;
 		set_param.value = value;
 
+		/* pass rec's iface because it has the db values */
+		rc = iface_conf_update(db, &set_param, &rec->iface);
+		if (rc)
+			goto update_fail;
+
 		rc = __for_each_rec(db, 0, rec, &set_param,
 				    idbm_node_set_param);
 		if (rc && rc != ENODEV)
 			goto update_fail;
 
-		/* pass rec's iface because it has the db values */
-		rc = iface_conf_update(&set_param, &rec->iface);
-		if (rc)
-			goto update_fail;
-
 		printf("%s updated.\n", iface->name);
 		break;
 update_fail:
@@ -1664,6 +1664,9 @@ main(int argc, char **argv)
 
 	INIT_LIST_HEAD(&ifaces);
 	/* do not allow ctrl-c for now... */
+	memset(&sa_old, 0, sizeof(struct sigaction));
+	memset(&sa_new, 0, sizeof(struct sigaction));
+
 	sa_new.sa_handler = catch_sigint;
 	sigemptyset(&sa_new.sa_mask);
 	sa_new.sa_flags = 0;
@@ -1777,6 +1780,7 @@ main(int argc, char **argv)
 		usage(0);
 
 	config_init();
+
 	if (initiator_name[0] == '\0') {
 		log_warning("exiting due to configuration error");
 		return -1;
@@ -1938,7 +1942,8 @@ main(int argc, char **argv)
 					  iface->hwaddress, iface->ipaddress);
 		}
 
-		rec = create_node_record(db, targetname, tpgt, ip, port, iface);
+		rec = create_node_record(db, targetname, tpgt, ip, port,
+					 iface, 1);
 		if (!rec) {
 			rc = -1;
 			goto out;
@@ -1993,7 +1998,7 @@ main(int argc, char **argv)
 						 info->tpgt,
 						 info->persistent_address,
 						 info->persistent_port,
-						 &info->iface);
+						 &info->iface, 1);
 			if (!rec) {
 				rc = -1;
 				goto free_info;
diff --git a/usr/iscsid.c b/usr/iscsid.c
index 35ceaef..2472fe3 100644
--- a/usr/iscsid.c
+++ b/usr/iscsid.c
@@ -19,7 +19,6 @@
  *
  * See the file COPYING included with this distribution for more details.
  */
-#include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <getopt.h>
@@ -28,13 +27,8 @@
 #include <unistd.h>
 #include <string.h>
 #include <signal.h>
-#include <dirent.h>
-#include <sys/types.h>
-#include <sys/stat.h>
 #include <sys/mman.h>
-#include <sys/poll.h>
 #include <sys/utsname.h>
-#include <sys/signal.h>
 
 #include "iscsid.h"
 #include "mgmt_ipc.h"
@@ -46,7 +40,6 @@
 #include "idbm.h"
 #include "version.h"
 #include "iscsi_sysfs.h"
-#include "iscsi_settings.h"
 
 /* global config info */
 struct iscsi_daemon_config daemon_config;
diff --git a/usr/iscsistart.c b/usr/iscsistart.c
index 5ef05bb..5d9dc1b 100644
--- a/usr/iscsistart.c
+++ b/usr/iscsistart.c
@@ -145,22 +145,22 @@ static int check_params(char *initiatorname)
 {
 	if (!initiatorname) {
 		log_error("InitiatorName not set. Exiting %s\n", program_name);
-		return -EINVAL;
+		return EINVAL;
 	}
 
 	if (config_rec.tpgt == PORTAL_GROUP_TAG_UNKNOWN) {
 		log_error("Portal Group not set. Exiting %s\n", program_name);
-		return -EINVAL;
+		return EINVAL;
 	}
 
 	if (!strlen(config_rec.name)) {
 		log_error("TargetName not set. Exiting %s\n", program_name);
-		return -EINVAL;
+		return EINVAL;
 	}
 
 	if (!strlen(config_rec.conn[0].address)) {
 		log_error("IP Address not set. Exiting %s\n", program_name);
-		return -EINVAL;
+		return EINVAL;
 	}
 
 	return 0;
diff --git a/usr/isns.c b/usr/isns.c
index b823bf4..6048469 100644
--- a/usr/isns.c
+++ b/usr/isns.c
@@ -50,16 +50,17 @@ struct isns_task {
 	int done;
 	int retry;
 	queue_task_t *qtask;
+	struct actor actor;
 };
 
-static actor_t isns_actor;
-static queue_t *isns_queue = NULL;
 static struct sockaddr_storage ss;
 static uint16_t transaction;
 
 static char isns_address[NI_MAXHOST];
 static int isns_port = 3205, isns_listen_port, max_retry = 10000;
 
+static void isns_poll(void *data);
+
 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
 
 #define get_hdr_param(hdr, function, length, flags, transaction, sequence)	\
@@ -432,8 +433,8 @@ static int isns_task_done(struct isns_task *task)
 		task->len = length + sizeof(*hdr);
 		task->done = 0;
 
-		queue_produce(isns_queue, EV_CONN_POLL, task, 0, NULL);
-		actor_schedule(&isns_actor);
+		actor_new(&task->actor, isns_poll, task);
+		actor_schedule(&task->actor);
 		finished = 0;
 		break;
 	default:
@@ -502,8 +503,8 @@ int isns_dev_attr_query_task(queue_task_t *qtask)
 
 	qtask->rsp.command = MGMT_IPC_ISNS_DEV_ATTR_QUERY;
 
-	queue_produce(isns_queue, EV_CONN_POLL, task, 0, NULL);
-	actor_schedule(&isns_actor);
+	actor_new(&task->actor, isns_poll, task);
+	actor_schedule(&task->actor);
 
 	return MGMT_IPC_OK;
 }
@@ -532,15 +533,15 @@ void isns_handle(int listen_fd)
 	task->state = ISNS_TASK_RECV_PDU;
 	task->fd = fd;
 
-	queue_produce(isns_queue, EV_CONN_POLL, task, 0, NULL);
-	actor_schedule(&isns_actor);
+	actor_new(&task->actor, isns_poll, task);
+	actor_schedule(&task->actor);
 }
 
-static void isns_poll(queue_item_t *item)
+static void isns_poll(void *data)
 {
 	int err, finished;
 	struct pollfd pfd;
-	struct isns_task *task = item->context;
+	struct isns_task *task = data;
 	struct isns_hdr *hdr = (struct isns_hdr *) task->data;
 	uint16_t function = ntohs(hdr->function);
 
@@ -573,9 +574,8 @@ static void isns_poll(queue_item_t *item)
 						goto free_task;
 				}
 
-				queue_produce(isns_queue, EV_CONN_POLL, task, 0,
-					      NULL);
-				actor_schedule(&isns_actor);
+				actor_new(&task->actor, isns_poll, task);
+				actor_schedule(&task->actor);
 			}
 			break;
 		case ISNS_TASK_RECV_PDU:
@@ -590,9 +590,9 @@ static void isns_poll(queue_item_t *item)
 						goto free_task;
 				} else {
 					/* need to read more */
-					queue_produce(isns_queue, EV_CONN_POLL,
-						      task, 0, NULL);
-					actor_schedule(&isns_actor);
+					actor_new(&task->actor, isns_poll,
+						  task);
+					actor_schedule(&task->actor);
 				}
 			}
 		}
@@ -602,8 +602,8 @@ static void isns_poll(queue_item_t *item)
 			log_error("abort task");
 			goto abort_task;
 		} else {
-			queue_produce(isns_queue, EV_CONN_POLL, task, 0, NULL);
-			actor_schedule(&isns_actor);
+			actor_new(&task->actor, isns_poll, task);
+			actor_schedule(&task->actor);
 		}
 	}
 
@@ -615,32 +615,6 @@ free_task:
 	isns_free_task(task);
 }
 
-static void isns_control(void *data)
-{
-	int count = isns_queue->count, i;
-	int err;
-	unsigned char item_buf[sizeof(queue_item_t) + EVENT_PAYLOAD_MAX];
-	queue_item_t *item = (queue_item_t *)(void *)item_buf;
-
-	for (i = 0; i < count; i++) {
-		err = queue_consume(isns_queue, EVENT_PAYLOAD_MAX, item);
-		if (err == QUEUE_IS_EMPTY) {
-			log_debug(4, "%d items flushed while mainloop "
-				  "was processing", count - i);
-			break;
-		}
-
-		switch (item->event_type) {
-		case EV_CONN_POLL:
-			isns_poll(item);
-			break;
-		default:
-			log_error("%d unknown event type", item->event_type);
-			break;
-		}
-	}
-}
-
 static int isns_dev_register(void)
 {
 	struct isns_task *task;
@@ -658,10 +632,9 @@ static int isns_dev_register(void)
 
 	task->state = ISNS_TASK_WAIT_CONN;
 	build_dev_reg_req(task);
-	queue_produce(isns_queue, EV_CONN_POLL, task, 0, NULL);
 
-	actor_new(&isns_actor, isns_control, NULL);
-	actor_schedule(&isns_actor);
+	actor_new(&task->actor, isns_poll, task);
+	actor_schedule(&task->actor);
 
 	return 0;
 }
@@ -741,62 +714,23 @@ int isns_init(void)
 	if (!strlen(isns_address))
 		return -1;
 
-	isns_queue = queue_create(4, 4, NULL, NULL);
-	if (!isns_queue) {
-		log_error("can't create queue %m");
-		return -ENOMEM;
-	}
-
 	snprintf(port, sizeof(port), "%d", isns_port);
 	err = resolve_address(isns_address, port, &ss);
 	if (err) {
 		log_error("can't resolve address %m, %s", isns_address);
-		goto free_queue;
+		return err;
 	}
 
 	err = isns_listen_init(&fd);
 	if (err)
-		goto free_queue;
+		return err;
 
 	isns_dev_register();
 	return fd;
-
-free_queue:
-	queue_destroy(isns_queue);
-	return err;
 }
 
 void isns_exit(void)
 {
-	int err, count, i;
-	unsigned char item_buf[sizeof(queue_item_t) + EVENT_PAYLOAD_MAX];
-	queue_item_t *item = (queue_item_t *)(void *)item_buf;
-
-	if (!isns_queue)
-		return;
-
-	count = isns_queue->count;
-	/*
-	 * TODO: Add some code to gracefully shutdown.
-	 */
-	for (i = 0; i < count; i++) {
-		err = queue_consume(isns_queue, EVENT_PAYLOAD_MAX, item);
-		if (err == QUEUE_IS_EMPTY) {
-			log_debug(4, "%d items flushed while mainloop "
-				  "was processing", count - i);
-			break;
-		}
-
-		log_debug(4, "Dropping event type %d\n", item->event_type);
-		switch (item->event_type) {
-		case EV_CONN_POLL:
-			isns_free_task(item->context);
-			continue;
-		default:
-			log_error("%d unknown event type", item->event_type);
-			continue;
-		}
-	}
-
-	queue_destroy(isns_queue);
+	/* do nothing for now */
+	;
 }
diff --git a/usr/list.h b/usr/list.h
index 3e85c9b..d2e0019 100644
--- a/usr/list.h
+++ b/usr/list.h
@@ -83,4 +83,10 @@ static inline void list_del(struct list_head *entry)
 	entry->next = entry->prev = NULL;
 }
 
+static inline void list_del_init(struct list_head *entry)
+{
+	__list_del(entry->prev, entry->next);
+	INIT_LIST_HEAD(entry);
+}
+
 #endif
diff --git a/usr/login.c b/usr/login.c
index 614fd42..f0b9b09 100644
--- a/usr/login.c
+++ b/usr/login.c
@@ -1399,8 +1399,6 @@ iscsi_login_req(iscsi_session_t *session, iscsi_login_context_t *c)
 		c->ret = LOGIN_IO_ERROR;
 		goto done;
 	}
-
-	conn->state = STATE_IN_LOGIN;
 	return 0;
 
  done:
@@ -1464,8 +1462,6 @@ iscsi_login_rsp(iscsi_session_t *session, iscsi_login_context_t *c)
 		     &c->final);
 	if (c->final)
 		goto done;
-
-	conn->state = STATE_FREE;
 	return 0;
 
  done:
@@ -1501,7 +1497,6 @@ iscsi_login(iscsi_session_t *session, int cid, char *buffer, size_t bufsize,
 	iscsi_login_context_t *c = &conn->login_context;
 	int ret;
 
-	conn->kernel_io = 0;
 	/*
 	 * assume iscsi_login is only called from discovery, so it is
 	 * safe to always set to zero
diff --git a/usr/mgmt_ipc.c b/usr/mgmt_ipc.c
index fb2be80..e6d081a 100644
--- a/usr/mgmt_ipc.c
+++ b/usr/mgmt_ipc.c
@@ -21,20 +21,13 @@
  *
  * See the file COPYING included with this distribution for more details.
  */
+#include <stdlib.h>
 #include <errno.h>
 #include <unistd.h>
-
-#include <string.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <stdlib.h>
+#include <pwd.h>
 #include <sys/poll.h>
-#include <sys/socket.h>
-#include <sys/types.h>
 #include <sys/un.h>
 #include <sys/wait.h>
-#include <pwd.h>
 
 #include "iscsid.h"
 #include "idbm.h"
@@ -42,9 +35,11 @@
 #include "iscsi_ipc.h"
 #include "log.h"
 #include "transport.h"
-#include "iscsi_sysfs.h"
+
+static int	leave_event_loop = 0;
 
 #define PEERUSER_MAX	64
+#define EXTMSG_MAX	(64 * 1024)
 
 int
 mgmt_ipc_listen(void)
@@ -84,8 +79,10 @@ mgmt_ipc_close(int fd)
 }
 
 static mgmt_ipc_err_e
-mgmt_ipc_session_login(queue_task_t *qtask, node_rec_t *rec)
+mgmt_ipc_session_login(queue_task_t *qtask)
 {
+	node_rec_t *rec = &qtask->req.u.session.rec;
+
 	if (session_is_running(rec)) {
 		log_error("session [%s,%s,%d] already running.", rec->name,
 			  rec->conn[0].address, rec->conn[0].port);
@@ -96,37 +93,46 @@ mgmt_ipc_session_login(queue_task_t *qtask, node_rec_t *rec)
 }
 
 static mgmt_ipc_err_e
-mgmt_ipc_session_getstats(queue_task_t *qtask, int sid,
-			  iscsiadm_rsp_t *rsp)
+mgmt_ipc_session_getstats(queue_task_t *qtask)
 {
-	struct iscsi_transport *t;
+	int sid = qtask->req.u.session.sid;
 	iscsi_session_t *session;
+	int rc;
 
-	list_for_each_entry(t, &transports, list) {
-		list_for_each_entry(session, &t->sessions, list) {
-			if (session->id == sid) {
-				int rc;
-
-				rc = ipc->get_stats(session->t->handle,
-					session->id, session->conn[0].id,
-					(void*)&rsp->u.getstats,
-					MGMT_IPC_GETSTATS_BUF_MAX);
-				if (rc) {
-					log_error("get_stats(): IPC error %d "
-						"session [%02d]", rc, sid);
-					return MGMT_IPC_ERR_INTERNAL;
-				}
-				return MGMT_IPC_OK;
-			}
-		}
+	if (!(session = session_find_by_sid(sid)))
+		return MGMT_IPC_ERR_NOT_FOUND;
+
+	rc = ipc->get_stats(session->t->handle,
+		session->id, session->conn[0].id,
+		(void *)&qtask->rsp.u.getstats,
+		MGMT_IPC_GETSTATS_BUF_MAX);
+	if (rc) {
+		log_error("get_stats(): IPC error %d "
+			"session [%02d]", rc, sid);
+		return MGMT_IPC_ERR_INTERNAL;
 	}
 
-	return MGMT_IPC_ERR_NOT_FOUND;
+	mgmt_ipc_write_rsp(qtask, MGMT_IPC_OK);
+	return MGMT_IPC_OK;
+}
+
+static mgmt_ipc_err_e
+mgmt_ipc_send_targets(queue_task_t *qtask)
+{
+	iscsiadm_req_t *req = &qtask->req;
+	mgmt_ipc_err_e err;
+
+	err = iscsi_host_send_targets(qtask, req->u.st.host_no,
+					  req->u.st.do_login,
+					  &req->u.st.ss);
+	mgmt_ipc_write_rsp(qtask, err);
+	return MGMT_IPC_OK;
 }
 
 static mgmt_ipc_err_e
-mgmt_ipc_session_logout(queue_task_t *qtask, node_rec_t *rec)
+mgmt_ipc_session_logout(queue_task_t *qtask)
 {
+	node_rec_t *rec = &qtask->req.u.session.rec;
 	iscsi_session_t *session;
 
 	if (!(session = session_find_by_rec(rec))) {
@@ -139,22 +145,25 @@ mgmt_ipc_session_logout(queue_task_t *qtask, node_rec_t *rec)
 }
 
 static mgmt_ipc_err_e
-mgmt_ipc_session_sync(queue_task_t *qtask, node_rec_t *rec, int sid)
+mgmt_ipc_session_sync(queue_task_t *qtask)
 {
-	return iscsi_sync_session(rec, qtask, sid);
+	struct ipc_msg_session *session= &qtask->req.u.session;
+
+	return iscsi_sync_session(&session->rec, qtask, session->sid);
 }
 
 static mgmt_ipc_err_e
-mgmt_ipc_cfg_initiatorname(queue_task_t *qtask, iscsiadm_rsp_t *rsp)
+mgmt_ipc_cfg_initiatorname(queue_task_t *qtask)
 {
-	strcpy(rsp->u.config.var, dconfig->initiator_name);
-
+	strcpy(qtask->rsp.u.config.var, dconfig->initiator_name);
+	mgmt_ipc_write_rsp(qtask, MGMT_IPC_OK);
 	return MGMT_IPC_OK;
 }
 
 static mgmt_ipc_err_e
-mgmt_ipc_session_info(queue_task_t *qtask, int sid, iscsiadm_rsp_t *rsp)
+mgmt_ipc_session_info(queue_task_t *qtask)
 {
+	int sid = qtask->req.u.session.sid;
 	iscsi_session_t *session;
 	struct ipc_msg_session_state *info;
 
@@ -163,36 +172,46 @@ mgmt_ipc_session_info(queue_task_t *qtask, int sid, iscsiadm_rsp_t *rsp)
 		return MGMT_IPC_ERR_NOT_FOUND;
 	}
 
-	info = &rsp->u.session_state;
+	info = &qtask->rsp.u.session_state;
 	info->conn_state = session->conn[0].state;
 	info->session_state = session->r_stage;
+
+	mgmt_ipc_write_rsp(qtask, MGMT_IPC_OK);
 	return MGMT_IPC_OK;
 }
 
 static mgmt_ipc_err_e
-mgmt_ipc_cfg_initiatoralias(queue_task_t *qtask, iscsiadm_rsp_t *rsp)
+mgmt_ipc_cfg_initiatoralias(queue_task_t *qtask)
 {
-	strcpy(rsp->u.config.var, dconfig->initiator_alias);
-
+	strcpy(qtask->rsp.u.config.var, dconfig->initiator_alias);
+	mgmt_ipc_write_rsp(qtask, MGMT_IPC_OK);
 	return MGMT_IPC_OK;
 }
 
 static mgmt_ipc_err_e
-mgmt_ipc_cfg_filename(queue_task_t *qtask, iscsiadm_rsp_t *rsp)
+mgmt_ipc_cfg_filename(queue_task_t *qtask)
 {
-	strcpy(rsp->u.config.var, dconfig->config_file);
-
+	strcpy(qtask->rsp.u.config.var, dconfig->config_file);
+	mgmt_ipc_write_rsp(qtask, MGMT_IPC_OK);
 	return MGMT_IPC_OK;
 }
 
 static mgmt_ipc_err_e
-mgmt_ipc_conn_add(queue_task_t *qtask, int cid)
+mgmt_ipc_conn_add(queue_task_t *qtask)
 {
 	return MGMT_IPC_ERR;
 }
 
 static mgmt_ipc_err_e
-mgmt_ipc_conn_remove(queue_task_t *qtask, int cid)
+mgmt_ipc_immediate_stop(queue_task_t *qtask)
+{
+	leave_event_loop = 1;
+	mgmt_ipc_write_rsp(qtask, MGMT_IPC_OK);
+	return MGMT_IPC_OK;
+}
+
+static mgmt_ipc_err_e
+mgmt_ipc_conn_remove(queue_task_t *qtask)
 {
 	return MGMT_IPC_ERR;
 }
@@ -203,6 +222,137 @@ mgmt_ipc_isns_dev_attr_query(queue_task_t *qtask)
 	return isns_dev_attr_query_task(qtask);
 }
 
+
+static mgmt_ipc_err_e
+mgmt_ipc_host_set_param(queue_task_t *qtask)
+{
+	struct ipc_msg_set_host_param *hp = &qtask->req.u.set_host_param;
+	int err;
+
+	err = iscsi_host_set_param(hp->host_no, hp->param, hp->value);
+	mgmt_ipc_write_rsp(qtask, err);
+	return MGMT_IPC_OK;
+}
+
+/*
+ * Parse a list of strings, encoded as a 32bit
+ * length followed by the string itself (not necessarily
+ * NUL-terminated).
+ */
+static int
+mgmt_ipc_parse_strings(queue_task_t *qtask, char ***result)
+{
+	char		*data, *endp, **argv = NULL;
+	unsigned int	left, argc;
+
+again:
+	data = qtask->payload;
+	left = qtask->req.payload_len;
+	endp = NULL;
+	argc = 0;
+
+	while (left) {
+		uint32_t len;
+
+		if (left < 4)
+			return -1;
+		memcpy(&len, data, 4);
+		data += 4;
+
+		if (endp)
+			*endp = '\0';
+
+		if (len > left)
+			return -1;
+
+		if (argv) {
+			argv[argc] = (char *) data;
+			endp = data + len;
+		}
+		data += len;
+		argc++;
+	}
+
+	if (endp)
+		*endp = '\0';
+
+	if (argv == NULL) {
+		argv = malloc((argc + 1) * sizeof(char *));
+		*result = argv;
+		goto again;
+	}
+
+	argv[argc] = NULL;
+	return argc;
+}
+
+static mgmt_ipc_err_e
+mgmt_ipc_notify_common(queue_task_t *qtask,
+		mgmt_ipc_err_e (*handler)(int, char **))
+{
+	char	**argv = NULL;
+	int	argc, err = MGMT_IPC_ERR;
+
+	argc = mgmt_ipc_parse_strings(qtask, &argv);
+	if (argc > 0)
+		err = handler(argc, argv);
+
+	if (argv)
+		free(argv);
+	mgmt_ipc_write_rsp(qtask, err);
+	return MGMT_IPC_OK;
+}
+
+/* Replace these dummies as you implement them
+   elsewhere */
+static mgmt_ipc_err_e
+iscsi_discovery_add_node(int argc, char **argv)
+{
+	return MGMT_IPC_OK;
+}
+
+static mgmt_ipc_err_e
+iscsi_discovery_del_node(int argc, char **argv)
+{
+	return MGMT_IPC_OK;
+}
+
+static mgmt_ipc_err_e
+iscsi_discovery_add_portal(int argc, char **argv)
+{
+	return MGMT_IPC_OK;
+}
+
+static mgmt_ipc_err_e
+iscsi_discovery_del_portal(int argc, char **argv)
+{
+	return MGMT_IPC_OK;
+}
+
+static mgmt_ipc_err_e
+mgmt_ipc_notify_add_node(queue_task_t *qtask)
+{
+	return mgmt_ipc_notify_common(qtask, iscsi_discovery_add_node);
+}
+
+static mgmt_ipc_err_e
+mgmt_ipc_notify_del_node(queue_task_t *qtask)
+{
+	return mgmt_ipc_notify_common(qtask, iscsi_discovery_del_node);
+}
+
+static mgmt_ipc_err_e
+mgmt_ipc_notify_add_portal(queue_task_t *qtask)
+{
+	return mgmt_ipc_notify_common(qtask, iscsi_discovery_add_portal);
+}
+
+static mgmt_ipc_err_e
+mgmt_ipc_notify_del_portal(queue_task_t *qtask)
+{
+	return mgmt_ipc_notify_common(qtask, iscsi_discovery_del_portal);
+}
+
 static int
 mgmt_peeruser(int sock, char *user)
 {
@@ -287,6 +437,24 @@ mgmt_peeruser(int sock, char *user)
 #endif
 }
 
+static void
+mgmt_ipc_destroy_queue_task(queue_task_t *qtask)
+{
+	if (qtask->mgmt_ipc_fd >= 0)
+		close(qtask->mgmt_ipc_fd);
+	if (qtask->payload)
+		free(qtask->payload);
+	if (qtask->allocated)
+		free(qtask);
+}
+
+/*
+ * Send the IPC response and destroy the queue_task.
+ * The recovery code uses a qtask which is allocated as
+ * part of a larger structure, and we don't want it to
+ * get freed when we come here. This is what qtask->allocated
+ * is for.
+ */
 void
 mgmt_ipc_write_rsp(queue_task_t *qtask, mgmt_ipc_err_e err)
 {
@@ -295,135 +463,136 @@ mgmt_ipc_write_rsp(queue_task_t *qtask, mgmt_ipc_err_e err)
 	log_debug(4, "%s: rsp to fd %d", __FUNCTION__,
 		 qtask->mgmt_ipc_fd);
 
-	if (qtask->mgmt_ipc_fd < 0)
+	if (qtask->mgmt_ipc_fd < 0) {
+		mgmt_ipc_destroy_queue_task(qtask);
 		return;
+	}
 
 	qtask->rsp.err = err;
 	write(qtask->mgmt_ipc_fd, &qtask->rsp, sizeof(qtask->rsp));
 	close(qtask->mgmt_ipc_fd);
-	free(qtask);
+	mgmt_ipc_destroy_queue_task(qtask);
+}
+
+static int
+mgmt_ipc_read_data(int fd, void *ptr, size_t len)
+{
+	int	n;
+
+	while (len) {
+		n = read(fd, ptr, len);
+		if (n < 0) {
+			if (errno == EINTR)
+				continue;
+			return -EIO;
+		}
+		if (n == 0) {
+			/* Client closed connection */
+			return -EIO;
+		}
+		ptr += n;
+		len -= n;
+	}
+	return 0;
 }
 
 static int
+mgmt_ipc_read_req(queue_task_t *qtask)
+{
+	iscsiadm_req_t *req = &qtask->req;
+	int	rc;
+
+	rc = mgmt_ipc_read_data(qtask->mgmt_ipc_fd, req, sizeof(*req));
+	if (rc >= 0 && req->payload_len > 0) {
+		/* Limit what we accept */
+		if (req->payload_len > EXTMSG_MAX)
+			return -EIO;
+
+		/* Remember the allocated pointer in the
+		 * qtask - it will be freed by write_rsp.
+		 * Note: we allocate one byte in excess
+		 * so we can append a NUL byte. */
+		qtask->payload = malloc(req->payload_len + 1);
+		rc = mgmt_ipc_read_data(qtask->mgmt_ipc_fd,
+				qtask->payload,
+				req->payload_len);
+	}
+	return rc;
+}
+
+static mgmt_ipc_fn_t *	mgmt_ipc_functions[__MGMT_IPC_MAX_COMMAND] = {
+[MGMT_IPC_SESSION_LOGIN]	= mgmt_ipc_session_login,
+[MGMT_IPC_SESSION_LOGOUT]	= mgmt_ipc_session_logout,
+[MGMT_IPC_SESSION_SYNC]		= mgmt_ipc_session_sync,
+[MGMT_IPC_SESSION_STATS]	= mgmt_ipc_session_getstats,
+[MGMT_IPC_SEND_TARGETS]		= mgmt_ipc_send_targets,
+[MGMT_IPC_SESSION_INFO]		= mgmt_ipc_session_info,
+[MGMT_IPC_CONN_ADD]		= mgmt_ipc_conn_add,
+[MGMT_IPC_CONN_REMOVE]		= mgmt_ipc_conn_remove,
+[MGMT_IPC_CONFIG_INAME]		= mgmt_ipc_cfg_initiatorname,
+[MGMT_IPC_CONFIG_IALIAS]	= mgmt_ipc_cfg_initiatoralias,
+[MGMT_IPC_CONFIG_FILE]		= mgmt_ipc_cfg_filename,
+[MGMT_IPC_IMMEDIATE_STOP]	= mgmt_ipc_immediate_stop,
+[MGMT_IPC_ISNS_DEV_ATTR_QUERY]	= mgmt_ipc_isns_dev_attr_query,
+[MGMT_IPC_SET_HOST_PARAM]	= mgmt_ipc_host_set_param,
+[MGMT_IPC_NOTIFY_ADD_NODE]	= mgmt_ipc_notify_add_node,
+[MGMT_IPC_NOTIFY_DEL_NODE]	= mgmt_ipc_notify_del_node,
+[MGMT_IPC_NOTIFY_ADD_PORTAL]	= mgmt_ipc_notify_add_portal,
+[MGMT_IPC_NOTIFY_DEL_PORTAL]	= mgmt_ipc_notify_del_portal,
+};
+
+static void
 mgmt_ipc_handle(int accept_fd)
 {
-	struct sockaddr addr;
-	int fd, rc = 0, immrsp = 0;
-	iscsiadm_req_t req;
-	iscsiadm_rsp_t rsp;
+	unsigned int command;
+	int fd, err;
 	queue_task_t *qtask = NULL;
+	mgmt_ipc_fn_t *handler = NULL;
 	char user[PEERUSER_MAX];
-	socklen_t len;
-
-	memset(&rsp, 0, sizeof(rsp));
-	len = sizeof(addr);
-	if ((fd = accept(accept_fd, (struct sockaddr *) &addr, &len)) < 0) {
-		if (errno == EINTR)
-			rc = -EINTR;
-		else
-			rc = -EIO;
-		return rc;
+
+	qtask = calloc(1, sizeof(queue_task_t));
+	if (!qtask)
+		return;
+
+	if ((fd = accept(accept_fd, NULL, NULL)) < 0) {
+		free(qtask);
+		return;
 	}
 
+	qtask->allocated = 1;
+	qtask->mgmt_ipc_fd = fd;
+
 	if (!mgmt_peeruser(fd, user) || strncmp(user, "root", PEERUSER_MAX)) {
-		rsp.err = MGMT_IPC_ERR_ACCESS;
-		rc = EINVAL;
+		err = MGMT_IPC_ERR_ACCESS;
 		goto err;
 	}
 
-	if (read(fd, &req, sizeof(req)) != sizeof(req)) {
-		rc = -EIO;
-		close(fd);
-		return rc;
-	}
-	rsp.command = req.command;
-
-	qtask = calloc(1, sizeof(queue_task_t));
-	if (!qtask) {
-		rsp.err = MGMT_IPC_ERR_NOMEM;
-		rc = -ENOMEM;
-		goto err;
+	if (mgmt_ipc_read_req(qtask) < 0) {
+		mgmt_ipc_destroy_queue_task(qtask);
+		return;
 	}
-	memcpy(&qtask->req, &req, sizeof(iscsiadm_req_t));
-	qtask->mgmt_ipc_fd = fd;
 
-	switch(req.command) {
-	case MGMT_IPC_SESSION_LOGIN:
-		rsp.err = mgmt_ipc_session_login(qtask, &req.u.session.rec);
-		break;
-	case MGMT_IPC_SESSION_LOGOUT:
-		rsp.err = mgmt_ipc_session_logout(qtask, &req.u.session.rec);
-		break;
-	case MGMT_IPC_SESSION_SYNC:
-		rsp.err = mgmt_ipc_session_sync(qtask, &req.u.session.rec,
-						req.u.session.sid);
-		break;
-	case MGMT_IPC_SESSION_STATS:
-		rsp.err = mgmt_ipc_session_getstats(qtask, req.u.session.sid,
-						    &rsp);
-		immrsp = 1;
-		break;
-	case MGMT_IPC_SEND_TARGETS:
-		rsp.err = iscsi_host_send_targets(qtask, req.u.st.host_no,
-						  req.u.st.do_login,
-						  &req.u.st.ss);
-		immrsp = 1;
-		break;
-	case MGMT_IPC_SESSION_INFO:
-		rsp.err = mgmt_ipc_session_info(qtask, req.u.session.sid,
-						&rsp);
-		immrsp = 1;
-		break;
-	case MGMT_IPC_CONN_ADD:
-		rsp.err = mgmt_ipc_conn_add(qtask, req.u.conn.cid);
-		break;
-	case MGMT_IPC_CONN_REMOVE:
-		rsp.err = mgmt_ipc_conn_remove(qtask, req.u.conn.cid);
-		break;
-	case MGMT_IPC_CONFIG_INAME:
-		rsp.err = mgmt_ipc_cfg_initiatorname(qtask, &rsp);
-		immrsp = 1;
-		break;
-	case MGMT_IPC_CONFIG_IALIAS:
-		rsp.err = mgmt_ipc_cfg_initiatoralias(qtask, &rsp);
-		immrsp = 1;
-		break;
-	case MGMT_IPC_CONFIG_FILE:
-		rsp.err = mgmt_ipc_cfg_filename(qtask, &rsp);
-		immrsp = 1;
-		break;
-	case MGMT_IPC_IMMEDIATE_STOP:
-		rsp.err = MGMT_IPC_OK;
-		immrsp = 1;
-		rc = 1;
-		break;
-	case MGMT_IPC_ISNS_DEV_ATTR_QUERY:
-		rsp.err = mgmt_ipc_isns_dev_attr_query(qtask);
-		break;
-	case MGMT_IPC_SET_HOST_PARAM:
-		rsp.err = iscsi_host_set_param(req.u.set_host_param.host_no,
-						req.u.set_host_param.param,
-						req.u.set_host_param.value);
-		immrsp = 1;
-		break;
-	default:
+	command = qtask->req.command;
+	qtask->rsp.command = command;
+
+	if (0 <= command && command < __MGMT_IPC_MAX_COMMAND)
+		handler = mgmt_ipc_functions[command];
+	if (handler != NULL) {
+		/* If the handler returns OK, this means it
+		 * already sent the reply. */
+		err = handler(qtask);
+		if (err == MGMT_IPC_OK)
+			return;
+	} else {
 		log_error("unknown request: %s(%d) %u",
-			  __FUNCTION__, __LINE__, req.command);
-		rsp.err = MGMT_IPC_ERR_INVALID_REQ;
-		immrsp = 1;
-		break;
+			  __FUNCTION__, __LINE__, command);
+		err = MGMT_IPC_ERR_INVALID_REQ;
 	}
 
-	if (rsp.err == MGMT_IPC_OK && !immrsp)
-		return 0;
-
 err:
-	if (write(fd, &rsp, sizeof(rsp)) != sizeof(rsp))
-		rc = -EIO;
-	close(fd);
-	if (qtask)
-		free(qtask);
-	return rc;
+	/* This will send the response, close the
+	 * connection and free the qtask */
+	mgmt_ipc_write_rsp(qtask, err);
 }
 
 static int reap_count;
@@ -477,7 +646,8 @@ void event_loop(struct iscsi_ipc *ipc, int control_fd, int mgmt_ipc_fd,
 		poll_array[POLL_ISNS].events = POLLIN;
 	}
 
-	while (1) {
+	leave_event_loop = 0;
+	while (!leave_event_loop) {
 		res = poll(poll_array, POLL_MAX, ACTOR_RESOLUTION);
 		if (res > 0) {
 			log_debug(6, "poll result %d", res);
@@ -485,8 +655,7 @@ void event_loop(struct iscsi_ipc *ipc, int control_fd, int mgmt_ipc_fd,
 				ipc->ctldev_handle();
 
 			if (poll_array[POLL_IPC].revents)
-				if (mgmt_ipc_handle(mgmt_ipc_fd) == 1)
-					break;
+				mgmt_ipc_handle(mgmt_ipc_fd);
 			if (poll_array[POLL_ISNS].revents)
 				isns_handle(isns_fd);
 
diff --git a/usr/mgmt_ipc.h b/usr/mgmt_ipc.h
index 3fa7521..f7d3e61 100644
--- a/usr/mgmt_ipc.h
+++ b/usr/mgmt_ipc.h
@@ -65,6 +65,12 @@ typedef enum iscsiadm_cmd {
 	MGMT_IPC_ISNS_DEV_ATTR_QUERY	= 14,
 	MGMT_IPC_SEND_TARGETS		= 15,
 	MGMT_IPC_SET_HOST_PARAM		= 16,
+	MGMT_IPC_NOTIFY_ADD_NODE	= 17,
+	MGMT_IPC_NOTIFY_DEL_NODE	= 18,
+	MGMT_IPC_NOTIFY_ADD_PORTAL	= 19,
+	MGMT_IPC_NOTIFY_DEL_PORTAL	= 20,
+
+	__MGMT_IPC_MAX_COMMAND
 } iscsiadm_cmd_e;
 
 typedef enum iscsi_conn_state_e {
@@ -87,6 +93,7 @@ typedef enum iscsi_session_r_stage_e {
 /* IPC Request */
 typedef struct iscsiadm_req {
 	iscsiadm_cmd_e command;
+	uint32_t payload_len;
 
 	union {
 		/* messages */
@@ -139,6 +146,9 @@ typedef struct iscsiadm_rsp {
 	} u;
 } iscsiadm_rsp_t;
 
+struct queue_task;
+typedef mgmt_ipc_err_e	mgmt_ipc_fn_t(struct queue_task *);
+
 struct iscsi_ipc *ipc;
 
 void need_reap(void);
diff --git a/usr/netlink.c b/usr/netlink.c
index 12cb7f7..65800ab 100644
--- a/usr/netlink.c
+++ b/usr/netlink.c
@@ -668,8 +668,7 @@ kstart_conn(uint64_t transport_handle, uint32_t sid, uint32_t cid,
 }
 
 static int
-krecv_pdu_begin(uint64_t transport_handle, uintptr_t recv_handle,
-		uintptr_t *pdu_handle, int *pdu_size)
+krecv_pdu_begin(struct iscsi_conn *conn)
 {
 	log_debug(7, "in %s", __FUNCTION__);
 
@@ -677,19 +676,17 @@ krecv_pdu_begin(uint64_t transport_handle, uintptr_t recv_handle,
 		log_error("recv's begin state machine bug?");
 		return -EIO;
 	}
-	recvbuf = (void*)recv_handle + sizeof(struct iscsi_uevent);
+	recvbuf = conn->recv_context->data + sizeof(struct iscsi_uevent);
 	recvlen = 0;
-	*pdu_handle = recv_handle;
 
 	log_debug(3, "recv PDU began, pdu handle 0x%p",
-		  (void*)*pdu_handle);
+		  recvbuf);
 
 	return 0;
 }
 
 static int
-krecv_pdu_end(uint64_t transport_handle, uintptr_t conn_handle,
-	      uintptr_t pdu_handle)
+krecv_pdu_end(struct iscsi_conn *conn)
 {
 	log_debug(7, "in %s", __FUNCTION__);
 
@@ -699,9 +696,10 @@ krecv_pdu_end(uint64_t transport_handle, uintptr_t conn_handle,
 	}
 
 	log_debug(3, "recv PDU finished for pdu handle 0x%p",
-		  (void*)pdu_handle);
+		  recvbuf);
 
-	recvpool_put((void*)conn_handle, (void*)pdu_handle);
+	iscsi_conn_context_put(conn->recv_context);
+	conn->recv_context = NULL;
 	recvbuf = NULL;
 	return 0;
 }
@@ -852,9 +850,9 @@ static int ctldev_handle(void)
 	struct iscsi_transport *t;
 	iscsi_session_t *session = NULL;
 	iscsi_conn_t *conn = NULL;
-	uintptr_t recv_handle;
 	char nlm_ev[NLMSG_SPACE(sizeof(struct iscsi_uevent))];
 	struct nlmsghdr *nlh;
+	struct iscsi_conn_context *conn_context;
 	int ev_size;
 
 	log_debug(7, "in %s", __FUNCTION__);
@@ -915,41 +913,41 @@ verify_conn:
 	}
 
 	ev_size = nlh->nlmsg_len - NLMSG_ALIGN(sizeof(struct nlmsghdr));
-	recv_handle = (uintptr_t)recvpool_get(conn, ev_size);
-	if (!recv_handle) {
+	conn_context = iscsi_conn_context_get(conn, ev_size);
+	if (!conn_context) {
 		/* retry later */
-		log_error("Can not allocate memory for receive handle.");
+		log_error("Can not allocate memory for receive context.");
 		return -ENOMEM;
 	}
 
 	log_debug(6, "message real length is %d bytes, recv_handle %p",
-		nlh->nlmsg_len, (void*)recv_handle);
+		nlh->nlmsg_len, conn_context->data);
 
-	if ((rc = nlpayload_read(ctrl_fd, (void*)recv_handle,
+	if ((rc = nlpayload_read(ctrl_fd, conn_context->data,
 				ev_size, 0)) < 0) {
-		recvpool_put(conn, (void*)recv_handle);
+		iscsi_conn_context_put(conn_context);
 		log_error("can not read from NL socket, error %d", rc);
 		/* retry later */
 		return rc;
 	}
 
+	/*
+	 * we sched these events because the handlers could call back
+	 * into ctldev_handle
+	 */
 	switch (ev->type) {
 	case ISCSI_KEVENT_RECV_PDU:
-		/* produce an event, so session manager will handle */
-		queue_produce(session->queue, EV_CONN_RECV_PDU, conn,
-			sizeof(uintptr_t), &recv_handle);
-		actor_schedule(&session->mainloop);
+		iscsi_sched_conn_context(conn_context, conn, 0,
+					 EV_CONN_RECV_PDU);
 		break;
 	case ISCSI_KEVENT_CONN_ERROR:
-		/* produce an event, so session manager will handle */
-		memcpy((void *)recv_handle, &ev->r.connerror.error,
+		memcpy(conn_context->data, &ev->r.connerror.error,
 			sizeof(ev->r.connerror.error));
-		queue_produce(session->queue, EV_CONN_ERROR, conn,
-			sizeof(uintptr_t), &recv_handle);
-		actor_schedule(&session->mainloop);
+		iscsi_sched_conn_context(conn_context, conn, 0,
+					 EV_CONN_ERROR);
 		break;
 	default:
-		recvpool_put(conn, (void*)recv_handle);
+		iscsi_conn_context_put(conn_context);
 		log_error("unknown kernel event %d", ev->type);
 		return -EEXIST;
 	}
diff --git a/usr/queue.c b/usr/queue.c
deleted file mode 100644
index 323bc6f..0000000
--- a/usr/queue.c
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- * iSCSI event queue
- *
- * Copyright (C) 2004 Dmitry Yusupov, Alex Aizman
- * maintained by open-iscsi@googlegroups.com
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * See the file COPYING included with this distribution for more details.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <search.h>
-#include "queue.h"
-#include "log.h"
-#include "actor.h"
-
-queue_t*
-queue_create(int pages_initial, int pages_max, queued_f queued,
-	     void *queued_data)
-{
-	queue_t *queue;
-
-	if ((queue = malloc(sizeof(queue_t))) == NULL) {
-		log_error("out of memory when allocating queue_t");
-		return NULL;
-	}
-
-	queue->queued_func = queued;
-	queue->queued_data = queued_data;
-	queue->pages_current = pages_initial;
-	queue->start_ptr = malloc(queue->pages_current * QUEUE_BUF_SIZE);
-	if (queue->start_ptr == NULL) {
-		log_error("out of memory when allocating queue's pages");
-		free(queue);
-		return NULL;
-	}
-	memset(queue->start_ptr, 0, queue->pages_current * QUEUE_BUF_SIZE);
-	queue->head_ptr = queue->tail_ptr = queue->start_ptr;
-	queue->end_ptr = (char *)queue->start_ptr +
-		queue->pages_current * QUEUE_BUF_SIZE;
-	queue->pages_initial = pages_initial;
-	queue->pages_max = pages_max;
-	queue->list_head.q_forw = &queue->list_head;
-	queue->list_head.q_back = &queue->list_head;
-	queue->count = 0;
-
-	return queue;
-}
-
-void
-queue_destroy(queue_t* queue)
-{
-	if (queue->list_head.q_forw != &queue->list_head) {
-		log_error("destroying non-empty queue 0x%p", queue);
-	}
-	free(queue->start_ptr);
-	free(queue);
-}
-
-static queue_status_e
-__io_queue_grow(queue_t *queue)
-{
-	void *newbuf, *oldbuf;
-	struct qelem *item;
-	queue_item_t *elem;
-
-	log_debug(7, "queue 0x%p:%d is growing", queue, queue->pages_current);
-
-	newbuf = malloc((queue->pages_current + 1) * QUEUE_BUF_SIZE);
-	if (newbuf == NULL) {
-		return QUEUE_OUT_OF_MEMORY;
-	}
-	memcpy(newbuf, queue->start_ptr, queue->pages_current * QUEUE_BUF_SIZE);
-	oldbuf = queue->start_ptr;
-
-	/* adjust queue sizes */
-	queue->start_ptr = newbuf;
-	queue->end_ptr = (char *)newbuf +
-			(queue->pages_current + 1) * QUEUE_BUF_SIZE;
-	queue->tail_ptr = (char *)newbuf + ((char *)queue->tail_ptr -
-					    (char *)oldbuf);
-	queue->head_ptr = (char *)newbuf + ((char *)queue->head_ptr -
-					    (char *)oldbuf);
-	queue->list_head.q_forw = (struct qelem *) (void *)((char *)newbuf +
-			((char *)queue->list_head.q_forw - (char *)oldbuf));
-	queue->list_head.q_back = (struct qelem *) (void *)((char *)newbuf +
-			((char *)queue->list_head.q_back - (char *)oldbuf));
-	/* adjust queue list */
-	for (item = queue->list_head.q_forw;
-	     item != queue->list_head.q_forw; item = item->q_forw) {
-		elem = (queue_item_t *)item;
-		if (elem->item.q_forw != &queue->list_head) {
-			elem->item.q_forw =
-				(struct qelem *)(void *)((char *)newbuf +
-				 ((char *)elem->item.q_forw - (char *)oldbuf));
-		}
-		if (elem->item.q_back != &queue->list_head) {
-			elem->item.q_back =
-				(struct qelem *) (void *)((char *)newbuf +
-				 ((char *)elem->item.q_back - (char *)oldbuf));
-		}
-	}
-	free(oldbuf);
-	queue->pages_current++;
-
-	return QUEUE_OK;
-}
-
-queue_status_e
-queue_consume(queue_t *queue, int data_max_size, queue_item_t *item)
-{
-	int real_size;
-	queue_item_t *elem;
-
-	if (queue->list_head.q_forw == &queue->list_head) {
-		if (queue->count)
-			log_error("queue integrety lost! Bug?");
-		return QUEUE_IS_EMPTY;
-	}
-	elem = (queue_item_t *)queue->list_head.q_forw;
-	if (elem->data_size > data_max_size) {
-		return QUEUE_NOT_ENOUGH_SPACE;
-	}
-	remque(&elem->item);
-	real_size = elem->data_size + sizeof(queue_item_t);
-	if (queue->head_ptr == elem) {
-		queue->head_ptr = (char *)queue->head_ptr + real_size;
-		log_debug(7,
-			"event_type: %d removing from the head: "
-			"0x%p:0x%p:0x%p:0x%p elem 0x%p length %d",
-			elem->event_type,
-			queue->start_ptr,
-			queue->head_ptr,
-			queue->tail_ptr,
-			queue->end_ptr,
-			elem,
-			real_size);
-	} else if ((char *)queue->tail_ptr - real_size == (char*)elem) {
-		queue->tail_ptr = (char *)queue->tail_ptr - real_size;
-		log_debug(7,
-			"event_type: %d removing from the tail: "
-			"0x%p:0x%p:0x%p:0x%p elem 0x%p length %d",
-			elem->event_type,
-			queue->start_ptr,
-			queue->head_ptr,
-			queue->tail_ptr,
-			queue->end_ptr,
-			elem,
-			real_size);
-	} else {
-		log_debug(7,
-			"event_type: %d removing from the list: "
-			"0x%p:0x%p:0x%p:0x%p elem 0x%p length %d",
-			elem->event_type,
-			queue->start_ptr,
-			queue->head_ptr,
-			queue->tail_ptr,
-			queue->end_ptr,
-			elem,
-			real_size);
-	}
-	memcpy(item, elem, sizeof(queue_item_t));
-	memcpy(queue_item_data(item), queue_item_data(elem), elem->data_size);
-
-	if (queue->list_head.q_forw == &queue->list_head) {
-		/* reset buffer pointers just to be clean */
-		queue->head_ptr = queue->tail_ptr = queue->start_ptr;
-	}
-
-	queue->count--;
-
-	return QUEUE_OK;
-}
-
-void*
-queue_item_data (queue_item_t *item)
-{
-	return (char *)item + sizeof(queue_item_t);
-}
-
-queue_status_e
-queue_produce(queue_t *queue, int event_type, void *context,
-	      const int data_size, void *data)
-{
-	int real_size = data_size + sizeof(queue_item_t);
-	queue_item_t *elem;
-
-try_again:
-	if ((char *)queue->tail_ptr + real_size <= (char *)queue->end_ptr) {
-		elem = queue->tail_ptr;
-		queue->tail_ptr = (void *)((char *)queue->tail_ptr + real_size);
-		log_debug(7, "event_type: %d adding to the tail: "
-			"0x%p:0x%p:0x%p:0x%p elem 0x%p length %d",
-			event_type,
-			queue->start_ptr,
-			queue->head_ptr,
-			queue->tail_ptr,
-			queue->end_ptr,
-			elem,
-			real_size);
-	} else if ((char *)queue->head_ptr - real_size >=
-					(char *)queue->start_ptr) {
-		elem = (void *)((char *)queue->head_ptr - real_size);
-		queue->head_ptr = elem;
-		log_debug(7, "event_type: %d adding to the head: "
-			"0x%p:0x%p:0x%p:0x%p length %d",
-			event_type,
-			queue->start_ptr,
-			queue->head_ptr,
-			queue->tail_ptr,
-			queue->end_ptr,
-			real_size);
-	} else {
-		queue_status_e status;
-
-		if (queue->pages_current >= queue->pages_max) {
-			return QUEUE_IS_FULL;
-		}
-
-		/* grow */
-		status = __io_queue_grow(queue);
-		if (status != QUEUE_OK) {
-			return status;
-		}
-
-		goto try_again;
-	}
-	elem->data_size = data_size;
-	elem->event_type = event_type;
-	elem->context = context;
-	memcpy(queue_item_data(elem), data, data_size);
-	insque(&elem->item, queue->list_head.q_back);
-
-	if (queue->queued_func)
-		queue->queued_func(queue->queued_data, event_type);
-
-	queue->count++;
-
-	return QUEUE_OK;
-}
-
diff --git a/usr/queue.h b/usr/queue.h
deleted file mode 100644
index 6abe613..0000000
--- a/usr/queue.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * iSCSI event queue
- *
- * Copyright (C) 2004 Dmitry Yusupov, Alex Aizman
- * maintained by open-iscsi@googlegroups.com
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * See the file COPYING included with this distribution for more details.
- */
-
-#ifndef QUEUE_H
-#define QUEUE_H
-
-#include "types.h"
-
-#define QUEUE_BUF_SIZE			4096
-#define EVENT_PAYLOAD_MAX		(DATASEG_MAX+HDRSEG_MAX)
-
-typedef enum queue_status_e {
-	QUEUE_OK		= 0,
-	QUEUE_IS_FULL		= 1,
-	QUEUE_IS_EMPTY		= 2,
-	QUEUE_OUT_OF_MEMORY	= 3,
-	QUEUE_NOT_ENOUGH_SPACE	= 4
-} queue_status_e;
-
-typedef struct queue_item_t {
-	struct qelem	item;
-	int		event_type;
-	int		data_size;
-	void		*context;
-} queue_item_t;
-
-typedef void (*queued_f) (void *data, int event_type);
-
-typedef struct queue_t {
-	void				*start_ptr;
-	void				*end_ptr;
-	void				*head_ptr;
-	void				*tail_ptr;
-	unsigned int			pages_initial;
-	unsigned int			pages_max;
-	unsigned int			pages_current;
-	struct qelem			list_head;
-	queued_f			queued_func;
-	void				*queued_data;
-	int				count;
-} queue_t;
-
-extern queue_t* queue_create(int pages_initial, int pages_max,
-				queued_f queued_func, void *queued_data);
-extern void queue_destroy(queue_t *queue);
-extern void* queue_item_data(queue_item_t *item);
-extern queue_status_e queue_produce(queue_t* queue, int event_type,
-	    void *context, const int data_size, void *data);
-extern queue_status_e queue_consume(queue_t *queue, int	data_max_size,
-				    queue_item_t *item);
-
-#endif /* QUEUE_H */
diff --git a/usr/strings.c b/usr/strings.c
index e885685..17829ee 100644
--- a/usr/strings.c
+++ b/usr/strings.c
@@ -165,13 +165,13 @@ int
 append_sprintf(struct string_buffer *s, const char *format, ...)
 {
 	va_list args;
-	size_t appended;
-	size_t available = s->allocated_length - s->data_length - 1;
+	size_t appended, available;
 	int ret = 0;
 
 	va_start(args, format);
 
 	for (;;) {
+		available = s->allocated_length - s->data_length - 1;
 		appended = vsnprintf(s->buffer + s->data_length, available,
 				     format, args);
 
diff --git a/usr/transport.c b/usr/transport.c
index fd47b8f..8fcc8c7 100644
--- a/usr/transport.c
+++ b/usr/transport.c
@@ -70,5 +70,5 @@ int set_transport_template(struct iscsi_transport *t)
 	}
 
 	log_error("Could not fund uspace transport for %s\n", t->name);
-	return -ENOSYS;
+	return ENOSYS;
 }
diff --git a/usr/types.h b/usr/types.h
index 63dd5cc..77e3f97 100644
--- a/usr/types.h
+++ b/usr/types.h
@@ -10,10 +10,6 @@
 #include <netinet/in.h>
 #include <stdint.h>
 #include <sys/types.h>
-#include <search.h>
-
-#define DATASEG_MAX	8192
-#define HDRSEG_MAX	48+4
 
 /*
  * using the __be types allows stricter static
diff --git a/usr/util.c b/usr/util.c
index ce8333b..d675b83 100644
--- a/usr/util.c
+++ b/usr/util.c
@@ -2,15 +2,9 @@
 #include <unistd.h>
 #include <stdlib.h>
 #include <stdio.h>
-#include <ctype.h>
 #include <string.h>
 #include <errno.h>
-#include <pwd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
 #include <sys/un.h>
-#include <linux/types.h>
-#include <linux/unistd.h>
 
 #include "log.h"
 #include "actor.h"
@@ -117,6 +111,11 @@ static mgmt_ipc_err_e iscsid_connect(int *fd)
 			/* Connection established */
 			return MGMT_IPC_OK;
 
+		/* If iscsid isn't there, there's no sense
+		 * in retrying. */
+		if (errno == ECONNREFUSED)
+			break;
+
 		/*
 		 * Delay before trying again
 		 */
diff --git a/utils/Makefile b/utils/Makefile
index b376129..2c7e891 100644
--- a/utils/Makefile
+++ b/utils/Makefile
@@ -9,4 +9,9 @@ iscsi-iname: md5.o iscsi-iname.o
 	$(CC) $(CFLAGS) $^ $(DBM_LIB) -o $@
 
 clean:
-	rm -f *.o $(PROGRAMS)
+	rm -f *.o $(PROGRAMS) .depend
+
+depend:
+	gcc $(CFLAGS) -M `ls *.c` > .depend
+
+-include .depend
diff --git a/utils/fwparam_ibft/Makefile b/utils/fwparam_ibft/Makefile
index ee090c1..86e190e 100644
--- a/utils/fwparam_ibft/Makefile
+++ b/utils/fwparam_ibft/Makefile
@@ -5,7 +5,13 @@ PROGRAMS = fwparam_ibft
 
 all: $(PROGRAMS)
 
-fwparam_ibft: fwparam_ibft.c fwparam_ibft.h
-	$(CC) $(CFLAGS) $^ -o $@
+fwparam_ibft: fwparam_ibft.o
+	$(CC) $(CFLAGS) $< -o $@
+
 clean:
-	rm -f *.o $(PROGRAMS)
+	rm -f *.o $(PROGRAMS) .depend
+
+depend:
+	gcc $(CFLAGS) -M `ls *.c` > .depend
+
+-include .depend
