diff --git a/ubuntu/iscsitarget/BOM b/ubuntu/iscsitarget/BOM
index cda1135..0fde30c 100644
--- a/ubuntu/iscsitarget/BOM
+++ b/ubuntu/iscsitarget/BOM
@@ -1,2 +1,2 @@
 Downloaded from:	svn://svn.berlios.de/iscsitarget/trunk
-Current Version:	1.4.19
+Current Version:	1.4.20.1
diff --git a/ubuntu/iscsitarget/block-io.c b/ubuntu/iscsitarget/block-io.c
index 708f101..4de1d20 100644
--- a/ubuntu/iscsitarget/block-io.c
+++ b/ubuntu/iscsitarget/block-io.c
@@ -86,7 +86,8 @@ blockio_make_request(struct iet_volume *volume, struct tio *tio, int rw)
 			goto out;
 		}
 
-		bio->bi_sector = ppos >> volume->blk_shift;
+		/* bi_sector is ALWAYS in units of 512 bytes */
+		bio->bi_sector = ppos >> 9;
 		bio->bi_bdev = bio_data->bdev;
 		bio->bi_end_io = blockio_bio_endio;
 		bio->bi_private = tio_work;
@@ -174,77 +175,21 @@ blockio_open_path(struct iet_volume *volume, const char *path)
 	return err;
 }
 
-static int
-set_scsiid(struct iet_volume *volume, const char *id)
-{
-	size_t len;
-
-	if ((len = strlen(id)) > SCSI_ID_LEN - VENDOR_ID_LEN) {
-		eprintk("SCSI ID too long, %zd provided, %u max\n", len,
-			SCSI_ID_LEN - VENDOR_ID_LEN);
-		return -EINVAL;
-	}
-
-	memcpy(volume->scsi_id + VENDOR_ID_LEN, id, len);
-
-	return 0;
-}
-
-static void
-gen_scsiid(struct iet_volume *volume, struct inode *inode)
-{
-	int i;
-	u32 *p;
-
-	strlcpy(volume->scsi_id, VENDOR_ID, VENDOR_ID_LEN);
-
-	for (i = VENDOR_ID_LEN; i < SCSI_ID_LEN; i++)
-		if (volume->scsi_id[i])
-			return;
-
-	/* If a scsi id doesn't exist generate a 16 byte one:
-	 * Bytes   1-4: target type
-	 * Bytes   5-8: target id
-	 * Bytes  9-12: inode number
-	 * Bytes 13-16: device type
-	 */
-	p = (u32 *) (volume->scsi_id + VENDOR_ID_LEN);
-	*(p + 0) = volume->target->trgt_param.target_type;
-	*(p + 1) = volume->target->tid;
-	*(p + 2) = volume->lun;
-	*(p + 3) = (unsigned int) inode->i_sb->s_dev;
-}
-
-static int
-set_scsisn(struct iet_volume *volume, const char *sn)
-{
-	size_t len;
-
-	if ((len = strlen(sn)) > SCSI_SN_LEN) {
-		eprintk("SCSI SN too long, %zd provided, %u max\n", len,
-			SCSI_SN_LEN);
-		return -EINVAL;
-	}
-
-	memcpy(volume->scsi_sn, sn, len);
-
-	return 0;
-}
-
 /* Create an enumeration of our accepted actions */
 enum
 {
-	Opt_scsiid, Opt_scsisn, Opt_path, Opt_ignore, Opt_err,
+	opt_path, opt_ignore, opt_err,
 };
 
 /* Create a match table using our action enums and their matching options */
 static match_table_t tokens = {
-	{Opt_scsiid, "ScsiId=%s"},
-	{Opt_scsisn, "ScsiSN=%s"},
-	{Opt_path, "Path=%s"},
-	{Opt_ignore, "Type=%s"},
-	{Opt_ignore, "IOMode=%s"},
-	{Opt_err, NULL},
+	{opt_path, "path=%s"},
+	{opt_ignore, "scsiid=%s"},
+	{opt_ignore, "scsisn=%s"},
+	{opt_ignore, "type=%s"},
+	{opt_ignore, "iomode=%s"},
+	{opt_ignore, "blocksize=%s"},
+	{opt_err, NULL},
 };
 
 static int
@@ -262,29 +207,10 @@ parse_blockio_params(struct iet_volume *volume, char *params)
 		int token;
 		if (!*p)
 			continue;
