Index: target/iscsi_target_iblock.c
===================================================================
--- target/iscsi_target_iblock.c	(revision 293)
+++ target/iscsi_target_iblock.c	(working copy)
@@ -234,6 +234,13 @@
 	ib_dev->ibd_major = di->iblock_major;
 	ib_dev->ibd_minor = di->iblock_minor;
 
+	if (!(ib_dev->ibd_bio_set = bioset_create(32, 64))) {
+		TRACE_ERROR("Unable to create bioset for IBLOCK\n");
+		dump_stack();
+		goto failed;
+	}
+	printk("IBLOCK: Created bio_set() for major/minor: %d:%d\n",
+		ib_dev->ibd_major, ib_dev->ibd_minor);
 	/*
 	 * Pass dev_flags for linux_blockdevice_claim() above..
 	 */
@@ -246,8 +253,9 @@
 	return(1);
 
 failed:
+	if (ib_dev->ibd_bio_set)
+		bioset_free(ib_dev->ibd_bio_set);
 	kfree(ib_dev);
-
 	return(0);
 }
 
@@ -327,6 +335,11 @@
 {
 	iblock_dev_t *ib_dev = (iblock_dev_t *) dev->dev_ptr;
 
+	if (ib_dev->ibd_bio_set) {
+		printk("Calling bioset_free ib_dev->ibd_bio_set: %p\n", ib_dev->ibd_bio_set);
+		bioset_free(ib_dev->ibd_bio_set);
+	}
+
 	transport_generic_release_phydevice(dev, 0);
 	kfree(ib_dev);
 
@@ -355,6 +368,7 @@
 	memset(ib_req, 0, sizeof(iblock_req_t));
 
 	ib_req->ib_dev = (iblock_dev_t *) dev->dev_ptr;
+	spin_lock_init(&ib_req->ib_bio_lock);
 	
 	return((void *)ib_req);
 }
@@ -483,12 +497,27 @@
 
 extern int iblock_do_task (iscsi_task_t *task)
 {
-	iblock_req_t *req = (iblock_req_t *) task->transport_req;
+	iblock_req_t *req = (iblock_req_t *)task->transport_req;
+	iblock_dev_t *ibd = (iblock_dev_t *)req->ib_dev;
+	request_queue_t *q = bdev_get_queue(ibd->ibd_bd);
+	struct bio *bio = req->ib_bio, *nbio = NULL;
 
 	if (!(task->iscsi_cmd->cmd_flags & ICF_SCSI_DATA_SG_IO_CDB))
 		return(iblock_emulate_scsi_cdb(task));
 
-	submit_bio((task->iscsi_cmd->data_direction == ISCSI_WRITE), req->ib_bio);
+	while (bio) {
+		nbio = bio->bi_next;
+		bio->bi_next = NULL;
+#if 0
+		printk("Calling submit_bio() task: %p bio: %p bio->bi_sector: %llu\n",
+				task, bio, bio->bi_sector);
+#endif
+		submit_bio((task->iscsi_cmd->data_direction == ISCSI_WRITE), bio);
+		bio = nbio;
+	}
+
+	if (q && q->unplug_fn)
+		q->unplug_fn(q);
 	
 	return(PYX_TRANSPORT_SENT_TO_TRANSPORT);
 }
@@ -497,8 +526,10 @@
 {
 	iblock_req_t *req = (iblock_req_t *) task->transport_req;
 
-	if (req->ib_bio)
-		bio_put(req->ib_bio);
+	/*
+	 * We do not release the bio(s) here associated with this task, as this is
+	 * handled by bio_put() and iblock_bio_destructor().
+	 */
 
 	kfree(req);
 	task->transport_req = NULL;
@@ -590,47 +621,108 @@
 	return;
 }
 
