diff --git a/include/iet_u.h b/include/iet_u.h
index 6fdc716..3bf2d30 100644
--- a/include/iet_u.h
+++ b/include/iet_u.h
@@ -1,7 +1,7 @@
 #ifndef _IET_U_H
 #define _IET_U_H
 
-#define IET_VERSION_STRING	"1.4.20.2"
+#define IET_VERSION_STRING	"1.4.20.3"
 
 /* The maximum length of 223 bytes in the RFC. */
 #define ISCSI_NAME_LEN	256
diff --git a/kernel/block-io.c b/kernel/block-io.c
index 941e4f0..e24f1ca 100644
--- a/kernel/block-io.c
+++ b/kernel/block-io.c
@@ -120,7 +120,7 @@ blockio_make_request(struct iet_volume *volume, struct tio *tio, int rw)
 	}
 
 	blk_start_plug(&plug);
-  
+
 	/* Walk the list, submitting bios 1 by 1 */
 	while (tio_bio) {
 		bio = tio_bio;
diff --git a/kernel/conn.c b/kernel/conn.c
index c7b8ea1..f72b366 100644
--- a/kernel/conn.c
+++ b/kernel/conn.c
@@ -44,7 +44,7 @@ void conn_info_show(struct seq_file *seq, struct iscsi_session *session)
 		switch (sk->sk_family) {
 		case AF_INET:
 			snprintf(buf, sizeof(buf),
-				 "%pI4", inet_sk(sk)->inet_daddr);
+				 "%pI4", &inet_sk(sk)->inet_daddr);
 			break;
 		case AF_INET6:
 			snprintf(buf, sizeof(buf), "[%pI6]",
@@ -241,8 +241,6 @@ int conn_add(struct iscsi_session *session, struct conn_info *info)
 		conn_close(conn);
 
 	err = iet_conn_alloc(session, info);
-	if (!err && conn)
-		err = -EEXIST;
 
 	return err;
 }
diff --git a/kernel/iscsi.c b/kernel/iscsi.c
index 05197f2..7a832e6 100644
--- a/kernel/iscsi.c
+++ b/kernel/iscsi.c
@@ -384,13 +384,29 @@ void send_scsi_rsp(struct iscsi_cmnd *req, void (*func)(struct iscsi_cmnd *))
 void send_data_rsp(struct iscsi_cmnd *req, void (*func)(struct iscsi_cmnd *))
 {
 	struct iscsi_cmnd *rsp;
+	struct iscsi_scsi_rsp_hdr *rsp_hdr;
+	u32 size;
 
 	func(req);
 
-	if (req->status == SAM_STAT_GOOD)
+	assert(req->tio);
+
+	size = min(req->tio->size, cmnd_read_size(req));
+
+	if (req->status == SAM_STAT_GOOD && size)
 		do_send_data_rsp(req);
 	else {
 		rsp = create_scsi_rsp(req);
+		rsp_hdr = (struct iscsi_scsi_rsp_hdr *) &rsp->pdu.bhs;
+		if ((size = cmnd_read_size(req)) > req->tio->size) {
+			rsp_hdr->flags |= ISCSI_FLG_RESIDUAL_UNDERFLOW;
+			rsp_hdr->residual_count =
+					cpu_to_be32(size - req->tio->size);
+		} else if (size < req->tio->size) {
+			rsp_hdr->flags |= ISCSI_FLG_RESIDUAL_OVERFLOW;
+			rsp_hdr->residual_count =
+					cpu_to_be32(req->tio->size - size);
+		}
 		iscsi_cmnd_init_write(rsp);
 	}
 }
@@ -510,10 +526,13 @@ static void cmnd_set_sn(struct iscsi_cmnd *cmnd, int set_stat_sn)
 	struct iscsi_conn *conn = cmnd->conn;
 	struct iscsi_session *sess = conn->session;
 
+	sess->max_cmd_sn = sess->exp_cmd_sn + sess->max_queued_cmnds;
+
 	if (set_stat_sn)
 		cmnd->pdu.bhs.sn = cpu_to_be32(conn->stat_sn++);
+
 	cmnd->pdu.bhs.exp_sn = cpu_to_be32(sess->exp_cmd_sn);
-	cmnd->pdu.bhs.max_sn = cpu_to_be32(sess->exp_cmd_sn + sess->max_queued_cmnds);
+	cmnd->pdu.bhs.max_sn = cpu_to_be32(sess->max_cmd_sn);
 }
 
 static void update_stat_sn(struct iscsi_cmnd *cmnd)
