Index: ulp/srp/ib_srp.c
===================================================================
--- ulp/srp/ib_srp.c	(revision 3496)
+++ ulp/srp/ib_srp.c	(working copy)
@@ -107,7 +107,7 @@ static struct srp_iu *srp_alloc_iu(struc
 {
 	struct srp_iu *iu;
 
-	iu = kmalloc(sizeof *iu, gfp_mask);
+	iu = kzalloc(sizeof *iu, gfp_mask);
 	if (!iu)
 		goto out;
 
@@ -134,6 +134,9 @@ out:
 
 static void srp_free_iu(struct srp_host *host, struct srp_iu *iu)
 {
+	if (!iu)
+		return;
+
 	dma_unmap_single(host->dev->dma_device, iu->dma, iu->size, iu->direction);
 	kfree(iu->buf);
 	kfree(iu);
@@ -519,6 +522,106 @@ err:
 	return ret;
 }
 
+static int srp_map_fmr(struct srp_target_port *target,
+		       struct scatterlist *scat, int sg_cnt, struct srp_iu *iu)
+{
+	dma_addr_t		dma_addr;
+	dma_addr_t		base_addr;
+	u32			dma_len;
+	u32			cur_len;
+	int			i;
+	u64		       *dma_pages;
+	u32			page_cnt;
+	struct srp_fmr	       *srp_fmr;
+	u32			unaligned;
+
+	dma_pages = kmalloc(sizeof(u64) * sg_cnt * (SRP_MAX_INDIRECT + 2), GFP_ATOMIC);
+	if (!dma_pages)
+		goto err_dpages;	
+
+	iu->fmr_arr = kzalloc(sizeof(struct srp_fmr) * sg_cnt, GFP_ATOMIC);
+	if (!iu->fmr_arr)
+		goto err_farr;
+
+	srp_fmr = iu->fmr_arr;
+	iu->fmr_cnt = 0;
+	page_cnt = 0;
+	cur_len = 0;
+	unaligned = 0;
+
+	for (i = 0; i < sg_cnt; ++i) {
+		dma_len = sg_dma_len(scat);
+		dma_addr = sg_dma_address(scat);
+
+		if ((dma_addr & (PAGE_SIZE - 1)) ||
+		    ((dma_addr + dma_len) & (PAGE_SIZE - 1)) ||
+		    ((i == (sg_cnt - 1)) && !unaligned)) {
+			srp_fmr->io_addr = dma_addr & PAGE_MASK;
+			++unaligned;
+		}
+
+		if (unaligned <= 1) {
+			cur_len += dma_len;
+			for (base_addr = dma_addr;
+			     (dma_addr & PAGE_MASK) <=
+			     ((base_addr + dma_len - 1) & PAGE_MASK);
+			     dma_addr += PAGE_SIZE)
+				dma_pages[page_cnt++] = dma_addr & PAGE_MASK;
+		}
+
+		if ((unaligned > 1) || (i == (sg_cnt - 1))) {
+			srp_fmr->mem =
+				ib_fmr_pool_map_phys(target->srp_host->fmr_pool,
+						     dma_pages, page_cnt,
+						     &srp_fmr->io_addr);
+			if (IS_ERR(srp_fmr->mem)) {
+				srp_fmr->mem = NULL;
+				goto err_map_phys;
+			}
+
+			srp_fmr->len = cur_len;
+			++iu->fmr_cnt;
+
+			/* restart the new fmr segment */
+			if (unaligned > 1) {
+				srp_fmr++;
+				page_cnt = 0;
+				--unaligned;
+				cur_len = dma_len;
+				srp_fmr->io_addr = dma_addr & PAGE_MASK;
+				for (base_addr = dma_addr;
+				     (dma_addr & PAGE_MASK) <=
+				     ((base_addr + dma_len - 1) & PAGE_MASK);
+				     dma_addr += PAGE_SIZE)
+					dma_pages[page_cnt++] = dma_addr & PAGE_MASK;
+				
+			}
+		}
+		scat++;
+	}
+
+	kfree(dma_pages);
+
+	return iu->fmr_cnt;
+
+err_map_phys:
+	srp_fmr = iu->fmr_arr;
+	for (i = 0; i < iu->fmr_cnt; ++i) {
+		ib_fmr_pool_unmap(srp_fmr->mem);
+		++srp_fmr;
+	}
+	kfree(iu->fmr_arr);
+	iu->fmr_arr = NULL;
+	iu->fmr_cnt = 0;
+
+err_farr:
+	kfree(dma_pages);
+
+err_dpages:
+	return -ENOMEM;
+}
+
+
 static int srp_map_data(struct scsi_cmnd *scmnd, struct srp_target_port *target,
 			struct srp_iu *iu)
 {
@@ -558,36 +661,69 @@ static int srp_map_data(struct scsi_cmnd
 		} else {
 			struct srp_indirect_buf *buf = (void *) cmd->add_data;
 			u32 datalen = 0;
+			int fmr_cnt;
+			struct srp_fmr *srp_fmr;
 
-			fmt = SRP_DATA_DESC_INDIRECT;
+			fmr_cnt = srp_map_fmr(target, scat, n, iu);
+			srp_fmr = iu->fmr_arr;
 
-			if (scmnd->sc_data_direction == DMA_TO_DEVICE)
-				cmd->data_out_desc_cnt = n;
-			else
-				cmd->data_in_desc_cnt = n;
-
-			buf->table_desc.va  = cpu_to_be64(iu->dma +
-							  sizeof *cmd +
-							  sizeof *buf);
-			buf->table_desc.key =
-				cpu_to_be32(target->srp_host->mr->rkey);
-			buf->table_desc.len =
-				cpu_to_be32(n * sizeof (struct srp_direct_buf));
-
-			for (i = 0; i < n; ++i) {
-				buf->desc_list[i].va  = cpu_to_be64(sg_dma_address(&scat[i]));
-				buf->desc_list[i].key =
-					cpu_to_be32(target->srp_host->mr->rkey);
-				buf->desc_list[i].len = cpu_to_be32(sg_dma_len(&scat[i]));
+			if (fmr_cnt == 1) {
+				struct srp_direct_buf *d_buf = (void *) cmd->add_data;
 
-				datalen += sg_dma_len(&scat[i]);
+				d_buf->va  = cpu_to_be64(srp_fmr->io_addr);
+				d_buf->key = cpu_to_be32(srp_fmr->mem->fmr->rkey);
+				d_buf->len = cpu_to_be32(srp_fmr->len);
+				n = 1;
+			} else if (fmr_cnt <= 0) {
+				for (i = 0; i < n; ++i) {
+					buf->desc_list[i].va  = 
+						cpu_to_be64(sg_dma_address(&scat[i]));
+					buf->desc_list[i].key =
+						cpu_to_be32(target->srp_host->mr->rkey);
+					buf->desc_list[i].len =
+						cpu_to_be32(sg_dma_len(&scat[i]));
+
+					datalen += sg_dma_len(&scat[i]);
+				}
+			} else {
+				for (i = 0; i < iu->fmr_cnt; ++i) {
+					buf->desc_list[i].va  = cpu_to_be64(srp_fmr->io_addr);
+					buf->desc_list[i].key =
+						cpu_to_be32(srp_fmr->mem->fmr->rkey);
+					buf->desc_list[i].len = cpu_to_be32(srp_fmr->len);
+			
+					datalen += srp_fmr->len;
+					++srp_fmr;
+				}
+				n = fmr_cnt;
 			}
 
-			buf->len = cpu_to_be32(datalen);
+			if (n == 1) {
+				fmt = SRP_DATA_DESC_DIRECT;
+				len = sizeof (struct srp_cmd) +
+					sizeof (struct srp_direct_buf);
+			} else {
+				fmt = SRP_DATA_DESC_INDIRECT;
+
+				if (scmnd->sc_data_direction == DMA_TO_DEVICE)
+					cmd->data_out_desc_cnt = n;
+				else
+					cmd->data_in_desc_cnt = n;
+
+				buf->table_desc.va  = cpu_to_be64(iu->dma +
+								  sizeof *cmd +
+								  sizeof *buf);
+				buf->table_desc.key =
+					cpu_to_be32(target->srp_host->mr->rkey);
+				buf->table_desc.len =
+					cpu_to_be32(n * sizeof (struct srp_direct_buf));
 
-			len = sizeof (struct srp_cmd) +
-				sizeof (struct srp_indirect_buf) +
-				n * sizeof (struct srp_direct_buf);
+				buf->len = cpu_to_be32(datalen);
+	
+				len = sizeof (struct srp_cmd) +
+					sizeof (struct srp_indirect_buf) +
+					n * sizeof (struct srp_direct_buf);
+			}
 		}
 	} else {
 		struct srp_direct_buf *buf = (void *) cmd->add_data;
@@ -657,6 +793,8 @@ static void srp_process_rsp(struct srp_t
 
 	req = &target->req_ring[rsp->tag & ~SRP_TAG_TSK_MGMT];
 
+	iu  = req->cmd;
+
 	if (rsp->tag & SRP_TAG_TSK_MGMT) {
 		if (be32_to_cpu(rsp->resp_data_len) < 4)
 			req->tsk_status = -1;
@@ -664,7 +802,6 @@ static void srp_process_rsp(struct srp_t
 			req->tsk_status = rsp->data[3];
 		complete(&req->done);
 	} else {
-		iu 	      = req->cmd;
 		scmnd 	      = req->scmnd;
 		scmnd->result = rsp->status;
 
@@ -680,6 +817,18 @@ static void srp_process_rsp(struct srp_t
 		else if (rsp->flags & (SRP_RSP_FLAG_DIOVER | SRP_RSP_FLAG_DIUNDER))
 			scmnd->resid = be32_to_cpu(rsp->data_in_res_cnt);
 
+		if (iu->fmr_arr) {
+			struct srp_fmr *srp_fmr = iu->fmr_arr;
+
+			while (iu->fmr_cnt) {
+				ib_fmr_pool_unmap(srp_fmr->mem);
+				++srp_fmr;
+				--iu->fmr_cnt;
+			}
+			kfree(iu->fmr_arr);
+			iu->fmr_arr = NULL;
+		}
+
 		srp_unmap_data(scmnd, target, iu->buf);
 
 		if (!req->tsk_mgmt) {
@@ -694,6 +843,9 @@ static void srp_process_rsp(struct srp_t
 			req->cmd_done = 1;
 	}
 
+	++target->tx_tail;
+	iu->in_use = 0;
+
 	spin_unlock_irqrestore(target->scsi_host->host_lock, flags);
 }
 
@@ -773,8 +925,6 @@ static void srp_completion(struct ib_cq 
 
 		if (wc.wr_id & SRP_OP_RECV)
 			srp_handle_recv(target, &wc);
-		else
-			++target->tx_tail;
 	}
 }
 
@@ -825,10 +975,22 @@ static int srp_post_recv(struct srp_targ
  */
 static struct srp_iu *__srp_get_tx_iu(struct srp_target_port *target)
 {
+	struct srp_iu *iu;
+
 	if (target->tx_head - target->tx_tail >= SRP_SQ_SIZE)
 		return NULL;
 
-	return target->tx_ring[target->tx_head & SRP_SQ_SIZE];
+	iu = target->tx_ring[target->tx_head & SRP_SQ_SIZE];
+
+	while (iu->in_use) {
+		++target->tx_head;
+		++target->tx_tail;
+		iu = target->tx_ring[target->tx_head & SRP_SQ_SIZE];
+	}
+
+	iu->in_use = 1;
+
+	return iu;
 }
 
 /*
@@ -943,6 +1105,19 @@ static int srp_queuecommand(struct scsi_
 err_unmap:
 	srp_unmap_data(scmnd, target, cmd);
 
+	if (iu->fmr_arr) {
+		struct srp_fmr *srp_fmr = iu->fmr_arr;
+
+		while (iu->fmr_cnt) {
+			ib_fmr_pool_unmap(srp_fmr->mem);
+			++srp_fmr;
+			--iu->fmr_cnt;
+		}
+		kfree(iu->fmr_arr);
+		iu->fmr_arr = NULL;
+	}
+
+
 err:
 	return SCSI_MLQUEUE_HOST_BUSY;
 }
@@ -1218,6 +1393,7 @@ static struct scsi_host_template srp_tem
 	.can_queue			= SRP_SQ_SIZE,
 	.this_id			= -1,
 	.sg_tablesize			= SRP_MAX_INDIRECT,
+	.max_sectors			= SRP_MAX_SECTORS,
 	.cmd_per_lun			= SRP_SQ_SIZE,
 	.use_clustering			= ENABLE_CLUSTERING
 };
@@ -1227,6 +1403,8 @@ static int srp_add_target(struct srp_hos
 	sprintf(target->target_name, "SRP.T10:%016llX",
 		 (unsigned long long) be64_to_cpu(target->id_ext));
 
+	target->scsi_host->max_lun = SRP_MAX_LUN;
+
 	if (scsi_add_host(target->scsi_host, host->dev->dma_device))
 		return -ENODEV;
 
@@ -1407,7 +1585,8 @@ static ssize_t srp_create_target(struct 
 	ib_get_cached_gid(host->dev, host->port, 0, &target->path.sgid);
 
 	printk(KERN_DEBUG PFX "new target: id_ext %016llx ioc_guid %016llx pkey %04x "
-	       "service_id %016llx dgid %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+	       "service_id %016llx dgid %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x "
+	       "max_sectors %dKB\n",
 	       (unsigned long long) be64_to_cpu(target->id_ext),
 	       (unsigned long long) be64_to_cpu(target->ioc_guid),
 	       be16_to_cpu(target->path.pkey),
@@ -1419,7 +1598,8 @@ static ssize_t srp_create_target(struct 
 	       (int) be16_to_cpu(*(__be16 *) &target->path.dgid.raw[8]),
 	       (int) be16_to_cpu(*(__be16 *) &target->path.dgid.raw[10]),
 	       (int) be16_to_cpu(*(__be16 *) &target->path.dgid.raw[12]),
-	       (int) be16_to_cpu(*(__be16 *) &target->path.dgid.raw[14]));
+	       (int) be16_to_cpu(*(__be16 *) &target->path.dgid.raw[14]),
+	       target->scsi_host->max_sectors/2);
 
 	ret = srp_lookup_path(target);
 	if (ret) {
@@ -1460,10 +1640,29 @@ err:
 
 static CLASS_DEVICE_ATTR(add_target, S_IWUSR, NULL, srp_create_target);
 
+static void srp_flush_fmr(struct ib_fmr_pool *pool, void *arg)
+{
+	struct srp_host *host = (struct srp_host *)arg;
+	LIST_HEAD(target_list);
+	struct srp_target_port *target, *tmp_target;
+	unsigned long flags;
+
+	down(&host->target_mutex);
+	list_for_each_entry_safe(target, tmp_target,
+				 &host->target_list, list) {
+		spin_lock_irqsave(target->scsi_host->host_lock, flags);
+		printk(KERN_WARNING PFX "flush_fmr for target %s\n",
+		       target->target_name);
+		spin_unlock_irqrestore(target->scsi_host->host_lock, flags);
+	}
+	up(&host->target_mutex);
+}
+
 static struct srp_host *srp_add_port(struct ib_device *device,
 				     __be64 node_guid, u8 port)
 {
 	struct srp_host *host;
+	struct ib_fmr_pool_param fmr_params;
 
 	host = kzalloc(sizeof *host, GFP_KERNEL);
 	if (!host)
@@ -1500,6 +1699,20 @@ static struct srp_host *srp_add_port(str
 		goto err_class;
 	/* XXX ibdev / port files as well */
 
+	memset(&fmr_params, 0, sizeof fmr_params);
+	fmr_params.pool_size = SRP_FMR_POOL_SIZE;
+	fmr_params.dirty_watermark = SRP_FMR_DIRTY_SIZE;
+	fmr_params.cache = 0;
+	fmr_params.max_pages_per_fmr = SRP_MAX_SECTORS * 512 / PAGE_SIZE;
+	fmr_params.access = (IB_ACCESS_LOCAL_WRITE |
+			     IB_ACCESS_REMOTE_WRITE |
+			     IB_ACCESS_REMOTE_READ);
+	fmr_params.flush_function = srp_flush_fmr;
+	fmr_params.flush_arg = (void *)host;
+	host->fmr_pool = ib_create_fmr_pool(host->pd, &fmr_params);
+	if (IS_ERR(host->fmr_pool))
+		goto err_class;
+
 	return host;
 
 err_class:
@@ -1607,6 +1820,7 @@ static void srp_remove_one(struct ib_dev
 			scsi_host_put(target->scsi_host);
 		}
 
+		ib_destroy_fmr_pool(host->fmr_pool);
 		ib_dereg_mr(host->mr);
 		ib_dealloc_pd(host->pd);
 		kfree(host);
Index: ulp/srp/ib_srp.h
===================================================================
--- ulp/srp/ib_srp.h	(revision 3496)
+++ ulp/srp/ib_srp.h	(working copy)
@@ -46,6 +46,7 @@
 #include <rdma/ib_verbs.h>
 #include <rdma/ib_sa.h>
 #include <rdma/ib_cm.h>
+#include <rdma/ib_fmr_pool.h>
 
 enum {
 	SRP_PATH_REC_TIMEOUT_MS	= 1000,
@@ -56,11 +57,17 @@ enum {
 
 	SRP_MAX_IU_LEN		= 256,
 
+	SRP_MAX_LUN		= 16,
+	SRP_MAX_SECTORS		= 256,
+
 	SRP_RQ_SHIFT    	= 6,
 	SRP_RQ_SIZE		= 1 << SRP_RQ_SHIFT,
 	SRP_SQ_SIZE		= SRP_RQ_SIZE - 1,
 	SRP_CQ_SIZE		= SRP_SQ_SIZE + SRP_RQ_SIZE,
 
+	SRP_FMR_POOL_SIZE	= 8192,
+	SRP_FMR_DIRTY_SIZE	= SRP_FMR_POOL_SIZE >> 4,
+
 	SRP_TAG_TSK_MGMT	= 1 << (SRP_RQ_SHIFT + 1)
 };
 
@@ -87,6 +94,7 @@ struct srp_host {
 	struct semaphore        target_mutex;
 	struct completion	released;
 	struct list_head	list;
+	struct ib_fmr_pool     *fmr_pool;
 };
 
 struct srp_request {
@@ -139,11 +147,20 @@ struct srp_target_port {
 	enum srp_target_state	state;
 };
 
+struct srp_fmr {
+	u64			io_addr;
+	struct ib_pool_fmr     *mem;
+	u32			len;
+};
+
 struct srp_iu {
 	dma_addr_t		dma;
 	void		       *buf;
 	size_t			size;
 	enum dma_data_direction	direction;
+	struct srp_fmr	       *fmr_arr;
+	u16			fmr_cnt;
+	u16			in_use;
 };
 
 /*