-extern int iblock_map_task_SG (iscsi_task_t *task)
+static void iblock_bio_destructor (struct bio *bio)
 {
-	int i = 0;
+	iscsi_task_t *task = (iscsi_task_t *)bio->bi_private;
 	iblock_dev_t *ib_dev = (iblock_dev_t *) task->iscsi_dev->dev_ptr;
-	iblock_req_t *ib_req = (iblock_req_t *) task->transport_req;
+
+	bio_free(bio, ib_dev->ibd_bio_set);
+}
+
+static struct bio *iblock_get_bio (iscsi_task_t *task,
+	iblock_req_t *ib_req,
+	iblock_dev_t *ib_dev,
+	int *ret,
+	u64 lba,
+	u32 sg_num)
+{
 	struct bio *bio;
-	struct bio_vec *bio_v;
-	struct scatterlist *sg = (struct scatterlist *)task->task_buf;
 
-	if (!(bio = bio_alloc(GFP_KERNEL, task->task_sg_num))) {
+	if (!(bio = bio_alloc_bioset(GFP_NOIO, sg_num, ib_dev->ibd_bio_set))) {
 		TRACE_ERROR("Unable to allocate memory for bio\n");
-		return(-1);
+		*ret = PYX_TRANSPORT_OUT_OF_MEMORY_RESOURCES;
+		return(NULL);
 	}
-
-	ib_req->ib_bio = bio;
+#if 0
+	printk("Allocated bio: %p task_sg_num: %u using ibd_bio_set: %p\n",
+		bio, task->task_sg_num, ib_dev->ibd_bio_set);
+	printk("Allocated bio: %p task_size: %u\n", bio, task->task_size);
+#endif
 	bio->bi_bdev = ib_dev->ibd_bd;
 	bio->bi_private = (void *) task;
+	bio->bi_destructor = iblock_bio_destructor;
 	bio->bi_end_io = &iblock_bio_done;
+	bio->bi_sector = lba;
+	atomic_inc(&ib_req->ib_bio_cnt);
+#if 0
+	printk("Set bio->bi_sector: %llu\n", bio->bi_sector);
+	printk("Set ib_req->ib_bio_cnt: %d\n", atomic_read(&ib_req->ib_bio_cnt));
+#endif
 
+	return(bio);
+}
+
+extern int iblock_map_task_SG (iscsi_task_t *task)
+{
+	iblock_dev_t *ib_dev = (iblock_dev_t *) task->iscsi_dev->dev_ptr;
+	iblock_req_t *ib_req = (iblock_req_t *) task->transport_req;
+	struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
+	struct scatterlist *sg = (struct scatterlist *)task->task_buf;
+	int ret = 0;
+	u32 i, sg_num = task->task_sg_num;
+	u64 lba = task->task_lba;
+
+	atomic_set(&ib_req->ib_bio_cnt, 0);
+
+	if (!(bio = iblock_get_bio(task, ib_req, ib_dev, &ret, task->task_lba, sg_num)))
+		return(ret);
+
+	ib_req->ib_bio = bio;
+	hbio = tbio = bio;
 	/*
-	 * Populate the bio->bi_io_vec array from the already calucated
-	 * iscsi_task_t
+	 * Use fs/bio.c:bio_add_pages() to setup the bio_vec maplist
+	 * from LIO-SE se_mem_t -> task->task_buf -> struct scatterlist memory.
 	 */
 	for (i = 0; i < task->task_sg_num; i++) {
-		bio_v = &bio->bi_io_vec[i];
+#if 0
+		printk("task: %p bio: %p Calling bio_add_page(): page: %p len: %u offset: %u\n",
+			task, bio, GET_PAGE_SG(&sg[i]), sg[i].length, sg[i].offset);
+#endif
+again:
+		if ((ret = bio_add_page(bio, GET_PAGE_SG(&sg[i]), sg[i].length,
+				sg[i].offset)) != sg[i].length) {
+#if 0
+			printk("*** Set bio->bi_sector: %llu\n", bio->bi_sector);
+			printk("** task->task_size: %u\n", task->task_size);
+			printk("*** bio->bi_max_vecs: %u\n", bio->bi_max_vecs);
+			printk("*** bio->bi_vcnt: %u\n", bio->bi_vcnt);
+#endif
+			if (!(bio = iblock_get_bio(task, ib_req, ib_dev, &ret, lba, sg_num)))
+				goto fail;
 
-		bio_v->bv_page = GET_PAGE_SG(&sg[i]);
-		bio_v->bv_len = sg[i].length;
-		bio_v->bv_offset = sg[i].offset;
+			tbio = tbio->bi_next = bio;
+//			printk("-----------------> Added +1 bio: %p to list, Going to again\n", bio);
+			goto again;
+		}
+
+		lba += sg[i].length >> IBLOCK_LBA_SHIFT;
+		sg_num--;
+#if 0
+		printk("task: %p bio-add_page() passed!, decremented sg_num to %u\n", task, sg_num);
+		printk("task: %p bio_add_page() passed!, increased lba to %llu\n", task, lba);
+		printk("task: %p bio_add_page() passed!, bio->bi_vcnt: %u\n", task, bio->bi_vcnt);
+#endif
 	}
 
-	/*
-	 * Fill in the remaing members of struct bio
-	 */
-	bio->bi_sector = task->task_lba;
-	bio->bi_size = task->task_size;
-	bio->bi_vcnt = task->task_sg_num;
-	bio->bi_phys_segments = task->task_sg_num;
-	bio->bi_hw_segments = task->task_sg_num;
-	
-	return(0);
+	return(task->task_sg_num);
+fail:
+	while (hbio) {
+		bio = hbio;
+		hbio = hbio->bi_next;
+		bio->bi_next = NULL;
+		bio_put(bio);
+	}
+	return(ret);
 }
 
 extern int iblock_CDB_inquiry (iscsi_task_t *task, __u32 size)
@@ -654,9 +746,7 @@
 
 extern int iblock_CDB_read_SG (iscsi_task_t *task, u32 size)
 {
-	iblock_map_task_SG(task);
-	
-	return(task->task_sg_num);
+	return(iblock_map_task_SG(task));
 }
 
 extern int iblock_CDB_write_non_SG (iscsi_task_t *task, u32 size)
@@ -668,9 +758,7 @@
 
 extern int iblock_CDB_write_SG (iscsi_task_t *task, u32 size)
 {
-	iblock_map_task_SG(task);
-
-	return(task->task_sg_num);
+	return(iblock_map_task_SG(task));
 }
 
 extern int iblock_check_lba (unsigned long long lba, iscsi_device_t *dev)
@@ -715,6 +803,8 @@
 	iblock_dev_t *ibd = (iblock_dev_t *) dev->dev_ptr;
 	request_queue_t *q = bdev_get_queue(ibd->ibd_bd);
 
+//	printk("q->max_sectors: %d\n", q->max_sectors);
+//	printk("q->max_hw_sectors: %d\n", q->max_hw_sectors);
 	return((q->max_sectors < IBLOCK_MAX_SECTORS) ?
 		q->max_sectors : IBLOCK_MAX_SECTORS);
 }