@@ -619,8 +638,9 @@ static int cmnd_insert_hash(struct iscsi_cmnd *cmnd)
 
 	err = cmnd_insert_hash_ttt(cmnd, ISCSI_RESERVED_TAG);
 	if (!err) {
-		update_stat_sn(cmnd);
 		err = check_cmd_sn(cmnd);
+		if (!err)
+			update_stat_sn(cmnd);
 	} else if (!cmnd_immediate(cmnd))
 		set_cmnd_tmfabort(cmnd);
 
@@ -691,6 +711,9 @@ static int cmnd_recv_pdu(struct iscsi_conn *conn, struct tio *tio, u32 offset, u
 	dprintk(D_GENERIC, "%p %u,%u\n", tio, offset, size);
 	offset += tio->offset;
 
+	if (!size)
+		return 0;
+
 	if (!(offset < tio->offset + tio->size) ||
 	    !(offset + size <= tio->offset + tio->size)) {
 		eprintk("%u %u %u %u", offset, size, tio->offset, tio->size);
@@ -876,10 +899,10 @@ static int nop_out_start(struct iscsi_conn *conn, struct iscsi_cmnd *cmnd)
 	if (cmnd_itt(cmnd) == cpu_to_be32(ISCSI_RESERVED_TAG)) {
 		if (!cmnd_immediate(cmnd))
 			eprintk("%s\n", "initiator bug!");
-		update_stat_sn(cmnd);
 		err = check_cmd_sn(cmnd);
 		if (err)
 			goto out;
+		update_stat_sn(cmnd);
 	} else if ((err = cmnd_insert_hash(cmnd)) < 0) {
 		eprintk("ignore this request %x\n", cmnd_itt(cmnd));
 		goto out;
@@ -1012,12 +1035,6 @@ static void scsi_cmnd_start(struct iscsi_conn *conn, struct iscsi_cmnd *req)
 		req->is_unsolicited_data = !(req_hdr->flags & ISCSI_CMD_FINAL);
 		req->target_task_tag = get_next_ttt(conn->session);
 
-		if (LUReadonly(req->lun)) {
-			create_sense_rsp(req, DATA_PROTECT, 0x27, 0x0);
-			cmnd_skip_data(req);
-			break;
-		}
-
 		if (!param->immediate_data && req->pdu.datasize)
 			eprintk("%x %x\n", cmnd_itt(req), req_hdr->scb[0]);
 
@@ -1798,11 +1815,8 @@ static int check_segment_length(struct iscsi_cmnd *cmnd)
 	if (cmnd->pdu.datasize > param->max_recv_data_length) {
 		eprintk("data too long %x %u %u\n", cmnd_itt(cmnd),
 			cmnd->pdu.datasize, param->max_recv_data_length);
-
-		if (get_pgcnt(cmnd->pdu.datasize, 0) > ISCSI_CONN_IOV_MAX) {
-			conn_close(conn);
-			return -EINVAL;
-		}
+		conn_close(conn);
+		return -EINVAL;
 	}
 
 	return 0;
diff --git a/kernel/nthread.c b/kernel/nthread.c
index 52cd69a..a96ff89 100644
--- a/kernel/nthread.c
+++ b/kernel/nthread.c
@@ -242,7 +242,7 @@ next_state:
 	case RX_CHECK_HDIGEST:
 		rx_hdigest(conn, RX_INIT_DATA);
 		if (conn->read_state != RX_INIT_DATA)
-			break;
+			return -EIO;
 	case RX_INIT_DATA:
 		cmnd_rx_start(cmnd);
 		conn->read_state = cmnd->pdu.datasize ? RX_DATA : RX_END;
@@ -261,6 +261,8 @@ next_state:
 			break;
 	case RX_CHECK_DDIGEST:
 		rx_ddigest(conn, RX_END);
+		if (conn->read_state != RX_END)
+			return -EIO;
 		break;
 	default:
 		eprintk("%d %d %x\n", res, conn->read_state, cmnd_opcode(cmnd));
diff --git a/kernel/param.c b/kernel/param.c
index 482d00c..35a4056 100644
--- a/kernel/param.c
+++ b/kernel/param.c
@@ -51,6 +51,8 @@ static void sess_param_check(struct iscsi_param_info *info)
 	CHECK_PARAM(info, iparam, error_recovery_level, 0, 0);
 	CHECK_PARAM(info, iparam, data_pdu_inorder, 1, 1);
 	CHECK_PARAM(info, iparam, data_sequence_inorder, 1, 1);
+	CHECK_PARAM(info, iparam, max_outstanding_r2t, 1, 1);
+	CHECK_PARAM(info, iparam, default_retain_time, 0, 0);
 
 	digest_alg_available(&iparam[key_header_digest]);
 	digest_alg_available(&iparam[key_data_digest]);
diff --git a/kernel/target.c b/kernel/target.c
index 2df633b..b458ce1 100644
--- a/kernel/target.c
+++ b/kernel/target.c
@@ -26,7 +26,7 @@ static struct iscsi_sess_param default_session_param = {
 	.max_burst_length = 262144,
 	.first_burst_length = 65536,
 	.default_wait_time = 2,
-	.default_retain_time = 20,
+	.default_retain_time = 0,
 	.max_outstanding_r2t = 1,
 	.data_pdu_inorder = 1,
 	.data_sequence_inorder = 1,
@@ -234,8 +234,6 @@ static void target_destroy(struct iscsi_target *target)
 {
 	dprintk(D_SETUP, "%u\n", target->tid);
 
-	target_thread_stop(target);
-
 	while (!list_empty(&target->volumes)) {
 		struct iet_volume *volume;
 		volume = list_entry(target->volumes.next, struct iet_volume, list);
@@ -243,6 +241,8 @@ static void target_destroy(struct iscsi_target *target)
 		iscsi_volume_destroy(volume);
 	}
 
+	target_thread_stop(target);
+
 	if (!worker_thread_pool)
 		kfree(target->wthread_info);
 	kfree(target);
diff --git a/kernel/target_disk.c b/kernel/target_disk.c
index 1f7693c..978f0e7 100644
--- a/kernel/target_disk.c
+++ b/kernel/target_disk.c
@@ -247,7 +247,7 @@ static void build_inquiry_response(struct iscsi_cmnd *cmnd)
 			data[7] = len;
 			if (cmnd->lun) { /* We need this ? */
 				memset(data + 8, 0x00, 8);
-				memcpy(data + 8, VENDOR_ID, 
+				memcpy(data + 8, VENDOR_ID,
 					min_t(size_t, strlen(VENDOR_ID), 8));
 				memcpy(data + 16, cmnd->lun->scsi_id,
 								SCSI_ID_LEN);
@@ -399,19 +399,29 @@ static void build_write_response(struct iscsi_cmnd *cmnd)
 	assert(cmnd->lun);
 
 	list_del_init(&cmnd->list);
-	err = tio_write(cmnd->lun, tio);
-	if (!err && !LUWCache(cmnd->lun))
-		err = tio_sync(cmnd->lun, tio);
 
-	if (err)
-		/* Medium Error/Write Fault */
-		iscsi_cmnd_set_sense(cmnd, MEDIUM_ERROR, 0x03, 0x0);
+	if (LUReadonly(cmnd->lun))
+		iscsi_cmnd_set_sense(cmnd, DATA_PROTECT, 0x27, 0x0);
+	else {
+		err = tio_write(cmnd->lun, tio);
+		if (!err && !LUWCache(cmnd->lun))
+			err = tio_sync(cmnd->lun, tio);
+
+		if (err)
+			/* Medium Error/Write Fault */
+			iscsi_cmnd_set_sense(cmnd, MEDIUM_ERROR, 0x03,
+					     0x0);
+	}
 }
 
 static void build_sync_cache_response(struct iscsi_cmnd *cmnd)
 {
+	int err;
+
 	assert(cmnd->lun);
-	if (tio_sync(cmnd->lun, NULL))
+
+	err = tio_sync(cmnd->lun, NULL);
+	if (err)
 		/* Medium Error/Write Fault */
 		iscsi_cmnd_set_sense(cmnd, MEDIUM_ERROR, 0x03, 0x0);
 }
@@ -477,11 +487,17 @@ static int disk_check_ua(struct iscsi_cmnd *cmnd)
 			break;
 		default:
 			ua = ua_get_first(cmnd->conn->session, cmnd->lun->lun);
-			iscsi_cmnd_set_sense(cmnd, UNIT_ATTENTION, ua->asc,
-					     ua->ascq);
-			ua_free(ua);
-			send_scsi_rsp(cmnd, build_generic_response);
-			return 1;
+			/*
+			 * potential race: another wthread could've reported it
+			 * in the meantime
+			 */
+			if (ua) {
+				iscsi_cmnd_set_sense(cmnd, UNIT_ATTENTION, ua->asc,
+						     ua->ascq);
+				ua_free(ua);
+				send_scsi_rsp(cmnd, build_generic_response);
+				return 1;
+			}
 		}
 	}
 	return 0;
diff --git a/kernel/tio.c b/kernel/tio.c
index 4220391..3c390d3 100644
--- a/kernel/tio.c
+++ b/kernel/tio.c
@@ -91,6 +91,8 @@ int tio_read(struct iet_volume *lu, struct tio *tio)
 {
 	struct iotype *iot = lu->iotype;
 	assert(iot);
+	if (!tio->size)
+		return 0;
 	return iot->make_request ? iot->make_request(lu, tio, READ) : 0;
 }
 
@@ -98,6 +100,8 @@ int tio_write(struct iet_volume *lu, struct tio *tio)
 {
 	struct iotype *iot = lu->iotype;
 	assert(iot);
+	if (!tio->size)
+		return 0;
 	return iot->make_request ? iot->make_request(lu, tio, WRITE) : 0;
 }
 
diff --git a/kernel/volume.c b/kernel/volume.c
index 2c9a698..c5f1b69 100644
--- a/kernel/volume.c
+++ b/kernel/volume.c
@@ -6,6 +6,7 @@
 
 #include <linux/types.h>
 #include <linux/parser.h>
+#include <linux/blkdev.h>
 
 #include "iscsi.h"
 #include "iscsi_dbg.h"
@@ -87,7 +88,7 @@ static void gen_scsiid(struct iet_volume *volume)
 	hash.tfm = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC);
 	hash.flags = 0;
 
-	if (hash.tfm) {
+	if (!IS_ERR(hash.tfm)) {
 		struct scatterlist sg[2];
 		unsigned int nbytes = 0;
 
@@ -111,8 +112,7 @@ static void gen_scsiid(struct iet_volume *volume)
 						sizeof(volume->target->tid));
 		memcpy(volume->scsi_id + sizeof(volume->target->tid),
 					&volume->lun, sizeof(volume->lun));
-	}	
-
+	}
 }
 
 static int parse_volume_params(struct iet_volume *volume, char *params)
@@ -120,10 +120,12 @@ static int parse_volume_params(struct iet_volume *volume, char *params)
 	int err = 0;
 	unsigned blk_sz;
 	substring_t args[MAX_OPT_ARGS];
-	char *p, *argp = NULL, *buf = (char *) get_zeroed_page(GFP_USER);
+	char *p, *argp = NULL, *bp, *buf = (char *) get_zeroed_page(GFP_USER);
 
 	if (!buf)
 		return -ENOMEM;
+	else
+		bp = buf;
 
 	strncpy(buf, params, PAGE_CACHE_SIZE);
 
@@ -203,7 +205,7 @@ static int parse_volume_params(struct iet_volume *volume, char *params)
 		err = -EINVAL;
 	}
 
