Index: ulp/srp/ib_srp.c
===================================================================
--- ulp/srp/ib_srp.c	(revision 3615)
+++ ulp/srp/ib_srp.c	(working copy)
@@ -522,10 +522,120 @@ err:
 	return ret;
 }
 
+static int srp_map_fmr(struct srp_target_port *target, struct scatterlist *scat,
+		       int sg_cnt, struct srp_request *req)
+		       
+{
+	dma_addr_t		dma_addr;
+	u32			dma_len;
+	u32			cur_len;
+	u32			tmp_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;	
+
+	req->fmr_arr = kmalloc(sizeof(struct srp_fmr) * sg_cnt, GFP_ATOMIC);
+	if (!req->fmr_arr)
+		goto err_farr;
+
+	srp_fmr = req->fmr_arr;
+	req->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);
+
+		/*
+		 * Checking for unaligned sg_element and assign the io_addr
+		 * for this fmr segment
+		 * If there is no such unaligned sg_element in the sg list
+		 * io_address will be the address of last sg_element
+		 */
+		if (scat->offset ||
+		    ((i == (sg_cnt - 1)) && !unaligned)) {
+			srp_fmr->io_addr = dma_addr & PAGE_MASK;
+			++unaligned;
+		}
+
+		/* 
+		 * Every FMR segment has atmost 1 unaligned element
+		 * prepare the page boundary bus addresses for this fmr segment
+		 */
+		if (unaligned <= 1) {
+			cur_len += dma_len;
+			for (tmp_len = 0; tmp_len < dma_len;
+			     tmp_len += PAGE_SIZE, dma_addr += PAGE_SIZE)
+				dma_pages[page_cnt++] = dma_addr & PAGE_MASK;
+		}
+
+		/*
+		 * Register this FMR segment if this is the last sg_element or
+		 * unaligned > 1
+		 * Restart the new fmr segment with the new unaligned sg_element
+		 */
+		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;
+			++req->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 (tmp_len = 0; tmp_len < dma_len;
+				     tmp_len += PAGE_SIZE, dma_addr += PAGE_SIZE)
+					dma_pages[page_cnt++] = dma_addr & PAGE_MASK;
+			}
+		}
+		scat++;
+	}
+
+	kfree(dma_pages);
+
+	return req->fmr_cnt;
+
+err_map_phys:
+	srp_fmr = req->fmr_arr;
+	for (i = 0; i < req->fmr_cnt; ++i) {
+		ib_fmr_pool_unmap(srp_fmr->mem);
+		++srp_fmr;
+	}
+	kfree(req->fmr_arr);
+	req->fmr_arr = NULL;
+	req->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)
+			struct srp_request *req)
 {
-	struct srp_cmd *cmd = iu->buf;
+	struct srp_cmd *cmd = req->cmd->buf;
 	int len;
 	u8 fmt;
 
@@ -561,36 +671,74 @@ 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;
-
-			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]));
+			fmr_cnt = srp_map_fmr(target, scat, n, req);
+			srp_fmr = req->fmr_arr;
 