@@ -753,26 +843,45 @@
 #endif
 {
 	iscsi_task_t *task = (iscsi_task_t *)bio->bi_private;
+	iblock_req_t *ibr = (iblock_req_t *)task->transport_req;
+	int ret = 0;
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
 	if (bio->bi_size) {
-		DEBUG_IBLOCK("bio: %p task_lba: %llu bio_lba: %llu err=%d,"
+		TRACE_ERROR("bio: %p task_lba: %llu bio_lba: %llu err=%d,"
 			" returned with bio->bi_size: %u\n", bio, task->task_lba,
 				bio->bi_sector, err, bio->bi_size);
 		transport_complete_task(task, 0);
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
-		return;
-#else
-		return(1);
+		ret = 1;
+		goto out;
+	}
 #endif
+	if ((err = test_bit(BIO_UPTODATE, &bio->bi_flags) ? err : -EIO) != 0) {
+		TRACE_ERROR("test_bit(BIO_UPTODATE) failed for bio: %p\n", bio);
+		transport_complete_task(task, 0);
+		ret = 1;
+		goto out;
 	}
-	
-	DEBUG_IBLOCK("bio: %p task_lba: %llu bio_lba: %llu err=%d\n", bio,
-		task->task_lba, bio->bi_sector, err);
-	
+	DEBUG_IBLOCK("done[%p] bio: %p task_lba: %llu bio_lba: %llu err=%d\n",
+		task, bio, task->task_lba, bio->bi_sector, err);
+	/*
+	 * bio_put() will call iblock_bio_destructor() to release the bio back
+	 * to ibr->ib_bio_set.
+	 */
+	bio_put(bio);
+
+	/*
+	 * Wait to complete the task until the last bio as completed.
+	 */
+	if (!(atomic_dec_and_test(&ibr->ib_bio_cnt)))
+		goto out;
+
+	ibr->ib_bio = NULL;
 	transport_complete_task(task, (!err));
+out:
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
 	return;
 #else
-	return(0);
+	return(ret);
 #endif
 }
Index: target/iscsi_target_iblock.h
===================================================================
--- target/iscsi_target_iblock.h	(revision 293)
+++ target/iscsi_target_iblock.h	(working copy)
@@ -32,8 +32,9 @@
 #define IBLOCK_BLOCKSIZE	512
 #define IBLOCK_MAX_SECTORS	128
 #define IBLOCK_HBA_QUEUE_DEPTH	512
-#define IBLOCK_DEVICE_QUEUE_DEPTH	32
+#define IBLOCK_DEVICE_QUEUE_DEPTH	1
 #define IBLOCK_MAX_CDBS		16
+#define IBLOCK_LBA_SHIFT	9
 
 #ifndef IBLOCK_INCLUDE_STRUCTS
 extern int iblock_CDB_inquiry (iscsi_task_t *, u32);
@@ -87,6 +88,8 @@
 
 typedef struct iblock_req_s {
 	unsigned char ib_scsi_cdb[SCSI_CDB_SIZE];
+	atomic_t ib_bio_cnt;
+	spinlock_t ib_bio_lock;
 	u32	ib_sg_count;
 	void	*ib_buf;
 	struct bio *ib_bio;
@@ -103,6 +106,7 @@
 	u32	ibd_depth;
 	u32	ibd_flags;
 	u32	ibd_uu_id[4];
+	struct bio_set	*ibd_bio_set;
 	struct block_device *ibd_bd;
 	struct iblock_hba_s *ibd_host;
 } ____cacheline_aligned iblock_dev_t;
Index: target/iscsi_target_transport.c
===================================================================
--- target/iscsi_target_transport.c	(revision 293)
+++ target/iscsi_target_transport.c	(working copy)
@@ -2208,10 +2208,8 @@
 		goto failure;
 	}
 	