-	free_page((unsigned long) buf);
+	free_page((unsigned long) bp);
 
 	return err;
 }
diff --git a/kernel/wthread.c b/kernel/wthread.c
index e113573..97e501b 100644
--- a/kernel/wthread.c
+++ b/kernel/wthread.c
@@ -67,17 +67,13 @@ static int worker_thread(void *arg)
 	struct iscsi_conn *conn;
 	DECLARE_WAITQUEUE(wait, current);
 
-	get_io_context(GFP_KERNEL, -1);
-
-	if (!current->io_context)
-		eprintk("%s\n", "Failed to get IO context");
-	else if (info->wthread_ioc &&
-			atomic_long_read(&info->wthread_ioc->refcount)) {
-		atomic_long_inc(&info->wthread_ioc->refcount);
+	if (info->wthread_ioc &&
+	    atomic_long_inc_not_zero(&info->wthread_ioc->refcount)) {
 		put_io_context(current->io_context);
 		current->io_context = info->wthread_ioc;
+		atomic_inc(&info->wthread_ioc->nr_tasks);
 	} else
-		info->wthread_ioc = current->io_context;
+		info->wthread_ioc = get_io_context(GFP_KERNEL, -1);
 
 	add_wait_queue(&info->wthread_sleep, &wait);
 
@@ -104,12 +100,14 @@ static int worker_thread(void *arg)
 	remove_wait_queue(&info->wthread_sleep, &wait);
 
 	if (current->io_context) {
-		struct io_context *ioc = current->io_context;
+		struct io_context *ioc;
 
 		task_lock(current);
+		ioc = current->io_context;
 		current->io_context = NULL;
 		task_unlock(current);
 
+		atomic_dec(&ioc->nr_tasks);
 		put_io_context(ioc);
 	}
 
diff --git a/usr/Makefile b/usr/Makefile
index ba56dbc..02ce1e7 100644
--- a/usr/Makefile
+++ b/usr/Makefile
@@ -1,4 +1,4 @@
-CFLAGS += -fno-inline -Wall -Wstrict-prototypes -I../include
+CFLAGS += -O2 -fno-inline -Wall -Wstrict-prototypes -I../include
 CFLAGS += -D_GNU_SOURCE # required for glibc >= 2.8
 PROGRAMS = ietd ietadm
 LIBS =
diff --git a/usr/config.h b/usr/config.h
index f05bacf..3724a1b 100644
--- a/usr/config.h
+++ b/usr/config.h
@@ -29,7 +29,7 @@
 #define CONFIG_H
 
 struct config_operations {
-	void (*init) (char *, char **, int *);
+	void (*init) (char *params, int *timeout);
 	int (*target_add) (u32 *, char *);
 	int (*target_stop) (u32);
 	int (*target_del) (u32);
diff --git a/usr/conn.c b/usr/conn.c
index 14b5c48..d61b5c9 100644
--- a/usr/conn.c
+++ b/usr/conn.c
@@ -51,7 +51,7 @@ void conn_take_fd(struct connection *conn, int fd)
 			      conn->session_param[key_data_digest].val);
 	if (!err)
 		conn->session->conn_cnt++;
-	else if (err < 0 && err != -EEXIST)
+	else
 		log_error("unable to create connection %u for session %#" PRIx64 " in target %u: %d",
 			conn->cid, conn->session->sid.id64, conn->tid, errno);
 
diff --git a/usr/event.c b/usr/event.c
index 09b790e..9f85ad2 100644
--- a/usr/event.c
+++ b/usr/event.c
@@ -87,10 +87,9 @@ retry:
 
 	switch (event.state) {
 	case E_CONN_CLOSE:
-		if (!(session = session_find_id(event.tid, event.sid))) {
-			log_warning("session %llu not found?", event.sid);
-			goto retry;
-		}
+		if (!(session = session_find_id(event.tid, event.sid)))
+			/* session previously closed for reinstatement? */
+			break;
 
 		if (--session->conn_cnt <= 0)
 			session_remove(session);
diff --git a/usr/ietd.c b/usr/ietd.c
index ca8c759..e9e83c4 100644
--- a/usr/ietd.c
+++ b/usr/ietd.c
@@ -426,6 +426,8 @@ void event_loop(int timeout)
 				pollfd->fd = -1;
 				incoming[i] = NULL;
 				incoming_cnt--;
+				if ((poll_array[POLL_LISTEN].events == 0) && (incoming_cnt < INCOMING_MAX))
+					poll_array[POLL_LISTEN].events = POLLIN;
 				if (session && session->conn_cnt <= 0)
 					session_remove(session);
 			}
@@ -439,8 +441,7 @@ int main(int argc, char **argv)
 	char *config = NULL, pid_buf[64];
 	uid_t uid = 0;
 	gid_t gid = 0;
-	char *isns = NULL;
-	int isns_ac = 0, pid_fd;
+	int pid_fd;
 
 	/* otherwise we would die in some later write() during the event_loop
 	 * instead of getting EPIPE! */
@@ -549,10 +550,6 @@ int main(int argc, char **argv)
 		exit(-1);
 	}
 