+		iet_strtolower(p);
 		token = match_token(p, tokens, args);
 		switch (token) {
-		case Opt_scsiid:
-			if (!(q = match_strdup(&args[0]))) {
-				err = -ENOMEM;
-				goto out;
-			}
-			err = set_scsiid(volume, q);
-			kfree(q);
-			if (err < 0)
-				goto out;
-			break;
-		case Opt_scsisn:
-			if (!(q = match_strdup(&args[0]))) {
-				err = -ENOMEM;
-				goto out;
-			}
-			err = set_scsisn(volume, q);
-			kfree(q);
-			if (err < 0)
-				goto out;
-			break;
-		case Opt_path:
+		case opt_path:
 			if (info->path) {
 				iprintk("Target %s, LUN %u: "
 					"duplicate \"Path\" param\n",
@@ -301,7 +227,7 @@ parse_blockio_params(struct iet_volume *volume, char *params)
 			if (err < 0)
 				goto out;
 			break;
-		case Opt_ignore:
+		case opt_ignore:
 			break;
 		default:
 			iprintk("Target %s, LUN %u: unknown param %s\n",
@@ -315,6 +241,7 @@ parse_blockio_params(struct iet_volume *volume, char *params)
 			volume->target->name, volume->lun);
 		err = -EINVAL;
 	}
+
   out:
 	return err;
 }
@@ -350,22 +277,31 @@ blockio_attach(struct iet_volume *volume, char *args)
 
 	volume->private = bio_data;
 
-	if ((err = parse_blockio_params(volume, args)) < 0) {
+	err = parse_blockio_params(volume, args);
+	if (!err) {
+		/* see Documentation/ABI/testing/sysfs-block */
+		unsigned bsz = bdev_logical_block_size(bio_data->bdev);
+		if (!volume->blk_shift)
+			volume->blk_shift = ilog2(bsz);
+		else if (volume->blk_shift < ilog2(bsz)) {
+			eprintk("Specified block size (%u) smaller than "
+				"device %s logical block size (%u)\n",
+				(1 << volume->blk_shift), bio_data->path, bsz);
+			err = -EINVAL;
+		}
+	}
+	if (err < 0) {
 		eprintk("Error attaching Lun %u to Target %s \n",
 			volume->lun, volume->target->name);
 		goto out;
 	}
 
-	/* Assign a vendor id, generate scsi id if none exists */
-	gen_scsiid(volume, bio_data->bdev->bd_inode);
+	volume->blk_cnt = bio_data->bdev->bd_inode->i_size >> volume->blk_shift;
 
 	/* Offer neither write nor read caching */
 	ClearLURCache(volume);
 	ClearLUWCache(volume);
 
-	volume->blk_shift = SECTOR_SIZE_BITS;
-	volume->blk_cnt = bio_data->bdev->bd_inode->i_size >> volume->blk_shift;
-
   out:
 	if (err < 0)
 		blockio_detach(volume);
diff --git a/ubuntu/iscsitarget/config.c b/ubuntu/iscsitarget/config.c
index 51331fb..87fa44b 100644
--- a/ubuntu/iscsitarget/config.c
+++ b/ubuntu/iscsitarget/config.c
@@ -9,6 +9,8 @@
 #include "iscsi.h"
 #include "iscsi_dbg.h"
 
+static DECLARE_MUTEX(ioctl_sem);
+
 struct proc_entries {
 	const char *name;
 	struct file_operations *fops;
@@ -60,26 +62,45 @@ err:
 	return -ENOMEM;
 }
 
-static int get_conn_info(struct iscsi_target *target, unsigned long ptr)
+static int get_module_info(unsigned long ptr)
 {
+	struct module_info info;
 	int err;
+
+	snprintf(info.version, sizeof(info.version), "%s", IET_VERSION_STRING);
+
+	err = copy_to_user((void *) ptr, &info, sizeof(info));
+	if (err)
+		return -EFAULT;
+
+	return 0;
+}
+
+static int get_conn_info(struct iscsi_target *target, unsigned long ptr)
+{
 	struct iscsi_session *session;
-	struct conn_info info;
 	struct iscsi_conn *conn;
+	struct conn_info info;
+	int err;
 
-	if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
-		return err;
+	err = copy_from_user(&info, (void *) ptr, sizeof(info));
+	if (err)
+		return -EFAULT;
 
 	session = session_lookup(target, info.sid);
 	if (!session)
 		return -ENOENT;
+
 	conn = conn_lookup(session, info.cid);
+	if (!conn)
+		return -ENOENT;
 
 	info.cid = conn->cid;
 	info.stat_sn = conn->stat_sn;
 	info.exp_stat_sn = conn->exp_stat_sn;
 
-	if (copy_to_user((void *) ptr, &info, sizeof(info)))
+	err = copy_to_user((void *) ptr, &info, sizeof(info));
+	if (err)
 		return -EFAULT;
 
 	return 0;
@@ -87,14 +108,16 @@ static int get_conn_info(struct iscsi_target *target, unsigned long ptr)
 
 static int add_conn(struct iscsi_target *target, unsigned long ptr)
 {
-	int err;
 	struct iscsi_session *session;
 	struct conn_info info;
+	int err;
 
-	if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
-		return err;
+	err = copy_from_user(&info, (void *) ptr, sizeof(info));
+	if (err)
+		return -EFAULT;
 
-	if (!(session = session_lookup(target, info.sid)))
+	session = session_lookup(target, info.sid);
+	if (!session)
 		return -ENOENT;
 
 	return conn_add(session, &info);
@@ -102,14 +125,16 @@ static int add_conn(struct iscsi_target *target, unsigned long ptr)
 
 static int del_conn(struct iscsi_target *target, unsigned long ptr)
 {
-	int err;
 	struct iscsi_session *session;
 	struct conn_info info;
+	int err;
 
-	if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
-		return err;
+	err = copy_from_user(&info, (void *) ptr, sizeof(info));
+	if (err)
+		return -EFAULT;
 
-	if (!(session = session_lookup(target, info.sid)))
+	session = session_lookup(target, info.sid);
+	if (!session)
 		return -ENOENT;
 
 	return conn_del(session, &info);
@@ -117,22 +142,23 @@ static int del_conn(struct iscsi_target *target, unsigned long ptr)
 
 static int get_session_info(struct iscsi_target *target, unsigned long ptr)
 {
-	int err;
 	struct iscsi_session *session;
 	struct session_info info;
+	int err;
 
-	if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
-		return err;
+	err = copy_from_user(&info, (void *) ptr, sizeof(info));
+	if (err)
+		return -EFAULT;
 
 	session = session_lookup(target, info.sid);
-
 	if (!session)
 		return -ENOENT;
 
 	info.exp_cmd_sn = session->exp_cmd_sn;
 	info.max_cmd_sn = session->max_cmd_sn;
 
-	if (copy_to_user((void *) ptr, &info, sizeof(info)))
+	err = copy_to_user((void *) ptr, &info, sizeof(info));
+	if (err)
 		return -EFAULT;
 
 	return 0;
@@ -140,78 +166,90 @@ static int get_session_info(struct iscsi_target *target, unsigned long ptr)
 
 static int add_session(struct iscsi_target *target, unsigned long ptr)
 {
-	int err;
 	struct session_info info;
+	int err;
 
-	if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
-		return err;
+	err = copy_from_user(&info, (void *) ptr, sizeof(info));
+	if (err)
+		return -EFAULT;
 
 	return session_add(target, &info);
 }
 
 static int del_session(struct iscsi_target *target, unsigned long ptr)
 {
-	int err;
 	struct session_info info;
+	int err;
 
-	if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
-		return err;
+	err = copy_from_user(&info, (void *) ptr, sizeof(info));
+	if (err)
+		return -EFAULT;
 
 	return session_del(target, info.sid);
 }
 
 static int add_volume(struct iscsi_target *target, unsigned long ptr)
 {
-	int err;
 	struct volume_info info;
+	int err;
 
-	if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
-		return err;
+	err = copy_from_user(&info, (void *) ptr, sizeof(info));
+	if (err)
+		return -EFAULT;
 
 	return volume_add(target, &info);
 }
 
 static int del_volume(struct iscsi_target *target, unsigned long ptr)
 {
-	int err;
 	struct volume_info info;
+	int err;
 
-	if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
-		return err;
+	err = copy_from_user(&info, (void *) ptr, sizeof(info));
+	if (err)
+		return -EFAULT;
 
 	return iscsi_volume_del(target, &info);
 }
 
 static int iscsi_param_config(struct iscsi_target *target, unsigned long ptr, int set)
 {
-	int err;
 	struct iscsi_param_info info;
+	int err;
 
-	if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
-		goto out;
+	err = copy_from_user(&info, (void *) ptr, sizeof(info));
+	if (err)
+		return -EFAULT;
 
-	if ((err = iscsi_param_set(target, &info, set)) < 0)
-		goto out;
+	err = iscsi_param_set(target, &info, set);
+	if (err < 0 || set)
+		return err;
 
-	if (!set)
-		err = copy_to_user((void *) ptr, &info, sizeof(info));
+	err = copy_to_user((void *) ptr, &info, sizeof(info));
+	if (err)
+		return -EFAULT;
 
-out:
-	return err;
+	return 0;
 }
 
 static int add_target(unsigned long ptr)
 {
-	int err;
 	struct target_info info;
+	int err;
 
-	if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
+	err = copy_from_user(&info, (void *) ptr, sizeof(info));
+	if (err)
+		return -EFAULT;
+
+	err = target_add(&info);
+	if (err < 0)
 		return err;
 
-	if (!(err = target_add(&info)))
-		err = copy_to_user((void *) ptr, &info, sizeof(info));
+	err = copy_to_user((void *) ptr, &info, sizeof(info));
+	if (err)
+		return -EFAULT;
 
-	return err;
+	return 0;
 }
 
 static long ioctl(struct file *file, unsigned int cmd, unsigned long arg)
@@ -220,40 +258,39 @@ static long ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 	long err;
 	u32 id;
 
-	if ((err = get_user(id, (u32 *) arg)) != 0)
-		goto done;
+	err = down_interruptible(&ioctl_sem);
+	if (err < 0)
+		return err;
 
-	if (cmd == DEL_TARGET) {
-		err = target_del(id);
+	if (cmd == GET_MODULE_INFO) {
+		err = get_module_info(arg);
 		goto done;
 	}
 
-	target = target_lookup_by_id(id);
+	if (cmd == ADD_TARGET) {
+		err = add_target(arg);
+		goto done;
+	}
 
-	if (cmd == ADD_TARGET)
-		if (target) {
-			err = -EEXIST;
-			eprintk("Target %u already exist!\n", id);
-			goto done;
-		}
+	err = get_user(id, (u32 *) arg);
+	if (err < 0)
+		goto done;
 
-	switch (cmd) {
-	case ADD_TARGET:
-		assert(!target);
-		err = add_target(arg);
+	/* locking handled in target_del */
+	if (cmd == DEL_TARGET) {
+		err = target_del(id);
 		goto done;
 	}
 
+	target = target_lookup_by_id(id);
 	if (!target) {
-		eprintk("can't find the target %u\n", id);
-		err = -EINVAL;
+		err = -ENOENT;
 		goto done;
 	}
 
-	if ((err = target_lock(target, 1)) < 0) {
-		eprintk("interrupted %ld %d\n", err, cmd);
+	err = target_lock(target, 1);
+	if (err < 0)
 		goto done;
-	}
 
 	switch (cmd) {
 	case ADD_VOLUME:
@@ -300,17 +337,20 @@ static long ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 		err = -EINVAL;
 	}
 
-	if (target)
-		target_unlock(target);
-
+	target_unlock(target);
 done:
+	up(&ioctl_sem);
+
 	return err;
 }
 
 static int release(struct inode *i __attribute__((unused)),
 		   struct file *f __attribute__((unused)))
 {
+	down(&ioctl_sem);
 	target_del_all();
+	up(&ioctl_sem);
+
 	return 0;
 }
 
diff --git a/ubuntu/iscsitarget/conn.c b/ubuntu/iscsitarget/conn.c
index 5f91e3d..ec6dada 100644
--- a/ubuntu/iscsitarget/conn.c
+++ b/ubuntu/iscsitarget/conn.c
@@ -7,6 +7,7 @@
 #include <linux/file.h>
 #include <linux/ip.h>
 #include <net/tcp.h>
+#include <scsi/scsi.h>
 
 #include "iscsi.h"
 #include "iscsi_dbg.h"
@@ -211,22 +212,39 @@ static int iet_conn_alloc(struct iscsi_session *session, struct conn_info *info)
 
 void conn_close(struct iscsi_conn *conn)
 {
+	struct iscsi_cmnd *cmnd;
+	struct iscsi_session *session = conn->session;
+
 	if (test_and_clear_bit(CONN_ACTIVE, &conn->state))
 		set_bit(CONN_CLOSING, &conn->state);
 
+	spin_lock(&conn->list_lock);
+	list_for_each_entry(cmnd, &conn->pdu_list, conn_list) {
+		set_cmnd_tmfabort(cmnd);
+		if (cmnd->lun) {
+			ua_establish_for_session(session, cmnd->lun->lun, 0x47, 0x7f);
+			iscsi_cmnd_set_sense(cmnd, UNIT_ATTENTION, 0x6e, 0x0);
+		}
+	}
+	spin_unlock(&conn->list_lock);
+
 	nthread_wakeup(conn->session->target);
 }
 
 int conn_add(struct iscsi_session *session, struct conn_info *info)
 {
 	struct iscsi_conn *conn;
-	int err = -EEXIST;
+	int err;
 
 	conn = conn_lookup(session, info->cid);
 	if (conn)
-		return err;
+		conn_close(conn);
 
-	return iet_conn_alloc(session, info);
+	err = iet_conn_alloc(session, info);
+	if (!err && conn)
+		err = -EEXIST;
+
+	return err;
 }
 
 int conn_del(struct iscsi_session *session, struct conn_info *info)
@@ -242,12 +260,3 @@ int conn_del(struct iscsi_session *session, struct conn_info *info)
 
 	return 0;
 }
-
-/* target_lock() supposed to be held */
-void conn_del_all(struct iscsi_session *session)
-{
-	struct iscsi_conn *conn;
-
-	list_for_each_entry(conn, &session->conn_list, list)
-		conn_close(conn);
-}
diff --git a/ubuntu/iscsitarget/file-io.c b/ubuntu/iscsitarget/file-io.c
index a492ce4..38951a9 100644
--- a/ubuntu/iscsitarget/file-io.c
+++ b/ubuntu/iscsitarget/file-io.c
@@ -53,9 +53,9 @@ static int fileio_make_request(struct iet_volume *lu, struct tio *tio, int rw)
 		set_fs(get_ds());
 
 		if (rw == READ)
-			ret = do_sync_read(filp, buf, count, &ppos);
+			ret = vfs_read(filp, buf, count, &ppos);
 		else
-			ret = do_sync_write(filp, buf, count, &ppos);
+			ret = vfs_write(filp, buf, count, &ppos);
 
 		set_fs(oldfs);
 
@@ -82,6 +82,7 @@ static int fileio_sync(struct iet_volume *lu, struct tio *tio)
 
 	if (tio) {
 		ppos = (loff_t) tio->idx << PAGE_CACHE_SHIFT;
+		ppos += tio->offset;
 		count = tio->size;
 	} else {
 		ppos = 0;
@@ -124,63 +125,18 @@ static int open_path(struct iet_volume *volume, const char *path)
 	return err;
 }
 
-static int set_scsiid(struct iet_volume *volume, const char *id)
-{
-	size_t len;
-
-	if ((len = strlen(id)) > SCSI_ID_LEN - VENDOR_ID_LEN) {
-		eprintk("SCSI ID too long, %zd provided, %u max\n", len,
-			SCSI_ID_LEN - VENDOR_ID_LEN);
-		return -EINVAL;
-	}
-
-	memcpy(volume->scsi_id + VENDOR_ID_LEN, id, len);
-
-	return 0;
-}
-
-static void gen_scsiid(struct iet_volume *volume, struct inode *inode)
-{
-	int i;
-	u32 *p;
-
-	strlcpy(volume->scsi_id, VENDOR_ID, VENDOR_ID_LEN);
-
-	for (i = VENDOR_ID_LEN; i < SCSI_ID_LEN; i++)
-		if (volume->scsi_id[i])
-			return;
-
-	p = (u32 *) (volume->scsi_id + VENDOR_ID_LEN);
-	*(p + 0) = volume->target->trgt_param.target_type;
-	*(p + 1) = volume->target->tid;
-	*(p + 2) = (unsigned int) inode->i_ino;
-	*(p + 3) = (unsigned int) inode->i_sb->s_dev;
-}
-
-static int set_scsisn(struct iet_volume *volume, const char *sn)
-{
-	size_t len;
-
-	if ((len = strlen(sn)) > SCSI_SN_LEN) {
-		eprintk("SCSI SN too long, %zd provided, %u max\n", len,
-			SCSI_SN_LEN);
-		return -EINVAL;
-	}
-	memcpy(volume->scsi_sn, sn, len);
-	return 0;
-}
-
 enum {
-	Opt_scsiid, Opt_scsisn, Opt_path, Opt_ignore, Opt_err,
+	opt_path, opt_ignore, opt_err,
 };
 
 static match_table_t tokens = {
-	{Opt_scsiid, "ScsiId=%s"},
-	{Opt_scsisn, "ScsiSN=%s"},
-	{Opt_path, "Path=%s"},
-	{Opt_ignore, "Type=%s"},
-	{Opt_ignore, "IOMode=%s"},
-	{Opt_err, NULL},
+	{opt_path, "path=%s"},
+	{opt_ignore, "scsiid=%s"},
+	{opt_ignore, "scsisn=%s"},
+	{opt_ignore, "type=%s"},
+	{opt_ignore, "iomode=%s"},
+	{opt_ignore, "blocksize=%s"},
+	{opt_err, NULL},
 };
 
 static int parse_fileio_params(struct iet_volume *volume, char *params)
@@ -194,29 +150,10 @@ static int parse_fileio_params(struct iet_volume *volume, char *params)
 		int token;
 		if (!*p)
 			continue;
+		iet_strtolower(p);
 		token = match_token(p, tokens, args);
 		switch (token) {
-		case Opt_scsiid:
-			if (!(q = match_strdup(&args[0]))) {
-				err = -ENOMEM;
-				goto out;
-			}
-			err = set_scsiid(volume, q);
-			kfree(q);
-			if (err < 0)
-				goto out;
-			break;
-		case Opt_scsisn:
-			if (!(q = match_strdup(&args[0]))) {
-				err = -ENOMEM;
-				goto out;
-			}
-			err = set_scsisn(volume, q);
-			kfree(q);
-			if (err < 0)
-				goto out;
-			break;
-		case Opt_path:
+		case opt_path:
 			if (info->path) {
 				iprintk("Target %s, LUN %u: "
 					"duplicate \"Path\" param\n",
@@ -233,7 +170,7 @@ static int parse_fileio_params(struct iet_volume *volume, char *params)
 			if (err < 0)
 				goto out;
 			break;
-		case Opt_ignore:
+		case opt_ignore:
 			break;
 		default:
 			iprintk("Target %s, LUN %u: unknown param %s\n",
@@ -285,8 +222,6 @@ static int fileio_attach(struct iet_volume *lu, char *args)
 	}
 	inode = p->filp->f_dentry->d_inode;
 
-	gen_scsiid(lu, inode);
-
 	if (S_ISREG(inode->i_mode))
 		;
 	else if (S_ISBLK(inode->i_mode))
@@ -296,7 +231,9 @@ static int fileio_attach(struct iet_volume *lu, char *args)
 		goto out;
 	}
 
-	lu->blk_shift = SECTOR_SIZE_BITS;
+	if (!lu->blk_shift)
+		lu->blk_shift = ilog2(IET_DEF_BLOCK_SIZE);
+
 	lu->blk_cnt = inode->i_size >> lu->blk_shift;
 
 	/* we're using the page cache */
diff --git a/ubuntu/iscsitarget/include/iet_u.h b/ubuntu/iscsitarget/include/iet_u.h
index 620b3c4..174f0dc 100644
--- a/ubuntu/iscsitarget/include/iet_u.h
+++ b/ubuntu/iscsitarget/include/iet_u.h
@@ -1,7 +1,7 @@
 #ifndef _IET_U_H
 #define _IET_U_H
 
-#define IET_VERSION_STRING	"1.4.19"
+#define IET_VERSION_STRING	"1.4.20.1"
 
 /* The maximum length of 223 bytes in the RFC. */
 #define ISCSI_NAME_LEN	256
@@ -9,14 +9,17 @@
 
 #define ISCSI_LISTEN_PORT	3260
 
-#define VENDOR_ID_LEN	8
-#define SCSI_ID_LEN	24
-#define SCSI_SN_LEN	16
+#define SCSI_ID_LEN	16
+#define SCSI_SN_LEN	(SCSI_ID_LEN * 2)
 
 #ifndef aligned_u64
 #define aligned_u64 unsigned long long __attribute__((aligned(8)))
 #endif
 
+struct module_info {
+	char version[128];
+};
+
 struct target_info {
 	u32 tid;
 	char name[ISCSI_NAME_LEN];
@@ -131,19 +134,18 @@ struct iet_event {
 
 #define NETLINK_IET	21
 
-#define ADD_TARGET _IOW('i', 0, struct target_info)
-#define DEL_TARGET _IOW('i', 1, struct target_info)
-#define START_TARGET _IO('i', 2)
-#define STOP_TARGET _IO('i', 3)
-#define ADD_VOLUME _IOW('i', 4, struct volume_info)
-#define DEL_VOLUME _IOW('i', 5, struct volume_info)
-#define ADD_SESSION _IOW('i', 6, struct session_info)
-#define DEL_SESSION _IOW('i', 7, struct session_info)
-#define GET_SESSION_INFO _IOWR('i', 8, struct session_info)
-#define ADD_CONN _IOW('i', 9, struct conn_info)
-#define DEL_CONN _IOW('i', 10, struct conn_info)
-#define GET_CONN_INFO _IOWR('i', 11, struct conn_info)
-#define ISCSI_PARAM_SET _IOW('i', 12, struct iscsi_param_info)
-#define ISCSI_PARAM_GET _IOWR('i', 13, struct iscsi_param_info)
+#define GET_MODULE_INFO _IOW('i', 20, struct module_info)
+#define ADD_TARGET _IOWR('i', 21, struct target_info)
+#define DEL_TARGET _IOW('i', 22, struct target_info)
+#define ADD_VOLUME _IOW('i', 24, struct volume_info)
+#define DEL_VOLUME _IOW('i', 25, struct volume_info)
+#define ADD_SESSION _IOW('i', 26, struct session_info)
+#define DEL_SESSION _IOW('i', 27, struct session_info)
+#define GET_SESSION_INFO _IOWR('i', 28, struct session_info)
+#define ADD_CONN _IOW('i', 29, struct conn_info)
+#define DEL_CONN _IOW('i', 30, struct conn_info)
+#define GET_CONN_INFO _IOWR('i', 31, struct conn_info)
+#define ISCSI_PARAM_SET _IOW('i', 32, struct iscsi_param_info)
+#define ISCSI_PARAM_GET _IOWR('i', 33, struct iscsi_param_info)
 
 #endif
diff --git a/ubuntu/iscsitarget/iotype.h b/ubuntu/iscsitarget/iotype.h
index bd7fbd0..db7956a 100644
--- a/ubuntu/iscsitarget/iotype.h
+++ b/ubuntu/iscsitarget/iotype.h
@@ -3,6 +3,7 @@
  * This code is licenced under the GPL.
  */
 
+#include <linux/ctype.h>
 #include "iscsi.h"
 
 #ifndef __IOTYPE_H__
@@ -26,4 +27,16 @@ extern struct iotype blockio;
 extern int iotype_init(void);
 extern void iotype_exit(void);
 
+/* For option parameter parsing.
+ * This is slightly iet specific: we only tolower() up to the first '='.
+ * Note that this changes *c _in place_, but our parsing
+ * routines copy the input to a scratch page before parsing anyways. */
+static inline void iet_strtolower(char *c)
+{
+	if (!c)
+		return;
+	for (; *c && *c != '='; c++)
+		*c = tolower(*c);
+}
+
 #endif
diff --git a/ubuntu/iscsitarget/iscsi.c b/ubuntu/iscsitarget/iscsi.c
index 4f62b8b..05197f2 100644
--- a/ubuntu/iscsitarget/iscsi.c
+++ b/ubuntu/iscsitarget/iscsi.c
@@ -345,6 +345,8 @@ void iscsi_cmnd_set_sense(struct iscsi_cmnd *cmnd, u8 sense_key, u8 asc,
 	cmnd->sense_buf[7] = 6;	// Additional sense length
 	cmnd->sense_buf[12] = asc;
 	cmnd->sense_buf[13] = ascq;
+
+	/* Call to ACA/UAI handler */
 }
 
 static struct iscsi_cmnd *create_sense_rsp(struct iscsi_cmnd *req,
@@ -534,10 +536,20 @@ static int check_cmd_sn(struct iscsi_cmnd *cmnd)
 	u32 cmd_sn;
 
 	cmnd->pdu.bhs.sn = cmd_sn = be32_to_cpu(cmnd->pdu.bhs.sn);
-	dprintk(D_GENERIC, "%d(%d)\n", cmd_sn, session->exp_cmd_sn);
-	if ((s32)(cmd_sn - session->exp_cmd_sn) >= 0)
+
+	dprintk(D_GENERIC, "cmd_sn(%u) exp_cmd_sn(%u) max_cmd_sn(%u)\n",
+		cmd_sn, session->exp_cmd_sn, session->max_cmd_sn);
+
+	if  (between(cmd_sn, session->exp_cmd_sn, session->max_cmd_sn))
+		return 0;
+	else if (cmnd_immediate(cmnd))
 		return 0;
-	eprintk("sequence error (%x,%x)\n", cmd_sn, session->exp_cmd_sn);
+
+	eprintk("sequence error: cmd_sn(%u) exp_cmd_sn(%u) max_cmd_sn(%u)\n",
+		cmd_sn, session->exp_cmd_sn, session->max_cmd_sn);
+
+	set_cmnd_tmfabort(cmnd);
+
 	return -ISCSI_REASON_PROTOCOL_ERROR;
 }
 
@@ -609,7 +621,8 @@ static int cmnd_insert_hash(struct iscsi_cmnd *cmnd)
 	if (!err) {
 		update_stat_sn(cmnd);
 		err = check_cmd_sn(cmnd);
-	}
+	} else if (!cmnd_immediate(cmnd))
+		set_cmnd_tmfabort(cmnd);
 
 	return err;
 }
@@ -826,15 +839,12 @@ static void send_r2t(struct iscsi_cmnd *req)
 
 static void scsi_cmnd_exec(struct iscsi_cmnd *cmnd)
 {
-	if (cmnd->r2t_length) {
-		if (!cmnd->is_unsolicited_data)
-			send_r2t(cmnd);
+	assert(!cmnd->r2t_length);
+
+	if (cmnd->lun) {
+		iscsi_scsi_queuecmnd(cmnd);
 	} else {
-		if (cmnd->lun) {
-			iscsi_scsi_queuecmnd(cmnd);
-		} else {
-			iscsi_device_queue_cmnd(cmnd);
-		}
+		iscsi_device_queue_cmnd(cmnd);
 	}
 }
 
@@ -864,7 +874,7 @@ 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->pdu.bhs.opcode & ISCSI_OP_IMMEDIATE))
+		if (!cmnd_immediate(cmnd))
 			eprintk("%s\n", "initiator bug!");
 		update_stat_sn(cmnd);
 		err = check_cmd_sn(cmnd);
@@ -1093,6 +1103,8 @@ skip_data:
 	return;
 }
 
+static void iscsi_session_push_cmnd(struct iscsi_cmnd *cmnd);
+
 static void data_out_end(struct iscsi_conn *conn, struct iscsi_cmnd *cmnd)
 {
 	struct iscsi_data_out_hdr *req = (struct iscsi_data_out_hdr *) &cmnd->pdu.bhs;
@@ -1116,8 +1128,7 @@ static void data_out_end(struct iscsi_conn *conn, struct iscsi_cmnd *cmnd)
 	if (req->ttt == cpu_to_be32(ISCSI_RESERVED_TAG)) {
 		if (req->flags & ISCSI_FLG_FINAL) {
 			scsi_cmnd->is_unsolicited_data = 0;
-			if (!cmnd_pending(scsi_cmnd))
-				scsi_cmnd_exec(scsi_cmnd);
+			iscsi_session_push_cmnd(scsi_cmnd);
 		}
 	} else {
 		/* TODO : proper error handling */
@@ -1132,7 +1143,7 @@ static void data_out_end(struct iscsi_conn *conn, struct iscsi_cmnd *cmnd)
 		if (scsi_cmnd->r2t_length == 0)
 			assert(list_empty(&scsi_cmnd->pdu_list));
 
-		scsi_cmnd_exec(scsi_cmnd);
+		iscsi_session_push_cmnd(scsi_cmnd);
 	}
 
 out:
@@ -1140,35 +1151,64 @@ out:
 	return;
 }
 
-static int __cmnd_abort(struct iscsi_cmnd *cmnd)
+static void __cmnd_abort(struct iscsi_cmnd *cmnd)
 {
+	if (cmnd_rxstart(cmnd))
+		set_cmnd_tmfabort(cmnd);
+
 	if (cmnd_waitio(cmnd))
-		return -ISCSI_RESPONSE_UNKNOWN_TASK;
+		return;
 
 	if (cmnd->conn->read_cmnd != cmnd)
 		cmnd_release(cmnd, 1);
-	else if (cmnd_rxstart(cmnd))
-		set_cmnd_tmfabort(cmnd);
-	else
-		return -ISCSI_RESPONSE_UNKNOWN_TASK;
-
-	return 0;
 }
 
-static int cmnd_abort(struct iscsi_session *session, u32 itt)
+static int cmnd_abort(struct iscsi_session *session, struct iscsi_cmnd *req)
 {
+	struct iscsi_task_mgt_hdr *req_hdr =
+				(struct iscsi_task_mgt_hdr *)&req->pdu.bhs;
 	struct iscsi_cmnd *cmnd;
-	int err =  -ISCSI_RESPONSE_UNKNOWN_TASK;
-
-	if ((cmnd = cmnd_find_hash(session, itt, ISCSI_RESERVED_TAG))) {
-		eprintk("%x %x %x %u %u %u %u\n", cmnd_itt(cmnd), cmnd_opcode(cmnd),
-			cmnd->r2t_length, cmnd_scsicode(cmnd),
-			cmnd_write_size(cmnd), cmnd->is_unsolicited_data,
-			cmnd->outstanding_r2t);
-		err = __cmnd_abort(cmnd);
+
+	u32 min_cmd_sn = req_hdr->cmd_sn - session->max_queued_cmnds;
+
+	req_hdr->ref_cmd_sn = be32_to_cpu(req_hdr->ref_cmd_sn);
+
+	dprintk(D_GENERIC, "cmd_sn(%u) ref_cmd_sn(%u) min_cmd_sn(%u) rtt(%x)"
+		" lun(%d) cid(%u)\n",
+		req_hdr->cmd_sn, req_hdr->ref_cmd_sn, min_cmd_sn, req_hdr->rtt,
+		translate_lun(req_hdr->lun), req->conn->cid);
+
+	if (after(req_hdr->ref_cmd_sn, req_hdr->cmd_sn))
+		return ISCSI_RESPONSE_FUNCTION_REJECTED;
+
+	if (!(cmnd = cmnd_find_hash(session, req_hdr->rtt, ISCSI_RESERVED_TAG))) {
+		if (between(req_hdr->ref_cmd_sn, min_cmd_sn, req_hdr->cmd_sn))
+			return ISCSI_RESPONSE_FUNCTION_COMPLETE;
+		else
+			return ISCSI_RESPONSE_UNKNOWN_TASK;
 	}
 
-	return err;
+	dprintk(D_GENERIC, "itt(%x) opcode(%x) scsicode(%x) lun(%d) cid(%u)\n",
+		cmnd_itt(cmnd), cmnd_opcode(cmnd), cmnd_scsicode(cmnd),
+		translate_lun(cmnd_hdr(cmnd)->lun), cmnd->conn->cid);
+
+	if (cmnd_opcode(cmnd) == ISCSI_OP_SCSI_TASK_MGT_MSG)
+		return ISCSI_RESPONSE_FUNCTION_REJECTED;
+
+	if (translate_lun(cmnd_hdr(cmnd)->lun) !=
+						translate_lun(req_hdr->lun))
+		return ISCSI_RESPONSE_FUNCTION_REJECTED;
+
+	if (cmnd->conn && test_bit(CONN_ACTIVE, &cmnd->conn->state)) {
+		if (cmnd->conn->cid != req->conn->cid)
+			return ISCSI_RESPONSE_FUNCTION_REJECTED;
+	} else {
+		/* Switch cmnd connection allegiance */
+	}
+
+	__cmnd_abort(cmnd);
+
+	return ISCSI_RESPONSE_FUNCTION_COMPLETE;
 }
 
 static int target_reset(struct iscsi_cmnd *req, u32 lun, int all)
@@ -1187,7 +1227,8 @@ static int target_reset(struct iscsi_cmnd *req, u32 lun, int all)
 
 				if (all)
 					__cmnd_abort(cmnd);
-				else if (translate_lun(cmnd_hdr(cmnd)->lun) == lun)
+				else if (translate_lun(cmnd_hdr(cmnd)->lun)
+									== lun)
 					__cmnd_abort(cmnd);
 			}
 		}
@@ -1214,7 +1255,12 @@ static void task_set_abort(struct iscsi_cmnd *req)
 
 	list_for_each_entry(conn, &session->conn_list, list) {
 		list_for_each_entry_safe(cmnd, tmp, &conn->pdu_list, conn_list) {
-			if (cmnd != req)
+			if (translate_lun(cmnd_hdr(cmnd)->lun)
+					!= translate_lun(cmnd_hdr(req)->lun))
+				continue;
+
+			if (before(cmnd_hdr(cmnd)->cmd_sn,
+					cmnd_hdr(req)->cmd_sn))
 				__cmnd_abort(cmnd);
 		}
 	}
@@ -1274,7 +1320,7 @@ static void execute_task_management(struct iscsi_cmnd *req)
 	struct iscsi_task_mgt_hdr *req_hdr = (struct iscsi_task_mgt_hdr *)&req->pdu.bhs;
 	struct iscsi_task_rsp_hdr *rsp_hdr;
 	u32 lun;
-	int err, function = req_hdr->function & ISCSI_FUNCTION_MASK;
+	int function = req_hdr->function & ISCSI_FUNCTION_MASK;
 
 	rsp = iscsi_cmnd_create_rsp_cmnd(req, 1);
 	rsp_hdr = (struct iscsi_task_rsp_hdr *)&rsp->pdu.bhs;
@@ -1299,8 +1345,7 @@ static void execute_task_management(struct iscsi_cmnd *req)
 
 	switch (function) {
 	case ISCSI_FUNCTION_ABORT_TASK:
-		if ((err = cmnd_abort(conn->session, req_hdr->rtt)) < 0)
-			rsp_hdr->response = -err;
+		rsp_hdr->response = cmnd_abort(conn->session, req);
 		break;
 	case ISCSI_FUNCTION_ABORT_TASK_SET:
 		task_set_abort(req);
@@ -1443,7 +1488,7 @@ static void nop_in_tx_end(struct iscsi_cmnd *cmnd)
 		conn->session->target->trgt_param.nop_timeout = t;
 	}
 
-	dprintk(D_GENERIC, "NOP-In %p, %x: timer %p\n",	cmnd, cmnd_ttt(cmnd),
+	dprintk(D_GENERIC, "NOP-In %p, %x: timer %p\n", cmnd, cmnd_ttt(cmnd),
 		&cmnd->req->timer);
 
 	set_cmnd_timer_active(cmnd->req);
@@ -1700,10 +1745,16 @@ static void iscsi_session_push_cmnd(struct iscsi_cmnd *cmnd)
 	struct list_head *entry;
 	u32 cmd_sn;
 
+	if (cmnd->r2t_length) {
+		if (!cmnd->is_unsolicited_data)
+			send_r2t(cmnd);
+		return;
+	}
+
 	dprintk(D_GENERIC, "%p:%x %u,%u\n",
 		cmnd, cmnd_opcode(cmnd), cmnd->pdu.bhs.sn, session->exp_cmd_sn);
 
-	if (cmnd->pdu.bhs.opcode & ISCSI_OP_IMMEDIATE) {
+	if (cmnd_immediate(cmnd)) {
 		iscsi_cmnd_exec(cmnd);
 		return;
 	}
@@ -1716,24 +1767,16 @@ static void iscsi_session_push_cmnd(struct iscsi_cmnd *cmnd)
 
 			if (list_empty(&session->pending_list))
 				break;
+
 			cmnd = list_entry(session->pending_list.next, struct iscsi_cmnd, list);
 			if (cmnd->pdu.bhs.sn != cmd_sn)
 				break;
-/* 			eprintk("find out-of-order %x %u %u\n", */
-/* 				cmnd_itt(cmnd), cmd_sn, cmnd->pdu.bhs.sn); */
+
 			list_del_init(&cmnd->list);
 			clear_cmnd_pending(cmnd);
 		}
 	} else {
-/* 		eprintk("out-of-order %x %u %u\n", */
-/* 			cmnd_itt(cmnd), cmd_sn, session->exp_cmd_sn); */
-
 		set_cmnd_pending(cmnd);
-		if (before(cmd_sn, session->exp_cmd_sn)) /* close the conn */
-			eprintk("unexpected cmd_sn (%u,%u)\n", cmd_sn, session->exp_cmd_sn);
-
-		if (after(cmd_sn, session->exp_cmd_sn + session->max_queued_cmnds))
-			eprintk("too large cmd_sn (%u,%u)\n", cmd_sn, session->exp_cmd_sn);
 
 		list_for_each(entry, &session->pending_list) {
 			struct iscsi_cmnd *tmp = list_entry(entry, struct iscsi_cmnd, list);
diff --git a/ubuntu/iscsitarget/iscsi.h b/ubuntu/iscsitarget/iscsi.h
index 92ce252..e9d37a4 100644
--- a/ubuntu/iscsitarget/iscsi.h
+++ b/ubuntu/iscsitarget/iscsi.h
@@ -8,6 +8,7 @@
 #ifndef __ISCSI_H__
 #define __ISCSI_H__
 
+#include <linux/blkdev.h>
 #include <linux/completion.h>
 #include <linux/pagemap.h>
 #include <linux/seq_file.h>
@@ -92,6 +93,8 @@ struct worker_thread_info {
 	struct list_head work_queue;
 
 	wait_queue_head_t wthread_sleep;
+
+	struct io_context *wthread_ioc;
 };
 
 struct iscsi_cmnd;
@@ -127,7 +130,6 @@ struct iscsi_target {
 	struct worker_thread_info * wthread_info;
 
 	struct semaphore target_sem;
-	struct completion *done;
 };
 
 struct iscsi_queue {
@@ -149,7 +151,7 @@ struct iet_volume {
 	struct iscsi_queue queue;
 
 	u8 scsi_id[SCSI_ID_LEN];
-	u8 scsi_sn[SCSI_SN_LEN];
+	u8 scsi_sn[SCSI_SN_LEN + 1];
 
 	u32 blk_shift;
 	u64 blk_cnt;
@@ -336,7 +338,6 @@ extern void send_nop_in(struct iscsi_conn *);
 extern struct iscsi_conn *conn_lookup(struct iscsi_session *, u16);
 extern int conn_add(struct iscsi_session *, struct conn_info *);
 extern int conn_del(struct iscsi_session *, struct conn_info *);
-extern void conn_del_all(struct iscsi_session *);
 extern int conn_free(struct iscsi_conn *);
 extern void conn_close(struct iscsi_conn *);
 extern void conn_info_show(struct seq_file *, struct iscsi_session *);
@@ -377,7 +378,6 @@ extern struct file_operations session_seq_fops;
 extern struct iscsi_session *session_lookup(struct iscsi_target *, u64);
 extern int session_add(struct iscsi_target *, struct session_info *);
 extern int session_del(struct iscsi_target *, u64);
-extern void session_del_all(struct iscsi_target *);
 
 /* volume.c */
 extern struct file_operations volume_seq_fops;
@@ -464,8 +464,11 @@ static inline void iscsi_cmnd_set_length(struct iscsi_pdu *pdu)
 #define cmnd_itt(cmnd) cpu_to_be32((cmnd)->pdu.bhs.itt)
 #define cmnd_opcode(cmnd) ((cmnd)->pdu.bhs.opcode & ISCSI_OPCODE_MASK)
 #define cmnd_scsicode(cmnd) cmnd_hdr(cmnd)->scb[0]
+#define cmnd_immediate(cmnd) ((cmnd)->pdu.bhs.opcode & ISCSI_OP_IMMEDIATE)
 
-#define	SECTOR_SIZE_BITS	9
+/* default and maximum scsi level block sizes */
+#define IET_DEF_BLOCK_SIZE	512
+#define IET_MAX_BLOCK_SIZE	4096
 
 enum cmnd_flags {
 	CMND_hashed,
diff --git a/ubuntu/iscsitarget/nthread.c b/ubuntu/iscsitarget/nthread.c
index ec54ead..52cd69a 100644
--- a/ubuntu/iscsitarget/nthread.c
+++ b/ubuntu/iscsitarget/nthread.c
@@ -209,6 +209,7 @@ static int recv(struct iscsi_conn *conn)
 	hdigest = conn->hdigest_type & DIGEST_NONE ? 0 : 1;
 	ddigest = conn->ddigest_type & DIGEST_NONE ? 0 : 1;
 
+next_state:
 	switch (conn->read_state) {
 	case RX_INIT_BHS:
 		assert(!cmnd);
@@ -270,7 +271,7 @@ static int recv(struct iscsi_conn *conn)
 		return res;
 
 	if (conn->read_state != RX_END)
-		return res;
+		goto next_state;
 
 	if (conn->read_size) {
 		eprintk("%d %x %d\n", res, cmnd_opcode(cmnd), conn->read_size);
@@ -525,6 +526,7 @@ static int send(struct iscsi_conn *conn)
 
 	ddigest = conn->ddigest_type != DIGEST_NONE ? 1 : 0;
 
+next_state:
 	switch (conn->write_state) {
 	case TX_INIT:
 		assert(!cmnd);
@@ -555,7 +557,7 @@ static int send(struct iscsi_conn *conn)
 		return res;
 
 	if (conn->write_state != TX_END)
-		return res;
+		goto next_state;
 
 	if (conn->write_size) {
 		eprintk("%d %x %u\n", res, cmnd_opcode(cmnd), conn->write_size);
diff --git a/ubuntu/iscsitarget/null-io.c b/ubuntu/iscsitarget/null-io.c
index cfa5899..f64b18c 100644
--- a/ubuntu/iscsitarget/null-io.c
+++ b/ubuntu/iscsitarget/null-io.c
@@ -16,41 +16,44 @@
 #include "iscsi_dbg.h"
 #include "iotype.h"
 
-struct nullio_data {
-	u64 sectors;
-};
-
 enum {
-	Opt_sectors, Opt_ignore, Opt_err,
+	opt_blk_cnt, opt_ignore, opt_err,
 };
 
 static match_table_t tokens = {
-	{Opt_sectors, "Sectors=%u"},
-	{Opt_ignore, "Type=%s"},
-	{Opt_err, NULL},
+	/* alias for compatibility with existing setups and documentation */
+	{opt_blk_cnt, "sectors=%u"},
+	/* but actually it is the scsi block count, now that we can
+	 * specify the block size. */
+	{opt_blk_cnt, "blocks=%u"},
+	{opt_ignore, "scsiid=%s"},
+	{opt_ignore, "scsisn=%s"},
+	{opt_ignore, "blocksize=%s"},
+	{opt_ignore, "type=%s"},
+	{opt_err, NULL},
 };
 
 static int parse_nullio_params(struct iet_volume *volume, char *params)
 {
 	int err = 0;
 	char *p, *q;
-	struct nullio_data *data = volume->private;
 
 	while ((p = strsep(&params, ",")) != NULL) {
 		substring_t args[MAX_OPT_ARGS];
 		int token;
 		if (!*p)
 			continue;
+		iet_strtolower(p);
 		token = match_token(p, tokens, args);
 		switch (token) {
-		case Opt_sectors:
+		case opt_blk_cnt:
 			q = match_strdup(&args[0]);
 			if (!q)
 				return -ENOMEM;
-			data->sectors = simple_strtoull(q, NULL, 10);
+			volume->blk_cnt = simple_strtoull(q, NULL, 10);
 			kfree(q);
 			break;
-		case Opt_ignore:
+		case opt_ignore:
 			break;
 		default:
 			eprintk("Unknown %s\n", p);
@@ -63,35 +66,23 @@ static int parse_nullio_params(struct iet_volume *volume, char *params)
 
 static void nullio_detach(struct iet_volume *lu)
 {
-	struct nullio_data *p = lu->private;
-
-	kfree(p);
-	lu->private = NULL;
 }
 
 static int nullio_attach(struct iet_volume *lu, char *args)
 {
 	int err = 0;
-	struct nullio_data *p;
-
-	if (lu->private) {
-		printk("already attached ? %d\n", lu->lun);
-		return -EBUSY;
-	}
-
-	p = kzalloc(sizeof(*p), GFP_KERNEL);
-	if (!p)
-		return -ENOMEM;
-
-	lu->private = p;
 
 	if ((err = parse_nullio_params(lu, args)) < 0) {
 		eprintk("%d\n", err);
 		goto out;
 	}
 
-	lu->blk_shift = SECTOR_SIZE_BITS;
-	lu->blk_cnt = (p->sectors = p->sectors ? : 1 << 27); /* 64 GB */
+	if (!lu->blk_shift)
+		lu->blk_shift = ilog2(IET_DEF_BLOCK_SIZE);
+
+	/* defaults to 64 GiB */
+	if (!lu->blk_cnt)
+		lu->blk_cnt = 1 << (36 - lu->blk_shift);
 
 out:
 	if (err < 0)
@@ -99,16 +90,9 @@ out:
 	return err;
 }
 
-void nullio_show(struct iet_volume *lu, struct seq_file *seq)
-{
-	struct nullio_data *p = lu->private;
-	seq_printf(seq, " sectors:%llu\n", p->sectors);
-}
-
 struct iotype nullio =
 {
 	.name = "nullio",
 	.attach = nullio_attach,
 	.detach = nullio_detach,
-	.show = nullio_show,
 };
diff --git a/ubuntu/iscsitarget/param.c b/ubuntu/iscsitarget/param.c
index 57ad301..482d00c 100644
--- a/ubuntu/iscsitarget/param.c
+++ b/ubuntu/iscsitarget/param.c
@@ -43,7 +43,7 @@ static void sess_param_check(struct iscsi_param_info *info)
 {
 	u32 *iparam = info->session_param;
 
-	CHECK_PARAM(info, iparam, max_connections, 1, 1);
+	CHECK_PARAM(info, iparam, max_connections, 1, 65535);
 	CHECK_PARAM(info, iparam, max_recv_data_length, 512,
 		    (u32) ((ISCSI_CONN_IOV_MAX - 1) * PAGE_CACHE_SIZE));
 	CHECK_PARAM(info, iparam, max_xmit_data_length, 512,
diff --git a/ubuntu/iscsitarget/session.c b/ubuntu/iscsitarget/session.c
index 6365373..a566d8b 100644
--- a/ubuntu/iscsitarget/session.c
+++ b/ubuntu/iscsitarget/session.c
@@ -124,40 +124,30 @@ int session_add(struct iscsi_target *target, struct session_info *info)
 int session_del(struct iscsi_target *target, u64 sid)
 {
 	struct iscsi_session *session;
+	struct iet_volume *volume;
 
 	session = session_lookup(target, sid);
 	if (!session)
 		return -ENOENT;
 
 	if (!list_empty(&session->conn_list)) {
-		eprintk("%llu still have connections\n", (unsigned long long) session->sid);
-		return -EBUSY;
-	}
+		DECLARE_COMPLETION_ONSTACK(done);
+		struct iscsi_conn *conn;
 
-	return session_free(session);
-}
-
-void session_del_all(struct iscsi_target *target)
-{
-	DECLARE_COMPLETION_ONSTACK(done);
-	struct iscsi_session *sess;
+		session->done = &done;
+		list_for_each_entry(conn, &session->conn_list, list)
+			conn_close(conn);
 
-	while (!list_empty(&target->session_list)) {
-		init_completion(&done);
-		target_lock(target, 0);
-		sess = list_entry(target->session_list.next, struct
-				  iscsi_session, list);
-		sess->done = &done;
-		conn_del_all(sess);
 		target_unlock(target);
 		wait_for_completion(&done);
 		target_lock(target, 0);
-		session_free(sess);
-		target_unlock(target);
 	}
 
-	if (target->done)
-		complete(target->done);
+	list_for_each_entry(volume, &target->volumes, list){
+		volume_release(volume, sid, 0);
+	}
+
+	return session_free(session);
 }
 
 static void iet_session_info_show(struct seq_file *seq, struct iscsi_target *target)
diff --git a/ubuntu/iscsitarget/target.c b/ubuntu/iscsitarget/target.c
index 15c0715..43326dc 100644
--- a/ubuntu/iscsitarget/target.c
+++ b/ubuntu/iscsitarget/target.c
@@ -192,21 +192,23 @@ out:
 
 int target_add(struct target_info *info)
 {
-	int err = -EEXIST;
 	u32 tid = info->tid;
+	int err;
 
-	down(&target_list_sem);
+	err = down_interruptible(&target_list_sem);
+	if (err < 0)
+		return err;
 
 	if (nr_targets > MAX_NR_TARGETS) {
 		err = -EBUSY;
 		goto out;
 	}
 
-	if (__target_lookup_by_name(info->name))
-		goto out;
-
-	if (tid && __target_lookup_by_id(tid))
+	if (__target_lookup_by_name(info->name) || 
+			(tid && __target_lookup_by_id(tid))) {
+		err = -EEXIST;
 		goto out;
+	}
 
 	if (!tid) {
 		do {
@@ -217,7 +219,8 @@ int target_add(struct target_info *info)
 		tid = next_target_id;
 	}
 
-	if (!(err = iscsi_target_create(info, tid)))
+	err = iscsi_target_create(info, tid);
+	if (!err)
 		nr_targets++;
 out:
 	up(&target_list_sem);
@@ -246,13 +249,24 @@ static void target_destroy(struct iscsi_target *target)
 }
 
 /* @locking: target_list_sem must be locked */
-int __target_del(struct iscsi_target *target)
+static int __target_del(struct iscsi_target *target)
 {
+	int err;
+
 	target_lock(target, 0);
 
 	if (!list_empty(&target->session_list)) {
-		target_unlock(target);
-		return -EBUSY;
+		struct iscsi_session *session;
+
+		do {
+			session = list_entry(target->session_list.next,
+						struct iscsi_session, list);
+			err = session_del(target, session->sid);
+			if (err < 0) {
+				target_unlock(target);
+				return err;
+			}
+		} while (!list_empty(&target->session_list));
 	}
 
 	list_del(&target->t_list);
@@ -260,13 +274,16 @@ int __target_del(struct iscsi_target *target)
 
 	target_unlock(target);
 	target_destroy(target);
+
 	return 0;
 }
 
 int target_del(u32 id)
 {
 	struct iscsi_target *target;
-	int err = down_interruptible(&target_list_sem);
+	int err;
+
+	err = down_interruptible(&target_list_sem);
 	if (err < 0)
 		return err;
 
@@ -277,15 +294,16 @@ int target_del(u32 id)
 	}
 
 	err = __target_del(target);
- out:
+out:
 	up(&target_list_sem);
+
 	return err;
 }
 
 void target_del_all(void)
 {
-	DECLARE_COMPLETION_ONSTACK(done);
 	struct iscsi_target *target, *tmp;
+	int err;
 
 	down(&target_list_sem);
 
@@ -293,11 +311,10 @@ void target_del_all(void)
 		iprintk("Removing all connections, sessions and targets\n");
 
 	list_for_each_entry_safe(target, tmp, &target_list, t_list) {
-		init_completion(&done);
-		target->done = &done;
-		session_del_all(target);
-		wait_for_completion(&done);
-		__target_del(target);
+		u32 tid = target->tid;
+		err =__target_del(target);
+		if (err)
+			eprintk("Error deleteing target %u: %d\n", tid, err);
 	}
 
 	next_target_id = 0;
diff --git a/ubuntu/iscsitarget/target_disk.c b/ubuntu/iscsitarget/target_disk.c
index 694edb2..1f7693c 100644
--- a/ubuntu/iscsitarget/target_disk.c
+++ b/ubuntu/iscsitarget/target_disk.c
@@ -193,11 +193,11 @@ static void build_inquiry_response(struct iscsi_cmnd *cmnd)
 		data[7] = 0x02;
 		memset(data + 8, 0x20, 28);
 		memcpy(data + 8,
-		       VENDOR_ID, min_t(size_t, strlen(VENDOR_ID), 8));
+			VENDOR_ID, min_t(size_t, strlen(VENDOR_ID), 8));
 		memcpy(data + 16,
-		       PRODUCT_ID, min_t(size_t, strlen(PRODUCT_ID), 16));
+			PRODUCT_ID, min_t(size_t, strlen(PRODUCT_ID), 16));
 		memcpy(data + 32,
-		       PRODUCT_REV, min_t(size_t, strlen(PRODUCT_REV), 4));
+			PRODUCT_REV, min_t(size_t, strlen(PRODUCT_REV), 4));
 		data[58] = 0x03;
 		data[59] = 0x20;
 		data[60] = 0x09;
@@ -217,35 +217,41 @@ static void build_inquiry_response(struct iscsi_cmnd *cmnd)
 			tio_set(tio, 7, 0);
 			err = 0;
 		} else if (scb[2] == 0x80) {
-			int len = (cmnd->lun && strlen(cmnd->lun->scsi_sn)) ?
-				SCSI_SN_LEN : 4;
+			u32 len = 4;
+
+			if (cmnd->lun) {
+				if (strlen(cmnd->lun->scsi_sn) <= 16)
+					len = 16;
+				else
+					len = SCSI_SN_LEN;
+			}
 
 			data[1] = 0x80;
 			data[3] = len;
 			memset(data + 4, 0x20, len);
+			if (cmnd->lun) {
+				size_t offset = len -
+						strlen(cmnd->lun->scsi_sn);
+				memcpy(data + 4 + offset, cmnd->lun->scsi_sn,
+						strlen(cmnd->lun->scsi_sn));
+			}
 			tio_set(tio, len + 4, 0);
 			err = 0;
-
-			if (len == SCSI_SN_LEN) {
-				char *p, *q;
-
-				p = data + 4 + len - 1;
-				q = cmnd->lun->scsi_sn + len - 1;
-
-				for (; len > 0; len--, q--)
-					if (isascii(*q) && isprint(*q))
-						*(p--) = *q;
-			}
 		} else if (scb[2] == 0x83) {
-			u32 len = SCSI_ID_LEN * sizeof(u8);
+			u32 len = SCSI_ID_LEN + 8;
 
 			data[1] = 0x83;
 			data[3] = len + 4;
 			data[4] = 0x1;
 			data[5] = 0x1;
 			data[7] = len;
-			if (cmnd->lun) /* We need this ? */
-				memcpy(data + 8, cmnd->lun->scsi_id, len);
+			if (cmnd->lun) { /* We need this ? */
+				memset(data + 8, 0x00, 8);
+				memcpy(data + 8, VENDOR_ID, 
+					min_t(size_t, strlen(VENDOR_ID), 8));
+				memcpy(data + 16, cmnd->lun->scsi_id,
+								SCSI_ID_LEN);
+			}
 			tio_set(tio, len + 8, 0);
 			err = 0;
 		}
@@ -432,9 +438,19 @@ static void build_reserve_response(struct iscsi_cmnd *cmnd)
 
 static void build_release_response(struct iscsi_cmnd *cmnd)
 {
-	if (volume_release(cmnd->lun,
-			   cmnd->conn->session->sid, 0))
+	int ret = volume_release(cmnd->lun,
+				 cmnd->conn->session->sid, 0);
+	switch (ret) {
+	case -ENOENT:
+		/* Logical Unit Not Supported (?) */
+		iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, 0x25, 0x0);
+		break;
+	case -EBUSY:
 		cmnd->status = SAM_STAT_RESERVATION_CONFLICT;
+		break;
+	default:
+		break;
+	}
 }
 
 static void build_reservation_conflict_response(struct iscsi_cmnd *cmnd)
@@ -475,8 +491,9 @@ static int disk_check_reservation(struct iscsi_cmnd *cmnd)
 {
 	struct iscsi_scsi_cmd_hdr *req = cmnd_hdr(cmnd);
 
-	if (is_volume_reserved(cmnd->lun,
-			       cmnd->conn->session->sid)) {
+	int ret = is_volume_reserved(cmnd->lun,
+				     cmnd->conn->session->sid);
+	if (ret == -EBUSY) {
 		switch (req->scb[0]) {
 		case INQUIRY:
 		case RELEASE:
diff --git a/ubuntu/iscsitarget/ua.c b/ubuntu/iscsitarget/ua.c
index db08169..c4ba4c2 100644
--- a/ubuntu/iscsitarget/ua.c
+++ b/ubuntu/iscsitarget/ua.c
@@ -109,6 +109,7 @@ void ua_establish_for_session(struct iscsi_session *sess, u32 lun,
 {
 	struct list_head *l = &sess->ua_hash[ua_hashfn(lun)];
 	struct ua_entry *ua = kmem_cache_alloc(ua_cache, GFP_KERNEL);
+	struct ua_entry *e;
 
 	if (!ua) {
 		eprintk("%s", "Failed to alloc ua");
@@ -119,8 +120,19 @@ void ua_establish_for_session(struct iscsi_session *sess, u32 lun,
 	ua->ascq = ascq;
 	ua->lun = lun;
 	ua->session = sess;
+	INIT_LIST_HEAD(&ua->entry);
 
 	spin_lock(&sess->ua_hash_lock);
+	/* One UA per occurrence of an event */
+	list_for_each_entry(e, l, entry) {
+		if (e->session == sess && e->lun == lun &&
+				e->asc == asc && e->ascq == ascq &&
+				e->session->exp_cmd_sn == sess->exp_cmd_sn) {
+			spin_unlock(&sess->ua_hash_lock);
+			ua_free(ua);
+			return;
+		}
+	}
 	list_add_tail(&ua->entry, l);
 	spin_unlock(&sess->ua_hash_lock);
 
diff --git a/ubuntu/iscsitarget/volume.c b/ubuntu/iscsitarget/volume.c
index ca0d9ca..6abe16b 100644
--- a/ubuntu/iscsitarget/volume.c
+++ b/ubuntu/iscsitarget/volume.c
@@ -6,6 +6,7 @@
 
 #include <linux/types.h>
 #include <linux/parser.h>
+#include <linux/log2.h>
 
 #include "iscsi.h"
 #include "iscsi_dbg.h"
@@ -23,25 +24,108 @@ struct iet_volume *volume_lookup(struct iscsi_target *target, u32 lun)
 }
 
 enum {
-	Opt_type,
-	Opt_iomode,
-	Opt_err,
+	opt_type,
+	opt_iomode,
+	opt_scsiid,
+	opt_scsisn,
+	opt_blk_size,
+	opt_err,
 };
 
 static match_table_t tokens = {
-	{Opt_type, "Type=%s"},
-	{Opt_iomode, "IOMode=%s"},
-	{Opt_err, NULL},
+	{opt_type, "type=%s"},
+	{opt_iomode, "iomode=%s"},
+	{opt_scsiid, "scsiid=%s"},
+	{opt_scsisn, "scsisn=%s"},
+	{opt_blk_size, "blocksize=%u"},
+	{opt_err, NULL},
 };
 
-static int set_iotype(struct iet_volume *volume, char *params)
+static int set_scsiid(struct iet_volume *volume, const char *id)
+{
+	size_t len;
+
+	if ((len = strlen(id)) > SCSI_ID_LEN) {
+		eprintk("SCSI ID too long, %zd provided, %u max\n", len,
+								SCSI_ID_LEN);
+		return -EINVAL;
+	}
+
+	memcpy(volume->scsi_id, id, len);
+
+	return 0;
+}
+
+static int set_scsisn(struct iet_volume *volume, const char *sn)
+{
+	size_t len;
+	int i;
+
+	if ((len = strlen(sn)) > SCSI_SN_LEN) {
+		eprintk("SCSI SN too long, %zd provided, %u max\n", len,
+								SCSI_SN_LEN);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < len; i++) {
+		if (!isascii(*(sn + i)) || !isprint(*(sn + i))) {
+			eprintk("invalid characters in SCSI SN, %s\n",
+				"only printable ascii characters allowed!");
+			return -EINVAL;
+		}
+	}
+
+	memcpy(volume->scsi_sn, sn, len);
+
+	return 0;
+}
+
+/* Generate a MD5 hash of the target IQN and LUN number */
+static void gen_scsiid(struct iet_volume *volume)
+{
+	struct hash_desc hash;
+
+	hash.tfm = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC);
+	hash.flags = 0;
+
+	if (hash.tfm) {
+		struct scatterlist sg[2];
+		unsigned int nbytes = 0;
+
+		sg_init_table(sg, 2);
+
+		sg_set_buf(&sg[0], volume->target->name,
+					strlen(volume->target->name));
+		nbytes += strlen(volume->target->name);
+
+		sg_set_buf(&sg[1], &volume->lun, sizeof(volume->lun));
+		nbytes += sizeof(volume->lun);
+
+		crypto_hash_init(&hash);
+		crypto_hash_update(&hash, sg, nbytes);
+		crypto_hash_final(&hash, volume->scsi_id);
+
+		crypto_free_hash(hash.tfm);
+	} else {
+		/* If no MD5 available set ID to TID and LUN */
+		memcpy(volume->scsi_id, &volume->target->tid,
+						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)
 {
 	int err = 0;
+	unsigned blk_sz;
 	substring_t args[MAX_OPT_ARGS];
 	char *p, *argp = NULL, *buf = (char *) get_zeroed_page(GFP_USER);
 
 	if (!buf)
 		return -ENOMEM;
+
 	strncpy(buf, params, PAGE_CACHE_SIZE);
 
 	while ((p = strsep(&buf, ",")) != NULL) {
@@ -49,22 +133,65 @@ static int set_iotype(struct iet_volume *volume, char *params)
 
 		if (!*p)
 			continue;
+		iet_strtolower(p);
 		token = match_token(p, tokens, args);
 		switch (token) {
-		case Opt_type:
-			if (!(argp = match_strdup(&args[0])))
+		case opt_type:
+			argp = match_strdup(&args[0]);
+			if (!argp) {
 				err = -ENOMEM;
-			if (argp && !(volume->iotype = get_iotype(argp)))
+				break;
+			}
+			if (!(volume->iotype = get_iotype(argp)))
 				err = -ENOENT;
 			kfree(argp);
 			break;
-		case Opt_iomode:
-			if (!(argp = match_strdup(&args[0])))
+		case opt_iomode:
+			argp = match_strdup(&args[0]);
+			if (!argp) {
 				err = -ENOMEM;
-			if (argp && !strcmp(argp, "ro"))
+				break;
+			}
+			if (!strcmp(argp, "ro"))
 				SetLUReadonly(volume);
-			else if (argp && !strcmp(argp, "wb"))
+			else if (!strcmp(argp, "wb"))
 				SetLUWCache(volume);
+			else if (strcmp(argp, "wt"))
+				err = -EINVAL;
+			kfree(argp);
+			break;
+		case opt_scsiid:
+			argp = match_strdup(&args[0]);
+			if (!argp) {
+				err = -ENOMEM;
+				break;
+			}
+			err = set_scsiid(volume, argp);
+			kfree(argp);
+			break;
+		case opt_scsisn:
+			argp = match_strdup(&args[0]);
+			if (!argp) {
+				err = -ENOMEM;
+				break;
+			}
+			err = set_scsisn(volume, argp);
+			kfree(argp);
+			break;
+		case opt_blk_size:
+			argp = match_strdup(&args[0]);
+			if (!argp) {
+				err = -ENOMEM;
+				break;
+			}
+			blk_sz = simple_strtoull(argp, NULL, 10);
+			if (is_power_of_2(blk_sz) &&
+			    512 <= blk_sz && blk_sz <= IET_MAX_BLOCK_SIZE)
+				volume->blk_shift = ilog2(blk_sz);
+			else {
+				eprintk("invalid BlockSize=%u\n", blk_sz);
+				err = -EINVAL;
+			}
 			kfree(argp);
 			break;
 		default:
@@ -115,7 +242,7 @@ int volume_add(struct iscsi_target *target, struct volume_info *info)
 		goto free_args;
 	}
 
-	ret = set_iotype(volume, args);
+	ret = parse_volume_params(volume, args);
 	if (ret < 0)
 		goto free_args;
 
@@ -123,6 +250,17 @@ int volume_add(struct iscsi_target *target, struct volume_info *info)
 	if (ret < 0)
 		goto free_args;
 
+	if (!volume->scsi_id[0])
+		gen_scsiid(volume);
+
+	if (!volume->scsi_sn[0]) {
+		int i;
+
+		for (i = 0; i < SCSI_ID_LEN; i++)
+			snprintf(volume->scsi_sn + (i * 2), 3, "%02x",
+							volume->scsi_id[i]);
+	}
+
 	INIT_LIST_HEAD(&volume->queue.wait_list);
 	spin_lock_init(&volume->queue.queue_lock);
 	spin_lock_init(&volume->reserve_lock);
@@ -193,35 +331,54 @@ void volume_put(struct iet_volume *volume)
 
 int volume_reserve(struct iet_volume *volume, u64 sid)
 {
+	int err = 0;
+
 	if (!volume)
 		return -ENOENT;
 
 	spin_lock(&volume->reserve_lock);
-	if (volume->reserve_sid && volume->reserve_sid != sid) {
-		spin_unlock(&volume->reserve_lock);
-		return -EBUSY;
-	}
+	if (volume->reserve_sid && volume->reserve_sid != sid)
+		err = -EBUSY;
+	else
+		volume->reserve_sid = sid;
 
-	volume->reserve_sid = sid;
 	spin_unlock(&volume->reserve_lock);
-
-	return 0;
+	return err;
 }
 
 int is_volume_reserved(struct iet_volume *volume, u64 sid)
 {
-	if (!volume || !volume->reserve_sid || volume->reserve_sid == sid)
-		return 0;
+	int err = 0;
 
-	return -EBUSY;
+	if (!volume)
+		return -ENOENT;
+
+	spin_lock(&volume->reserve_lock);
+	if (!volume->reserve_sid || volume->reserve_sid == sid)
+		err = 0;
+	else
+		err = -EBUSY;
+
+	spin_unlock(&volume->reserve_lock);
+	return err;
 }
 
 int volume_release(struct iet_volume *volume, u64 sid, int force)
 {
+	int err = 0;
+
+	if (!volume)
+		return -ENOENT;
+
+	spin_lock(&volume->reserve_lock);
+
 	if (force || volume->reserve_sid == sid)
 		volume->reserve_sid = 0;
+	else
+		err = -EBUSY;
 
-	return 0;
+	spin_unlock(&volume->reserve_lock);
+	return err;
 }
 
 static void iet_volume_info_show(struct seq_file *seq, struct iscsi_target *target)
@@ -238,6 +395,8 @@ static void iet_volume_info_show(struct seq_file *seq, struct iscsi_target *targ
 		else
 			seq_printf(seq, " iomode:wt");
 
+		seq_printf(seq, " blocks:%llu blocksize:%u",
+			volume->blk_cnt, 1 << volume->blk_shift);
 		if (volume->iotype->show)
 			volume->iotype->show(volume, seq);
 		else
diff --git a/ubuntu/iscsitarget/wthread.c b/ubuntu/iscsitarget/wthread.c
index b49ddb7..997a3d6 100644
--- a/ubuntu/iscsitarget/wthread.c
+++ b/ubuntu/iscsitarget/wthread.c
@@ -67,6 +67,15 @@ 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)
+		copy_io_context(&current->io_context, &info->wthread_ioc);
+	else
+		info->wthread_ioc = current->io_context;
+
 	add_wait_queue(&info->wthread_sleep, &wait);
 
 	__set_current_state(TASK_RUNNING);
@@ -74,12 +83,15 @@ static int worker_thread(void *arg)
 		while (!list_empty(&info->work_queue) &&
 		       (cmnd = get_ready_cmnd(info))) {
 			conn = cmnd->conn;
-			cmnd_execute(cmnd);
+			if (cmnd_tmfabort(cmnd))
+				cmnd_release(cmnd, 1);
+			else
+				cmnd_execute(cmnd);
 			assert(conn);
 			atomic_dec(&conn->nr_busy_cmnds);
 		}
 
-		__set_current_state(TASK_INTERRUPTIBLE);
+		set_current_state(TASK_INTERRUPTIBLE);
 		if (list_empty(&info->work_queue))
 			schedule();
 
@@ -88,6 +100,16 @@ static int worker_thread(void *arg)
 
 	remove_wait_queue(&info->wthread_sleep, &wait);
 
+	if (current->io_context) {
+		struct io_context *ioc = current->io_context;
+
+		task_lock(current);
+		current->io_context = NULL;
+		task_unlock(current);
+
+		put_io_context(ioc);
+	}
+
 	return 0;
 }
 
@@ -138,6 +160,7 @@ int wthread_init(struct worker_thread_info *info)
 	spin_lock_init(&info->wthread_lock);
 
 	info->nr_running_wthreads = 0;
+	info->wthread_ioc = NULL;
 
 	INIT_LIST_HEAD(&info->work_queue);
 	INIT_LIST_HEAD(&info->wthread_list);
