isci: fix support for arbitrarily large smp requests

Instead of duplicating the smp request buffer reuse the one provided by
libsas.  This future proofs the driver to support arbitrarily large smp
requests, and shrinks the request structure size by ~700 bytes.

Signed-off-by: Dan Williams <dan.j.williams@intel.com>
diff --git a/drivers/scsi/isci/request.c b/drivers/scsi/isci/request.c
index 3950849..1043fed 100644
--- a/drivers/scsi/isci/request.c
+++ b/drivers/scsi/isci/request.c
@@ -2943,6 +2943,20 @@
 			dma_unmap_sg(&isci_host->pdev->dev, task->scatter,
 				     request->num_sg_entries, task->data_dir);
 		break;
+	case SAS_PROTOCOL_SMP: {
+		struct scatterlist *sg = &task->smp_task.smp_req;
+		struct smp_req *smp_req;
+		void *kaddr;
+
+		dma_unmap_sg(&isci_host->pdev->dev, sg, 1, DMA_TO_DEVICE);
+
+		/* need to swab it back in case the command buffer is re-used */
+		kaddr = kmap_atomic(sg_page(sg), KM_IRQ0);
+		smp_req = kaddr + sg->offset;
+		sci_swab32_cpy(smp_req, smp_req, sg->length / sizeof(u32));
+		kunmap_atomic(kaddr, KM_IRQ0);
+		break;
+	}
 	default:
 		break;
 	}
@@ -3160,7 +3174,7 @@
 	else if (dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP))
 		memset(&sci_req->stp.cmd, 0, sizeof(sci_req->stp.cmd));
 	else if (dev_is_expander(dev))
-		memset(&sci_req->smp.cmd, 0, sizeof(sci_req->smp.cmd));
+		/* pass */;
 	else
 		return SCI_FAILURE_UNSUPPORTED_PROTOCOL;
 
@@ -3236,30 +3250,54 @@
 	return status;
 }
 