-				datalen += sg_dma_len(&scat[i]);
+			if (fmr_cnt == 1) {
+				struct srp_direct_buf *d_buf = (void *) cmd->add_data;
+				/* single fmr segment - using direct descriptor */
+
+				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) {
+				/* fail fmr - fallback to global r_key */
+				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 {
+				/* prepare indirect descriptors with fmr segments */
+				for (i = 0; i < req->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(req->cmd->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;
@@ -606,6 +754,8 @@ static int srp_map_data(struct scsi_cmnd
 			return -EINVAL;
 		}
 
+		pci_unmap_addr_set(req, direct_mapping, dma);
+
 		buf->va  = cpu_to_be64(dma);
 		buf->key = cpu_to_be32(target->srp_host->mr->rkey);
 		buf->len = cpu_to_be32(scmnd->request_bufflen);
@@ -626,7 +776,7 @@ static int srp_map_data(struct scsi_cmnd
 
 static void srp_unmap_data(struct scsi_cmnd *scmnd,
 			   struct srp_target_port *target,
-			   struct srp_cmd *cmd)
+			   struct srp_request *req)
 {
 	if (!scmnd->request_buffer ||
 	    (scmnd->sc_data_direction != DMA_TO_DEVICE &&
@@ -639,7 +789,7 @@ static void srp_unmap_data(struct scsi_c
 			     scmnd->use_sg, scmnd->sc_data_direction);
 	else
 		dma_unmap_single(target->srp_host->dev->dma_device,
-				 be64_to_cpu(((struct srp_direct_buf *) cmd->add_data)->va),
+				 pci_unmap_addr(req, direct_mapping),
 				 scmnd->request_bufflen,
 				 scmnd->sc_data_direction);
 }
@@ -648,7 +798,6 @@ static void srp_process_rsp(struct srp_t
 {
 	struct srp_request *req;
 	struct scsi_cmnd *scmnd;
-	struct srp_iu *iu;
 	unsigned long flags;
 	s32 delta;
 
@@ -667,7 +816,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;
 
@@ -683,7 +831,19 @@ 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);
 
-		srp_unmap_data(scmnd, target, iu->buf);
+		if (req->fmr_arr) {
+			struct srp_fmr *srp_fmr = req->fmr_arr;
+
+			while (req->fmr_cnt) {
+				ib_fmr_pool_unmap(srp_fmr->mem);
+				++srp_fmr;
+				--req->fmr_cnt;
+			}
+			kfree(req->fmr_arr);
+			req->fmr_arr = NULL;
+		}
+
+		srp_unmap_data(scmnd, target, req);
 
 		if (!req->tsk_mgmt) {
 			req->scmnd = NULL;
@@ -919,7 +1079,7 @@ static int srp_queuecommand(struct scsi_
 	req->cmd_done = 0;
 	req->tsk_mgmt = NULL;
 
-	len = srp_map_data(scmnd, target, iu);
+	len = srp_map_data(scmnd, target, req);
 	if (len < 0) {
 		printk(KERN_ERR PFX "Failed to map data\n");
 		goto err;
@@ -944,7 +1104,19 @@ static int srp_queuecommand(struct scsi_
 	return 0;
 
 err_unmap:
-	srp_unmap_data(scmnd, target, cmd);
+	if (req->fmr_arr) {
+		struct srp_fmr *srp_fmr = req->fmr_arr;
+
+		while (req->fmr_cnt) {
+			ib_fmr_pool_unmap(srp_fmr->mem);
+			++srp_fmr;
+			--req->fmr_cnt;
+		}
+		kfree(req->fmr_arr);
+		req->fmr_arr = NULL;
+	}
+
+	srp_unmap_data(scmnd, target, req);
 
 err:
 	return SCSI_MLQUEUE_HOST_BUSY;
@@ -1230,6 +1402,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;
 
@@ -1464,6 +1638,7 @@ err:
 static CLASS_DEVICE_ATTR(add_target, S_IWUSR, NULL, srp_create_target);
 
 static struct srp_host *srp_add_port(struct ib_device *device,
+				     struct srp_device_data *srp_data,
 				     __be64 node_guid, u8 port)
 {
 	struct srp_host *host;
@@ -1477,28 +1652,20 @@ static struct srp_host *srp_add_port(str
 	init_completion(&host->released);
 	host->dev  = device;
 	host->port = port;
+	host->pd = srp_data->pd;
+	host->mr = srp_data->mr;
+	host->fmr_pool = srp_data->fmr_pool;
 
 	host->initiator_port_id[7] = port;
 	memcpy(host->initiator_port_id + 8, &node_guid, 8);
 
-	host->pd   = ib_alloc_pd(device);
-	if (IS_ERR(host->pd))
-		goto err_free;
-
-	host->mr   = ib_get_dma_mr(host->pd,
-				   IB_ACCESS_LOCAL_WRITE |
-				   IB_ACCESS_REMOTE_READ |
-				   IB_ACCESS_REMOTE_WRITE);
-	if (IS_ERR(host->mr))
-		goto err_pd;
-
 	host->class_dev.class = &srp_class;
 	host->class_dev.dev   = device->dma_device;
 	snprintf(host->class_dev.class_id, BUS_ID_SIZE, "srp-%s-%d",
 		 device->name, port);
 
 	if (class_device_register(&host->class_dev))
-		goto err_mr;
+		goto err_free;
 	if (class_device_create_file(&host->class_dev, &class_device_attr_add_target))
 		goto err_class;
 	/* XXX ibdev / port files as well */
@@ -1508,12 +1675,6 @@ static struct srp_host *srp_add_port(str
 err_class:
 	class_device_unregister(&host->class_dev);
 
-err_mr:
-	ib_dereg_mr(host->mr);
-
-err_pd:
-	ib_dealloc_pd(host->pd);
-
 err_free:
 	kfree(host);
 
@@ -1522,10 +1683,11 @@ err_free:
 
 static void srp_add_one(struct ib_device *device)
 {
-	struct list_head *dev_list;
+	struct srp_device_data *srp_data;
 	struct srp_host *host;
 	struct ib_device_attr *dev_attr;
 	int s, e, p;
+	struct ib_fmr_pool_param fmr_params;
 
 	dev_attr = kmalloc(sizeof *dev_attr, GFP_KERNEL);
 	if (!dev_attr)
@@ -1534,14 +1696,37 @@ static void srp_add_one(struct ib_device
 	if (ib_query_device(device, dev_attr)) {
 		printk(KERN_WARNING PFX "Couldn't query node GUID for %s.\n",
 		       device->name);
-		goto out;
+		goto free_attr;
 	}
 
-	dev_list = kmalloc(sizeof *dev_list, GFP_KERNEL);
-	if (!dev_list)
-		goto out;
+	srp_data = kmalloc(sizeof *srp_data, GFP_KERNEL); 
+	if (!srp_data)
+		goto free_attr;
+
+	INIT_LIST_HEAD(&srp_data->dev_list);
+
+	srp_data->pd   = ib_alloc_pd(device);
+	if (IS_ERR(srp_data->pd))
+		goto free_srp_data;
 
-	INIT_LIST_HEAD(dev_list);
+	srp_data->mr   = ib_get_dma_mr(srp_data->pd,
+					 IB_ACCESS_LOCAL_WRITE |
+					 IB_ACCESS_REMOTE_READ |
+					 IB_ACCESS_REMOTE_WRITE);
+	if (IS_ERR(srp_data->mr))
+		goto err_pd;
+
+	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);
+	srp_data->fmr_pool = ib_create_fmr_pool(srp_data->pd, &fmr_params);
+	if (IS_ERR(srp_data->fmr_pool))
+		goto err_mr;
 
 	if (device->node_type == IB_NODE_SWITCH) {
 		s = 0;
@@ -1552,28 +1737,36 @@ static void srp_add_one(struct ib_device
 	}
 
 	for (p = s; p <= e; ++p) {
-		host = srp_add_port(device, dev_attr->node_guid, p);
+		host = srp_add_port(device, srp_data, dev_attr->node_guid, p);
 		if (host)
-			list_add_tail(&host->list, dev_list);
+			list_add_tail(&host->list, &srp_data->dev_list);
 	}
 
-	ib_set_client_data(device, &srp_client, dev_list);
+	ib_set_client_data(device, &srp_client, srp_data);
 
-out:
+	goto free_attr;
+
+err_mr:
+	ib_dereg_mr(srp_data->mr);
+err_pd:
+	ib_dealloc_pd(srp_data->pd);
+free_srp_data:
+	kfree(srp_data);
+free_attr:
 	kfree(dev_attr);
 }
 
 static void srp_remove_one(struct ib_device *device)
 {
-	struct list_head *dev_list;
+	struct srp_device_data *srp_data;
 	struct srp_host *host, *tmp_host;
 	LIST_HEAD(target_list);
 	struct srp_target_port *target, *tmp_target;
 	unsigned long flags;
 
-	dev_list = ib_get_client_data(device, &srp_client);
+	srp_data = ib_get_client_data(device, &srp_client);
 
-	list_for_each_entry_safe(host, tmp_host, dev_list, list) {
+	list_for_each_entry_safe(host, tmp_host, &srp_data->dev_list, list) {
 		class_device_unregister(&host->class_dev);
 		/*
 		 * Wait for the sysfs entry to go away, so that no new
@@ -1610,12 +1803,14 @@ static void srp_remove_one(struct ib_dev
 			scsi_host_put(target->scsi_host);
 		}
 
-		ib_dereg_mr(host->mr);
-		ib_dealloc_pd(host->pd);
 		kfree(host);
 	}
 
-	kfree(dev_list);
+	ib_destroy_fmr_pool(srp_data->fmr_pool);
+	ib_dereg_mr(srp_data->mr);
+	ib_dealloc_pd(srp_data->pd);
+
+	kfree(srp_data);
 }
 
 static int __init srp_init_module(void)
Index: ulp/srp/ib_srp.h
===================================================================
--- ulp/srp/ib_srp.h	(revision 3615)
+++ 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		= 512,
+	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)
 };
 
@@ -76,6 +83,13 @@ enum srp_target_state {
 	SRP_TARGET_REMOVED
 };
 
+struct srp_device_data {
+	struct list_head	dev_list;
+	struct ib_pd	       *pd;
+	struct ib_mr	       *mr;
+	struct ib_fmr_pool     *fmr_pool;
+};
+	
 struct srp_host {
 	u8			initiator_port_id[16];
 	struct ib_device       *dev;
@@ -87,6 +101,13 @@ struct srp_host {
 	struct semaphore        target_mutex;
 	struct completion	released;
 	struct list_head	list;
+	struct ib_fmr_pool     *fmr_pool;
+};
+
+struct srp_fmr {
+	u64			io_addr;
+	struct ib_pool_fmr     *mem;
+	u32			len;
 };
 
 struct srp_request {
@@ -94,10 +115,13 @@ struct srp_request {
 	struct scsi_cmnd       *scmnd;
 	struct srp_iu	       *cmd;
 	struct srp_iu	       *tsk_mgmt;
+	DECLARE_PCI_UNMAP_ADDR(direct_mapping)
 	struct completion	done;
 	short			next;
 	u8			cmd_done;
 	u8			tsk_status;
+	struct srp_fmr	       *fmr_arr;
+	u32			fmr_cnt;
 };
 
 struct srp_target_port {