-	cops->init(config, &isns, &isns_ac);
-	if (isns)
-		timeout = isns_init(isns, isns_ac);
-
 	if (gid && setgid(gid) < 0) {
 		log_error("unable to setgid: %m");
 		exit(-1);
@@ -563,6 +560,8 @@ int main(int argc, char **argv)
 		exit(-1);
 	}
 
+	cops->init(config, &timeout);
+
 	event_loop(timeout);
 
 	return 0;
diff --git a/usr/iscsid.c b/usr/iscsid.c
index a99916d..fd0420d 100644
--- a/usr/iscsid.c
+++ b/usr/iscsid.c
@@ -273,43 +273,6 @@ static void text_scan_security(struct connection *conn)
 		login_rsp_ini_err(conn, ISCSI_STATUS_AUTH_FAILED);
 }
 
-static void login_security_done(struct connection *conn)
-{
-	struct iscsi_login_req_hdr *req =
-				(struct iscsi_login_req_hdr *)&conn->req.bhs;
-	struct session *session;
-
-	if (!conn->tid)
-		return;
-
-	if ((session = session_find_name(conn->tid, conn->initiator, req->sid))) {
-		if (!req->sid.id.tsih) {
-			/* do session reinstatement */
-			log_debug(1, "session %#" PRIx64 " reinstated",
-								req->sid.id64);
-			if (session_remove(session))
-				login_rsp_tgt_err(conn, ISCSI_STATUS_TARGET_ERROR);
-			return;
-		} else if (req->sid.id.tsih != session->sid.id.tsih) {
-			/* fail the login */
-			login_rsp_ini_err(conn, ISCSI_STATUS_SESSION_NOT_FOUND);
-			return;
-		}
-		/* add connection to existing session */
-		/* reinstatement handled in kernel */
-		log_debug(1, "connection %u added to session %#" PRIx64,
-						conn->cid, req->sid.id64);
-		conn->session = session;
-	} else {
-		if (req->sid.id.tsih) {
-			/* fail the login */
-			login_rsp_ini_err(conn, ISCSI_STATUS_SESSION_NOT_FOUND);
-			return;
-		}
-		/* instantiate a new session */
-	}
-}
-
 static void text_scan_login(struct connection *conn)
 {
 	char *key, *value, *data;
@@ -510,14 +473,43 @@ static void login_start(struct connection *conn)
 
 static void login_finish(struct connection *conn)
 {
+	struct iscsi_login_req_hdr *req =
+			(struct iscsi_login_req_hdr *)&conn->req.bhs;
+	struct session *session =
+			session_find_name(conn->tid, conn->initiator, req->sid);
+
 	switch (conn->session_type) {
 	case SESSION_NORMAL:
+		if (session) {
+			if (!req->sid.id.tsih) {
+				/* session reinstatement */
+				log_debug(1, "session %#" PRIx64 " reinstated",
+					req->sid.id64);
+				if (session_remove(session)) {
+					login_rsp_tgt_err(conn,
+						ISCSI_STATUS_TARGET_ERROR);
+					return;
+				}
+			} else if (req->sid.id.tsih != session->sid.id.tsih) {
+				/* fail the login */
+				login_rsp_ini_err(conn,
+					ISCSI_STATUS_SESSION_NOT_FOUND);
+				return;
+			}
+			/* add connection to existing session */
+			log_debug(1, "connection %u added to session %#" PRIx64,
+				conn->cid, req->sid.id64);
+			conn->session = session;
+		} else if (req->sid.id.tsih) {
+			/* fail the login */
+			login_rsp_ini_err(conn, ISCSI_STATUS_SESSION_NOT_FOUND);
+			return;
+		}
 		/* create or re-create in case the session closed */
 		if (session_create(conn)) {
 			login_rsp_tgt_err(conn, ISCSI_STATUS_TARGET_ERROR);
 			return;
 		}
-		conn->sid = conn->session->sid;
 		break;
 	case SESSION_DISCOVERY:
 		/* set a dummy tsih value */
@@ -680,7 +672,6 @@ static void cmnd_exec_login(struct connection *conn)
 			case STATE_SECURITY:
 			case STATE_SECURITY_DONE:
 				conn->state = STATE_SECURITY_LOGIN;
-				login_security_done(conn);
 				break;
 			default:
 				goto init_err;
@@ -696,7 +687,6 @@ static void cmnd_exec_login(struct connection *conn)
 					break;
 				}
 				conn->state = STATE_SECURITY_FULL;
-				login_security_done(conn);
 				break;
 			case STATE_LOGIN:
 				if (stay)
diff --git a/usr/isns.c b/usr/isns.c
index f6d4072..65fbd30 100644
--- a/usr/isns.c
+++ b/usr/isns.c
@@ -222,13 +222,13 @@ static int isns_scn_deregister(char *name)
 
 #if __BYTE_ORDER == __LITTLE_ENDIAN
 #define set_scn_flag(x)						\
-({								\
+{								\
 	x = (x & 0x55555555) << 1 | (x & 0xaaaaaaaa) >> 1;	\
 	x = (x & 0x33333333) << 2 | (x & 0xcccccccc) >> 2;	\
 	x = (x & 0x0f0f0f0f) << 4 | (x & 0xf0f0f0f0) >> 4;	\
 	x = (x & 0x00ff00ff) << 8 | (x & 0xff00ff00) >> 8;	\
 	x = (x & 0x0000ffff) << 16 | (x & 0xffff0000) >> 16;	\
-})
+}
 #else
 #define set_scn_flag(x) (x)
 #endif
diff --git a/usr/param.c b/usr/param.c
index 91ae3e6..a31d5ed 100644
--- a/usr/param.c
+++ b/usr/param.c
@@ -142,11 +142,15 @@ static int digest_val_to_str(unsigned int val, char *str)
 {
 	int err = 0;
 
-	if (val & DIGEST_CRC32C)
-		strcpy(str, "CRC32C");
-	else if (val & DIGEST_NONE)
+	if (val & DIGEST_NONE)
 		strcpy(str, "None");
-	else
+	if (val & DIGEST_CRC32C) {
+		if (strlen(str))
+			strcat(str, ",CRC32C");
+		else
+			strcpy(str, "CRC32C");
+	}
+	if (!strlen(str))
 		err = -EINVAL;
 
 	return err;
@@ -158,20 +162,18 @@ static int digest_str_to_val(char *str, unsigned int *val)
 	char *p, *q;
 	p = str;
 
-	*val = DIGEST_NONE;
+	*val = 0;
 	do {
-		if (!strncmp(p, "None", strlen("None")))
+		q = strsep(&p, ",");
+		if (!strcmp(q, "None"))
 			*val |= DIGEST_NONE;
-		else if (!strncmp(p, "CRC32C", strlen("CRC32C")))
+		else if (!strcmp(q, "CRC32C"))
 			*val |= DIGEST_CRC32C;
 		else {
 			err = -EINVAL;
 			break;
 		}
-
-		if ((q = strchr(p, ',')))
-			p = q + 1;
-	} while (q);
+	} while (p);
 
 	return err;
 }
diff --git a/usr/plain.c b/usr/plain.c
index 0b1b3c1..b3b1da2 100644
--- a/usr/plain.c
+++ b/usr/plain.c
@@ -691,10 +691,12 @@ static int iscsi_param_partial_set(u32 tid, u64 sid, int type, int key, u32 val)
 	return __plain_param_set(tid, sid, type, 1 << key, param, 0);
 }
 