-/*
- * This function will fill in the SCU Task Context for a SMP request. The
- *    following important settings are utilized: -# task_type ==
- *    SCU_TASK_TYPE_SMP.  This simply indicates that a normal request type
- *    (i.e. non-raw frame) is being utilized to perform task management. -#
- *    control_frame == 1.  This ensures that the proper endianess is set so
- *    that the bytes are transmitted in the right order for a smp request frame.
- * @sci_req: This parameter specifies the smp request object being
- *    constructed.
- *
- */
-static void
-scu_smp_request_construct_task_context(struct scic_sds_request *sci_req,
-				       ssize_t req_len)
+static enum sci_status
+scic_io_request_construct_smp(struct device *dev,
+			      struct scic_sds_request *sci_req,
+			      struct sas_task *task)
 {
-	dma_addr_t dma_addr;
+	struct scatterlist *sg = &task->smp_task.smp_req;
 	struct scic_sds_remote_device *sci_dev;
-	struct scic_sds_port *sci_port;
 	struct scu_task_context *task_context;
-	ssize_t word_cnt = sizeof(struct smp_req) / sizeof(u32);
+	struct scic_sds_port *sci_port;
+	struct smp_req *smp_req;
+	void *kaddr;
+	u8 req_len;
+	u32 cmd;
+
+	kaddr = kmap_atomic(sg_page(sg), KM_IRQ0);
+	smp_req = kaddr + sg->offset;
+	/*
+	 * Look at the SMP requests' header fields; for certain SAS 1.x SMP
+	 * functions under SAS 2.0, a zero request length really indicates
+	 * a non-zero default length.
+	 */
+	if (smp_req->req_len == 0) {
+		switch (smp_req->func) {
+		case SMP_DISCOVER:
+		case SMP_REPORT_PHY_ERR_LOG:
+		case SMP_REPORT_PHY_SATA:
+		case SMP_REPORT_ROUTE_INFO:
+			smp_req->req_len = 2;
+			break;
+		case SMP_CONF_ROUTE_INFO:
+		case SMP_PHY_CONTROL:
+		case SMP_PHY_TEST_FUNCTION:
+			smp_req->req_len = 9;
+			break;
+			/* Default - zero is a valid default for 2.0. */
+		}
+	}
+	req_len = smp_req->req_len;
+	sci_swab32_cpy(smp_req, smp_req, sg->length / sizeof(u32));
+	cmd = *(u32 *) smp_req;
+	kunmap_atomic(kaddr, KM_IRQ0);
+
+	if (!dma_map_sg(dev, sg, 1, DMA_TO_DEVICE))
+		return SCI_FAILURE;
+
+	sci_req->protocol = SCIC_SMP_PROTOCOL;
 
 	/* byte swap the smp request. */
-	sci_swab32_cpy(&sci_req->smp.cmd, &sci_req->smp.cmd,
-		       word_cnt);
 
 	task_context = scic_sds_request_get_task_context(sci_req);
 
@@ -3307,7 +3345,7 @@
 	 * 18h ~ 30h, protocol specific
 	 * since commandIU has been build by framework at this point, we just
 	 * copy the frist DWord from command IU to this location. */
-	memcpy(&task_context->type.smp, &sci_req->smp.cmd, sizeof(u32));
+	memcpy(&task_context->type.smp, &cmd, sizeof(u32));
 
 	/*
 	 * 40h
@@ -3347,48 +3385,12 @@
 	 * Copy the physical address for the command buffer to the SCU Task
 	 * Context command buffer should not contain command header.
 	 */
-	dma_addr = scic_io_request_get_dma_addr(sci_req,
-						((char *) &sci_req->smp.cmd) +
-						sizeof(u32));
-
-	task_context->command_iu_upper = upper_32_bits(dma_addr);
-	task_context->command_iu_lower = lower_32_bits(dma_addr);
+	task_context->command_iu_upper = upper_32_bits(sg_dma_address(sg));
+	task_context->command_iu_lower = lower_32_bits(sg_dma_address(sg) + sizeof(u32));
 
 	/* SMP response comes as UF, so no need to set response IU address. */
 	task_context->response_iu_upper = 0;
 	task_context->response_iu_lower = 0;
-}
-
-static enum sci_status
-scic_io_request_construct_smp(struct scic_sds_request *sci_req)
-{
-	struct smp_req *smp_req = &sci_req->smp.cmd;
-
-	sci_req->protocol = SCIC_SMP_PROTOCOL;
-
-	/*
-	 * Look at the SMP requests' header fields; for certain SAS 1.x SMP
-	 * functions under SAS 2.0, a zero request length really indicates
-	 * a non-zero default length.
-	 */
-	if (smp_req->req_len == 0) {
-		switch (smp_req->func) {
-		case SMP_DISCOVER:
-		case SMP_REPORT_PHY_ERR_LOG:
-		case SMP_REPORT_PHY_SATA:
-		case SMP_REPORT_ROUTE_INFO:
-			smp_req->req_len = 2;
-			break;
-		case SMP_CONF_ROUTE_INFO:
-		case SMP_PHY_CONTROL:
-		case SMP_PHY_TEST_FUNCTION:
-			smp_req->req_len = 9;
-			break;
-			/* Default - zero is a valid default for 2.0. */
-		}
-	}
-
-	scu_smp_request_construct_task_context(sci_req, smp_req->req_len);
 
 	sci_change_state(&sci_req->sm, SCI_REQ_CONSTRUCTED);
 
@@ -3404,24 +3406,12 @@
  */
 static enum sci_status isci_smp_request_build(struct isci_request *ireq)
 {
-	enum sci_status status = SCI_FAILURE;
 	struct sas_task *task = isci_request_access_task(ireq);
+	struct device *dev = &ireq->isci_host->pdev->dev;
 	struct scic_sds_request *sci_req = &ireq->sci;
+	enum sci_status status = SCI_FAILURE;
 
-	dev_dbg(&ireq->isci_host->pdev->dev,
-		"%s: request = %p\n", __func__, ireq);
-
-	dev_dbg(&ireq->isci_host->pdev->dev,
-		"%s: smp_req len = %d\n",
-		__func__,
-		task->smp_task.smp_req.length);
-
-	/* copy the smp_command to the address; */
-	sg_copy_to_buffer(&task->smp_task.smp_req, 1,
-			  &sci_req->smp.cmd,
-			  sizeof(struct smp_req));
-
-	status = scic_io_request_construct_smp(sci_req);
+	status = scic_io_request_construct_smp(dev, sci_req, task);
 	if (status != SCI_SUCCESS)
 		dev_warn(&ireq->isci_host->pdev->dev,
 			 "%s: failed with status = %d\n",
diff --git a/drivers/scsi/isci/request.h b/drivers/scsi/isci/request.h
index 324fb7b..7c8b59a 100644
--- a/drivers/scsi/isci/request.h
+++ b/drivers/scsi/isci/request.h
@@ -244,7 +244,6 @@
 		} ssp;
 
 		struct {
-			struct smp_req cmd;
 			struct smp_resp rsp;
 		} smp;
 
diff --git a/drivers/scsi/isci/sas.h b/drivers/scsi/isci/sas.h
index 822a8db..462b151 100644
--- a/drivers/scsi/isci/sas.h
+++ b/drivers/scsi/isci/sas.h
@@ -190,8 +190,6 @@
 	u8 _r_h[3];			/* bytes 37-39 */
 }  __packed;
 
-#define SMP_REQ_VENDOR_SPECIFIC_MAX_LEN 1016
-
 /*
  * struct smp_req - This structure simply unionizes the existing request
  *    structures into a common request type.
@@ -203,14 +201,7 @@
 	u8 func;		/* byte 1 */
 	u8 alloc_resp_len;	/* byte 2 */
 	u8 req_len;		/* byte 3 */
-
-	union { /* bytes 4-N */
-		u32 smp_req_gen;
-		struct smp_req_phy_id phy_id;
-		struct smp_req_phycntl phy_cntl;
-		struct smp_req_conf_rtinfo conf_rt_info;
-		u8 vendor[SMP_REQ_VENDOR_SPECIFIC_MAX_LEN];
-	};
+	u8 req_data[0];
 }  __packed;
 
 #define SMP_RESP_HDR_SZ	4