-	if (cmd->transport_map_buffers_to_tasks(cmd) < 0) {
-		ret = PYX_TRANSPORT_OUT_OF_MEMORY_RESOURCES;
+	if ((ret = cmd->transport_map_buffers_to_tasks(cmd)) < 0)
 		goto failure;
-	}
 	
 	if (cmd->data_direction == ISCSI_WRITE)
 		transport_add_tasks_to_state_queue(cmd);
@@ -4895,6 +4893,7 @@
 static int transport_generic_map_buffers_to_tasks (iscsi_cmd_t *cmd)
 {
 	iscsi_task_t *task = NULL;
+	int ret;
 
 	/*
 	 * Deal with non [READ,WRITE]_XX CDBs here.
@@ -4906,10 +4905,11 @@
 			if (atomic_read(&task->task_sent))
 				continue;
 			
-			task->transport_map_task(task, task->task_size);
+			if ((ret = task->transport_map_task(task, task->task_size)) < 0)
+				return(ret);
 
-			DEBUG_CMD_M("Mapping ICF_SCSI_CONTROL_NONSG_IO_CDB task_size: %u\n",
-					task->task_size);
+			DEBUG_CMD_M("Mapping ICF_SCSI_CONTROL_NONSG_IO_CDB"
+				" task_size: %u\n", task->task_size);
 		}
 		return(0);
 	}
@@ -4923,12 +4923,15 @@
 		if (atomic_read(&task->task_sent))
 			continue;
 		
-		task->transport_map_task(task, task->task_size);
+		if ((ret = task->transport_map_task(task, task->task_size)) < 0)
+			return(ret);
 
-		DEBUG_CMD_M("Mapping task[%d]_se_obj_ptr[%p] %s_IO task_lba: %llu task_size: %u"
-			" task_sg_num: %d\n", task->task_no, task->se_obj_ptr,
-			(cmd->cmd_flags & ICF_SCSI_CONTROL_SG_IO_CDB) ? "CONTROL" : "DATA",
-			task->task_lba, task->task_size, task->task_sg_num);
+		DEBUG_CMD_M("Mapping task[%d]_se_obj_ptr[%p] %s_IO task_lba:"
+			" %llu task_size: %u task_sg_num: %d\n",
+			task->task_no, task->se_obj_ptr,
+			(cmd->cmd_flags & ICF_SCSI_CONTROL_SG_IO_CDB) ?
+			"CONTROL" : "DATA", task->task_lba, task->task_size,
+			task->task_sg_num);
 	}	
 
 	return(0);
@@ -4938,10 +4941,12 @@
 		if (atomic_read(&task->task_sent))
 			continue;
 		
-		task->transport_map_task(task, task->task_size);
+		if ((ret = task->transport_map_task(task, task->task_size)) < 0)
+			return(ret);
 
 		DEBUG_CMD_M("Mapping ICF_SCSI_NON_DATA_CDB task_size: %u"
-			" task->task_sg_num: %d\n", task->task_size, task->task_sg_num);
+			" task->task_sg_num: %d\n", task->task_size,
+				task->task_sg_num);
 	}
 
 	return(0);
@@ -5759,10 +5764,8 @@
 	 * buffer list in iscsi_cmd_t to the transport task's native
 	 * buffers format.
 	 */
-	if (cmd->transport_map_buffers_to_tasks(cmd) < 0) {
-		ret = PYX_TRANSPORT_OUT_OF_MEMORY_RESOURCES;
+	if ((ret = cmd->transport_map_buffers_to_tasks(cmd)) < 0)
 		goto failure;
-	}
 
 	/*
 	 * For WRITEs, let the iSCSI Target RX Thread know its buffer is ready..