-static void plain_portal_init(FILE *fp, char **isns, int *isns_ac)
+static void plain_portal_init(FILE *fp, int *timeout)
 {
 	char buf[BUFSIZE];
 	char *p, *q;
+	char *isns = NULL;
+	int isns_ac = 0;
 
 	while (fgets(buf, BUFSIZE, fp)) {
 		q = buf;
@@ -702,15 +704,16 @@ static void plain_portal_init(FILE *fp, char **isns, int *isns_ac)
 		if (!p || *p == '#')
 			continue;
 		if (!strcasecmp(p, "iSNSServer")) {
-			*isns = strdup(target_sep_string(&q));
+			isns = strdup(target_sep_string(&q));
 		} else if (!strcasecmp(p, "iSNSAccessControl")) {
 			char *str = target_sep_string(&q);
 			if (!strcasecmp(str, "Yes"))
-				*isns_ac = 1;
+				isns_ac = 1;
 		}
 	}
 
-	return;
+	if (isns)
+		*timeout = isns_init(isns, isns_ac);
 }
 
 static void plain_target_init(FILE *fp)
@@ -796,7 +799,7 @@ static void plain_account_init(FILE *fp)
 	return;
 }
 
-static void plain_init(char *params, char **isns, int *isns_ac)
+static void plain_init(char *params, int *timeout)
 {
 	FILE *fp;
 	struct stat st;
@@ -826,7 +829,7 @@ static void plain_init(char *params, char **isns, int *isns_ac)
 		}
 	}
 
-	plain_portal_init(fp, isns, isns_ac);
+	plain_portal_init(fp, timeout);
 
 	rewind(fp);
 
diff --git a/usr/session.c b/usr/session.c
index 42f909d..999ff77 100644
--- a/usr/session.c
+++ b/usr/session.c
@@ -102,8 +102,10 @@ int session_create(struct connection *conn)
 		session->initiator = strdup(conn->initiator);
 
 		conn->session = session;
+		conn->sid = session->sid;
 	} else {
 		session = conn->session;
+		conn->sid = session->sid;
 
 		if (session_exist(conn->tid, session->sid.id64))
 			return 0;
diff --git a/usr/target.c b/usr/target.c
index c35de85..2ca038b 100644
--- a/usr/target.c
+++ b/usr/target.c
@@ -78,6 +78,9 @@ static void target_print_addr(struct connection *conn, char *addr, int family)
 {
 	char taddr[NI_MAXHOST + NI_MAXSERV + 5];
 
+	/* strip ipv6 zone id */
+	addr = strsep(&addr, "%");
+
 	snprintf(taddr, sizeof(taddr),
 		(family == AF_INET) ? "%s:%d,1" : "[%s]:%d,1",
 							addr, server_port);
