diff mbox series

[v2,02/15] mpi3mr: Add framework to issue cnfg requests

Message ID 20220804131226.16653-3-sreekanth.reddy@broadcom.com (mailing list archive)
State Accepted
Headers show
Series mpi3mr: Added Support for SAS Transport | expand

Commit Message

Sreekanth Reddy Aug. 4, 2022, 1:12 p.m. UTC
Added framework to issue config requests commands to
controller firmware.

Signed-off-by: Sreekanth Reddy <sreekanth.reddy@broadcom.com>
---
 drivers/scsi/mpi3mr/mpi3mr.h    |  28 ++++
 drivers/scsi/mpi3mr/mpi3mr_fw.c | 253 ++++++++++++++++++++++++++++++++
 drivers/scsi/mpi3mr/mpi3mr_os.c |   1 +
 3 files changed, 282 insertions(+)

Comments

Himanshu Madhani Aug. 9, 2022, 4:12 p.m. UTC | #1
> On Aug 4, 2022, at 6:12 AM, Sreekanth Reddy <sreekanth.reddy@broadcom.com> wrote:
> 
> Added framework to issue config requests commands to
> controller firmware.
> 
> Signed-off-by: Sreekanth Reddy <sreekanth.reddy@broadcom.com>
> ---
> drivers/scsi/mpi3mr/mpi3mr.h    |  28 ++++
> drivers/scsi/mpi3mr/mpi3mr_fw.c | 253 ++++++++++++++++++++++++++++++++
> drivers/scsi/mpi3mr/mpi3mr_os.c |   1 +
> 3 files changed, 282 insertions(+)
> 
> diff --git a/drivers/scsi/mpi3mr/mpi3mr.h b/drivers/scsi/mpi3mr/mpi3mr.h
> index 0935b2e..e15ad0e 100644
> --- a/drivers/scsi/mpi3mr/mpi3mr.h
> +++ b/drivers/scsi/mpi3mr/mpi3mr.h
> @@ -97,6 +97,7 @@ extern atomic64_t event_counter;
> #define MPI3MR_HOSTTAG_PEL_ABORT	3
> #define MPI3MR_HOSTTAG_PEL_WAIT		4
> #define MPI3MR_HOSTTAG_BLK_TMS		5
> +#define MPI3MR_HOSTTAG_CFG_CMDS		6
> 
> #define MPI3MR_NUM_DEVRMCMD		16
> #define MPI3MR_HOSTTAG_DEVRMCMD_MIN	(MPI3MR_HOSTTAG_BLK_TMS + 1)
> @@ -126,6 +127,8 @@ extern atomic64_t event_counter;
> 
> #define MPI3MR_WATCHDOG_INTERVAL		1000 /* in milli seconds */
> 
> +#define MPI3MR_DEFAULT_CFG_PAGE_SZ		1024 /* in bytes */
> +
> #define MPI3MR_SCMD_TIMEOUT    (60 * HZ)
> #define MPI3MR_EH_SCMD_TIMEOUT (60 * HZ)
> 
> @@ -274,6 +277,7 @@ enum mpi3mr_reset_reason {
> 	MPI3MR_RESET_FROM_SYSFS = 23,
> 	MPI3MR_RESET_FROM_SYSFS_TIMEOUT = 24,
> 	MPI3MR_RESET_FROM_FIRMWARE = 27,
> +	MPI3MR_RESET_FROM_CFG_REQ_TIMEOUT = 29,
> };
> 
> /* Queue type definitions */
> @@ -679,6 +683,21 @@ struct mpi3mr_drv_cmd {
> 	    struct mpi3mr_drv_cmd *drv_cmd);
> };
> 
> +/**
> + * struct dma_memory_desc - memory descriptor structure to store
> + * virtual address, dma address and size for any generic dma
> + * memory allocations in the driver.
> + *
> + * @size: buffer size
> + * @addr: virtual address
> + * @dma_addr: dma address
> + */
> +struct dma_memory_desc {
> +	u32 size;
> +	void *addr;
> +	dma_addr_t dma_addr;
> +};
> +
> 
> /**
>  * struct chain_element - memory descriptor structure to store
> @@ -756,6 +775,7 @@ struct scmd_priv {
>  * @num_op_reply_q: Number of operational reply queues
>  * @op_reply_qinfo: Operational reply queue info pointer
>  * @init_cmds: Command tracker for initialization commands
> + * @cfg_cmds: Command tracker for configuration requests
>  * @facts: Cached IOC facts data
>  * @op_reply_desc_sz: Operational reply descriptor size
>  * @num_reply_bufs: Number of reply buffers allocated
> @@ -854,6 +874,9 @@ struct scmd_priv {
>  * @io_throttle_low: I/O size to stop throttle in 512b blocks
>  * @num_io_throttle_group: Maximum number of throttle groups
>  * @throttle_groups: Pointer to throttle group info structures
> + * @cfg_page: Default memory for configuration pages
> + * @cfg_page_dma: Configuration page DMA address
> + * @cfg_page_sz: Default configuration page memory size
>  */
> struct mpi3mr_ioc {
> 	struct list_head list;
> @@ -904,6 +927,7 @@ struct mpi3mr_ioc {
> 	struct op_reply_qinfo *op_reply_qinfo;
> 
> 	struct mpi3mr_drv_cmd init_cmds;
> +	struct mpi3mr_drv_cmd cfg_cmds;
> 	struct mpi3mr_ioc_facts facts;
> 	u16 op_reply_desc_sz;
> 
> @@ -1025,6 +1049,10 @@ struct mpi3mr_ioc {
> 	u32 io_throttle_low;
> 	u16 num_io_throttle_group;
> 	struct mpi3mr_throttle_group_info *throttle_groups;
> +
> +	void *cfg_page;
> +	dma_addr_t cfg_page_dma;
> +	u16 cfg_page_sz;
> };
> 
> /**
> diff --git a/drivers/scsi/mpi3mr/mpi3mr_fw.c b/drivers/scsi/mpi3mr/mpi3mr_fw.c
> index 0866dfd..99d8df6 100644
> --- a/drivers/scsi/mpi3mr/mpi3mr_fw.c
> +++ b/drivers/scsi/mpi3mr/mpi3mr_fw.c
> @@ -299,6 +299,8 @@ mpi3mr_get_drv_cmd(struct mpi3mr_ioc *mrioc, u16 host_tag,
> 	switch (host_tag) {
> 	case MPI3MR_HOSTTAG_INITCMDS:
> 		return &mrioc->init_cmds;
> +	case MPI3MR_HOSTTAG_CFG_CMDS:
> +		return &mrioc->cfg_cmds;
> 	case MPI3MR_HOSTTAG_BSG_CMDS:
> 		return &mrioc->bsg_cmds;
> 	case MPI3MR_HOSTTAG_BLK_TMS:
> @@ -907,6 +909,7 @@ static const struct {
> 	{ MPI3MR_RESET_FROM_SYSFS, "sysfs invocation" },
> 	{ MPI3MR_RESET_FROM_SYSFS_TIMEOUT, "sysfs TM timeout" },
> 	{ MPI3MR_RESET_FROM_FIRMWARE, "firmware asynchronous reset" },
> +	{ MPI3MR_RESET_FROM_CFG_REQ_TIMEOUT, "configuration request timeout"},
> };
> 
> /**
> @@ -3738,6 +3741,14 @@ retry_init:
> 
> 	mpi3mr_print_ioc_info(mrioc);
> 
> +	dprint_init(mrioc, "allocating config page buffers\n");
> +	mrioc->cfg_page = dma_alloc_coherent(&mrioc->pdev->dev,
> +	    MPI3MR_DEFAULT_CFG_PAGE_SZ, &mrioc->cfg_page_dma, GFP_KERNEL);
> +	if (!mrioc->cfg_page)
> +		goto out_failed_noretry;
> +
> +	mrioc->cfg_page_sz = MPI3MR_DEFAULT_CFG_PAGE_SZ;
> +
> 	retval = mpi3mr_alloc_reply_sense_bufs(mrioc);
> 	if (retval) {
> 		ioc_err(mrioc,
> @@ -4362,6 +4373,10 @@ static void mpi3mr_flush_drv_cmds(struct mpi3mr_ioc *mrioc)
> 
> 	cmdptr = &mrioc->init_cmds;
> 	mpi3mr_drv_cmd_comp_reset(mrioc, cmdptr);
> +
> +	cmdptr = &mrioc->cfg_cmds;
> +	mpi3mr_drv_cmd_comp_reset(mrioc, cmdptr);
> +
> 	cmdptr = &mrioc->bsg_cmds;
> 	mpi3mr_drv_cmd_comp_reset(mrioc, cmdptr);
> 	cmdptr = &mrioc->host_tm_cmds;
> @@ -4786,3 +4801,241 @@ out:
> 	    ((retval == 0) ? "successful" : "failed"));
> 	return retval;
> }
> +
> +
> +/**
> + * mpi3mr_free_config_dma_memory - free memory for config page
> + * @mrioc: Adapter instance reference
> + * @mem_desc: memory descriptor structure
> + *
> + * Check whether the size of the buffer specified by the memory
> + * descriptor is greater than the default page size if so then
> + * free the memory pointed by the descriptor.
> + *
> + * Return: Nothing.
> + */
> +static void mpi3mr_free_config_dma_memory(struct mpi3mr_ioc *mrioc,
> +	struct dma_memory_desc *mem_desc)
> +{
> +	if ((mem_desc->size > mrioc->cfg_page_sz) && mem_desc->addr) {
> +		dma_free_coherent(&mrioc->pdev->dev, mem_desc->size,
> +		    mem_desc->addr, mem_desc->dma_addr);
> +		mem_desc->addr = NULL;
> +	}
> +}
> +
> +/**
> + * mpi3mr_alloc_config_dma_memory - Alloc memory for config page
> + * @mrioc: Adapter instance reference
> + * @mem_desc: Memory descriptor to hold dma memory info
> + *
> + * This function allocates new dmaable memory or provides the
> + * default config page dmaable memory based on the memory size
> + * described by the descriptor.
> + *
> + * Return: 0 on success, non-zero on failure.
> + */
> +static int mpi3mr_alloc_config_dma_memory(struct mpi3mr_ioc *mrioc,
> +	struct dma_memory_desc *mem_desc)
> +{
> +	if (mem_desc->size > mrioc->cfg_page_sz) {
> +		mem_desc->addr = dma_alloc_coherent(&mrioc->pdev->dev,
> +		    mem_desc->size, &mem_desc->dma_addr, GFP_KERNEL);
> +		if (!mem_desc->addr)
> +			return -ENOMEM;
> +	} else {
> +		mem_desc->addr = mrioc->cfg_page;
> +		mem_desc->dma_addr = mrioc->cfg_page_dma;
> +		memset(mem_desc->addr, 0, mrioc->cfg_page_sz);
> +	}
> +	return 0;
> +}
> +
> +/**
> + * mpi3mr_post_cfg_req - Issue config requests and wait
> + * @mrioc: Adapter instance reference
> + * @cfg_req: Configuration request
> + * @timeout: Timeout in seconds
> + * @ioc_status: Pointer to return ioc status
> + *
> + * A generic function for posting MPI3 configuration request to
> + * the firmware. This blocks for the completion of request for
> + * timeout seconds and if the request times out this function
> + * faults the controller with proper reason code.
> + *
> + * On successful completion of the request this function returns
> + * appropriate ioc status from the firmware back to the caller.
> + *
> + * Return: 0 on success, non-zero on failure.
> + */
> +static int mpi3mr_post_cfg_req(struct mpi3mr_ioc *mrioc,
> +	struct mpi3_config_request *cfg_req, int timeout, u16 *ioc_status)
> +{
> +	int retval = 0;
> +
> +	mutex_lock(&mrioc->cfg_cmds.mutex);
> +	if (mrioc->cfg_cmds.state & MPI3MR_CMD_PENDING) {
> +		retval = -1;
> +		ioc_err(mrioc, "sending config request failed due to command in use\n");
> +		mutex_unlock(&mrioc->cfg_cmds.mutex);
> +		goto out;
> +	}
> +	mrioc->cfg_cmds.state = MPI3MR_CMD_PENDING;
> +	mrioc->cfg_cmds.is_waiting = 1;
> +	mrioc->cfg_cmds.callback = NULL;
> +	mrioc->cfg_cmds.ioc_status = 0;
> +	mrioc->cfg_cmds.ioc_loginfo = 0;
> +
> +	cfg_req->host_tag = cpu_to_le16(MPI3MR_HOSTTAG_CFG_CMDS);
> +	cfg_req->function = MPI3_FUNCTION_CONFIG;
> +
> +	init_completion(&mrioc->cfg_cmds.done);
> +	dprint_cfg_info(mrioc, "posting config request\n");
> +	if (mrioc->logging_level & MPI3_DEBUG_CFG_INFO)
> +		dprint_dump(cfg_req, sizeof(struct mpi3_config_request),
> +		    "mpi3_cfg_req");
> +	retval = mpi3mr_admin_request_post(mrioc, cfg_req, sizeof(*cfg_req), 1);
> +	if (retval) {
> +		ioc_err(mrioc, "posting config request failed\n");
> +		goto out_unlock;
> +	}
> +	wait_for_completion_timeout(&mrioc->cfg_cmds.done, (timeout * HZ));
> +	if (!(mrioc->cfg_cmds.state & MPI3MR_CMD_COMPLETE)) {
> +		mpi3mr_check_rh_fault_ioc(mrioc,
> +		    MPI3MR_RESET_FROM_CFG_REQ_TIMEOUT);
> +		ioc_err(mrioc, "config request timed out\n");
> +		retval = -1;
> +		goto out_unlock;
> +	}
> +	*ioc_status = mrioc->cfg_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK;
> +	if ((*ioc_status) != MPI3_IOCSTATUS_SUCCESS)
> +		dprint_cfg_err(mrioc,
> +		    "cfg_page request returned with ioc_status(0x%04x), log_info(0x%08x)\n",
> +		    *ioc_status, mrioc->cfg_cmds.ioc_loginfo);
> +
> +out_unlock:
> +	mrioc->cfg_cmds.state = MPI3MR_CMD_NOTUSED;
> +	mutex_unlock(&mrioc->cfg_cmds.mutex);
> +
> +out:
> +	return retval;
> +}
> +
> +/**
> + * mpi3mr_process_cfg_req - config page request processor
> + * @mrioc: Adapter instance reference
> + * @cfg_req: Configuration request
> + * @cfg_hdr: Configuration page header
> + * @timeout: Timeout in seconds
> + * @ioc_status: Pointer to return ioc status
> + * @cfg_buf: Memory pointer to copy config page or header
> + * @cfg_buf_sz: Size of the memory to get config page or header
> + *
> + * This is handler for config page read, write and config page
> + * header read operations.
> + *
> + * This function expects the cfg_req to be populated with page
> + * type, page number, action for the header read and with page
> + * address for all other operations.
> + *
> + * The cfg_hdr can be passed as null for reading required header
> + * details for read/write pages the cfg_hdr should point valid
> + * configuration page header.
> + *
> + * This allocates dmaable memory based on the size of the config
> + * buffer and set the SGE of the cfg_req.
> + *
> + * For write actions, the config page data has to be passed in
> + * the cfg_buf and size of the data has to be mentioned in the
> + * cfg_buf_sz.
> + *
> + * For read/header actions, on successful completion of the
> + * request with successful ioc_status the data will be copied
> + * into the cfg_buf limited to a minimum of actual page size and
> + * cfg_buf_sz
> + *
> + *
> + * Return: 0 on success, non-zero on failure.
> + */
> +static int mpi3mr_process_cfg_req(struct mpi3mr_ioc *mrioc,
> +	struct mpi3_config_request *cfg_req,
> +	struct mpi3_config_page_header *cfg_hdr, int timeout, u16 *ioc_status,
> +	void *cfg_buf, u32 cfg_buf_sz)
> +{
> +	struct dma_memory_desc mem_desc;
> +	int retval = -1;
> +	u8 invalid_action = 0;
> +	u8 sgl_flags = MPI3MR_SGEFLAGS_SYSTEM_SIMPLE_END_OF_LIST;
> +
> +	memset(&mem_desc, 0, sizeof(struct dma_memory_desc));
> +
> +	if (cfg_req->action == MPI3_CONFIG_ACTION_PAGE_HEADER)
> +		mem_desc.size = sizeof(struct mpi3_config_page_header);
> +	else {
> +		if (!cfg_hdr) {
> +			ioc_err(mrioc, "null config header passed for config action(%d), page_type(0x%02x), page_num(%d)\n",
> +			    cfg_req->action, cfg_req->page_type,
> +			    cfg_req->page_number);
> +			goto out;
> +		}
> +		switch (cfg_hdr->page_attribute & MPI3_CONFIG_PAGEATTR_MASK) {
> +		case MPI3_CONFIG_PAGEATTR_READ_ONLY:
> +			if (cfg_req->action
> +			    != MPI3_CONFIG_ACTION_READ_CURRENT)
> +				invalid_action = 1;
> +			break;
> +		case MPI3_CONFIG_PAGEATTR_CHANGEABLE:
> +			if ((cfg_req->action ==
> +			     MPI3_CONFIG_ACTION_READ_PERSISTENT) ||
> +			    (cfg_req->action ==
> +			     MPI3_CONFIG_ACTION_WRITE_PERSISTENT))
> +				invalid_action = 1;
> +			break;
> +		case MPI3_CONFIG_PAGEATTR_PERSISTENT:
> +		default:
> +			break;
> +		}
> +		if (invalid_action) {
> +			ioc_err(mrioc,
> +			    "config action(%d) is not allowed for page_type(0x%02x), page_num(%d) with page_attribute(0x%02x)\n",
> +			    cfg_req->action, cfg_req->page_type,
> +			    cfg_req->page_number, cfg_hdr->page_attribute);
> +			goto out;
> +		}
> +		mem_desc.size = le16_to_cpu(cfg_hdr->page_length) * 4;
> +		cfg_req->page_length = cfg_hdr->page_length;
> +		cfg_req->page_version = cfg_hdr->page_version;
> +	}
> +	if (mpi3mr_alloc_config_dma_memory(mrioc, &mem_desc))
> +		goto out;
> +
> +	mpi3mr_add_sg_single(&cfg_req->sgl, sgl_flags, mem_desc.size,
> +	    mem_desc.dma_addr);
> +
> +	if ((cfg_req->action == MPI3_CONFIG_ACTION_WRITE_PERSISTENT) ||
> +	    (cfg_req->action == MPI3_CONFIG_ACTION_WRITE_CURRENT)) {
> +		memcpy(mem_desc.addr, cfg_buf, min_t(u16, mem_desc.size,
> +		    cfg_buf_sz));
> +		dprint_cfg_info(mrioc, "config buffer to be written\n");
> +		if (mrioc->logging_level & MPI3_DEBUG_CFG_INFO)
> +			dprint_dump(mem_desc.addr, mem_desc.size, "cfg_buf");
> +	}
> +
> +	if (mpi3mr_post_cfg_req(mrioc, cfg_req, timeout, ioc_status))
> +		goto out;
> +
> +	retval = 0;
> +	if ((*ioc_status == MPI3_IOCSTATUS_SUCCESS) &&
> +	    (cfg_req->action != MPI3_CONFIG_ACTION_WRITE_PERSISTENT) &&
> +	    (cfg_req->action != MPI3_CONFIG_ACTION_WRITE_CURRENT)) {
> +		memcpy(cfg_buf, mem_desc.addr, min_t(u16, mem_desc.size,
> +		    cfg_buf_sz));
> +		dprint_cfg_info(mrioc, "config buffer read\n");
> +		if (mrioc->logging_level & MPI3_DEBUG_CFG_INFO)
> +			dprint_dump(mem_desc.addr, mem_desc.size, "cfg_buf");
> +	}
> +
> +out:
> +	mpi3mr_free_config_dma_memory(mrioc, &mem_desc);
> +	return retval;
> +}
> diff --git a/drivers/scsi/mpi3mr/mpi3mr_os.c b/drivers/scsi/mpi3mr/mpi3mr_os.c
> index 8bdf927..40bed22 100644
> --- a/drivers/scsi/mpi3mr/mpi3mr_os.c
> +++ b/drivers/scsi/mpi3mr/mpi3mr_os.c
> @@ -4574,6 +4574,7 @@ mpi3mr_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> 	mpi3mr_init_drv_cmd(&mrioc->init_cmds, MPI3MR_HOSTTAG_INITCMDS);
> 	mpi3mr_init_drv_cmd(&mrioc->host_tm_cmds, MPI3MR_HOSTTAG_BLK_TMS);
> 	mpi3mr_init_drv_cmd(&mrioc->bsg_cmds, MPI3MR_HOSTTAG_BSG_CMDS);
> +	mpi3mr_init_drv_cmd(&mrioc->cfg_cmds, MPI3MR_HOSTTAG_CFG_CMDS);
> 
> 	for (i = 0; i < MPI3MR_NUM_DEVRMCMD; i++)
> 		mpi3mr_init_drv_cmd(&mrioc->dev_rmhs_cmds[i],
> -- 
> 2.27.0
> 

Looks Good. 

Reviewed-by: Himanshu Madhani <himanshu.madhani@oracle.com>

— 
Himanshu Madhani	Oracle Linux Engineering
diff mbox series

Patch

diff --git a/drivers/scsi/mpi3mr/mpi3mr.h b/drivers/scsi/mpi3mr/mpi3mr.h
index 0935b2e..e15ad0e 100644
--- a/drivers/scsi/mpi3mr/mpi3mr.h
+++ b/drivers/scsi/mpi3mr/mpi3mr.h
@@ -97,6 +97,7 @@  extern atomic64_t event_counter;
 #define MPI3MR_HOSTTAG_PEL_ABORT	3
 #define MPI3MR_HOSTTAG_PEL_WAIT		4
 #define MPI3MR_HOSTTAG_BLK_TMS		5
+#define MPI3MR_HOSTTAG_CFG_CMDS		6
 
 #define MPI3MR_NUM_DEVRMCMD		16
 #define MPI3MR_HOSTTAG_DEVRMCMD_MIN	(MPI3MR_HOSTTAG_BLK_TMS + 1)
@@ -126,6 +127,8 @@  extern atomic64_t event_counter;
 
 #define MPI3MR_WATCHDOG_INTERVAL		1000 /* in milli seconds */
 
+#define MPI3MR_DEFAULT_CFG_PAGE_SZ		1024 /* in bytes */
+
 #define MPI3MR_SCMD_TIMEOUT    (60 * HZ)
 #define MPI3MR_EH_SCMD_TIMEOUT (60 * HZ)
 
@@ -274,6 +277,7 @@  enum mpi3mr_reset_reason {
 	MPI3MR_RESET_FROM_SYSFS = 23,
 	MPI3MR_RESET_FROM_SYSFS_TIMEOUT = 24,
 	MPI3MR_RESET_FROM_FIRMWARE = 27,
+	MPI3MR_RESET_FROM_CFG_REQ_TIMEOUT = 29,
 };
 
 /* Queue type definitions */
@@ -679,6 +683,21 @@  struct mpi3mr_drv_cmd {
 	    struct mpi3mr_drv_cmd *drv_cmd);
 };
 
+/**
+ * struct dma_memory_desc - memory descriptor structure to store
+ * virtual address, dma address and size for any generic dma
+ * memory allocations in the driver.
+ *
+ * @size: buffer size
+ * @addr: virtual address
+ * @dma_addr: dma address
+ */
+struct dma_memory_desc {
+	u32 size;
+	void *addr;
+	dma_addr_t dma_addr;
+};
+
 
 /**
  * struct chain_element - memory descriptor structure to store
@@ -756,6 +775,7 @@  struct scmd_priv {
  * @num_op_reply_q: Number of operational reply queues
  * @op_reply_qinfo: Operational reply queue info pointer
  * @init_cmds: Command tracker for initialization commands
+ * @cfg_cmds: Command tracker for configuration requests
  * @facts: Cached IOC facts data
  * @op_reply_desc_sz: Operational reply descriptor size
  * @num_reply_bufs: Number of reply buffers allocated
@@ -854,6 +874,9 @@  struct scmd_priv {
  * @io_throttle_low: I/O size to stop throttle in 512b blocks
  * @num_io_throttle_group: Maximum number of throttle groups
  * @throttle_groups: Pointer to throttle group info structures
+ * @cfg_page: Default memory for configuration pages
+ * @cfg_page_dma: Configuration page DMA address
+ * @cfg_page_sz: Default configuration page memory size
  */
 struct mpi3mr_ioc {
 	struct list_head list;
@@ -904,6 +927,7 @@  struct mpi3mr_ioc {
 	struct op_reply_qinfo *op_reply_qinfo;
 
 	struct mpi3mr_drv_cmd init_cmds;
+	struct mpi3mr_drv_cmd cfg_cmds;
 	struct mpi3mr_ioc_facts facts;
 	u16 op_reply_desc_sz;
 
@@ -1025,6 +1049,10 @@  struct mpi3mr_ioc {
 	u32 io_throttle_low;
 	u16 num_io_throttle_group;
 	struct mpi3mr_throttle_group_info *throttle_groups;
+
+	void *cfg_page;
+	dma_addr_t cfg_page_dma;
+	u16 cfg_page_sz;
 };
 
 /**
diff --git a/drivers/scsi/mpi3mr/mpi3mr_fw.c b/drivers/scsi/mpi3mr/mpi3mr_fw.c
index 0866dfd..99d8df6 100644
--- a/drivers/scsi/mpi3mr/mpi3mr_fw.c
+++ b/drivers/scsi/mpi3mr/mpi3mr_fw.c
@@ -299,6 +299,8 @@  mpi3mr_get_drv_cmd(struct mpi3mr_ioc *mrioc, u16 host_tag,
 	switch (host_tag) {
 	case MPI3MR_HOSTTAG_INITCMDS:
 		return &mrioc->init_cmds;
+	case MPI3MR_HOSTTAG_CFG_CMDS:
+		return &mrioc->cfg_cmds;
 	case MPI3MR_HOSTTAG_BSG_CMDS:
 		return &mrioc->bsg_cmds;
 	case MPI3MR_HOSTTAG_BLK_TMS:
@@ -907,6 +909,7 @@  static const struct {
 	{ MPI3MR_RESET_FROM_SYSFS, "sysfs invocation" },
 	{ MPI3MR_RESET_FROM_SYSFS_TIMEOUT, "sysfs TM timeout" },
 	{ MPI3MR_RESET_FROM_FIRMWARE, "firmware asynchronous reset" },
+	{ MPI3MR_RESET_FROM_CFG_REQ_TIMEOUT, "configuration request timeout"},
 };
 
 /**
@@ -3738,6 +3741,14 @@  retry_init:
 
 	mpi3mr_print_ioc_info(mrioc);
 
+	dprint_init(mrioc, "allocating config page buffers\n");
+	mrioc->cfg_page = dma_alloc_coherent(&mrioc->pdev->dev,
+	    MPI3MR_DEFAULT_CFG_PAGE_SZ, &mrioc->cfg_page_dma, GFP_KERNEL);
+	if (!mrioc->cfg_page)
+		goto out_failed_noretry;
+
+	mrioc->cfg_page_sz = MPI3MR_DEFAULT_CFG_PAGE_SZ;
+
 	retval = mpi3mr_alloc_reply_sense_bufs(mrioc);
 	if (retval) {
 		ioc_err(mrioc,
@@ -4362,6 +4373,10 @@  static void mpi3mr_flush_drv_cmds(struct mpi3mr_ioc *mrioc)
 
 	cmdptr = &mrioc->init_cmds;
 	mpi3mr_drv_cmd_comp_reset(mrioc, cmdptr);
+
+	cmdptr = &mrioc->cfg_cmds;
+	mpi3mr_drv_cmd_comp_reset(mrioc, cmdptr);
+
 	cmdptr = &mrioc->bsg_cmds;
 	mpi3mr_drv_cmd_comp_reset(mrioc, cmdptr);
 	cmdptr = &mrioc->host_tm_cmds;
@@ -4786,3 +4801,241 @@  out:
 	    ((retval == 0) ? "successful" : "failed"));
 	return retval;
 }
+
+
+/**
+ * mpi3mr_free_config_dma_memory - free memory for config page
+ * @mrioc: Adapter instance reference
+ * @mem_desc: memory descriptor structure
+ *
+ * Check whether the size of the buffer specified by the memory
+ * descriptor is greater than the default page size if so then
+ * free the memory pointed by the descriptor.
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_free_config_dma_memory(struct mpi3mr_ioc *mrioc,
+	struct dma_memory_desc *mem_desc)
+{
+	if ((mem_desc->size > mrioc->cfg_page_sz) && mem_desc->addr) {
+		dma_free_coherent(&mrioc->pdev->dev, mem_desc->size,
+		    mem_desc->addr, mem_desc->dma_addr);
+		mem_desc->addr = NULL;
+	}
+}
+
+/**
+ * mpi3mr_alloc_config_dma_memory - Alloc memory for config page
+ * @mrioc: Adapter instance reference
+ * @mem_desc: Memory descriptor to hold dma memory info
+ *
+ * This function allocates new dmaable memory or provides the
+ * default config page dmaable memory based on the memory size
+ * described by the descriptor.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+static int mpi3mr_alloc_config_dma_memory(struct mpi3mr_ioc *mrioc,
+	struct dma_memory_desc *mem_desc)
+{
+	if (mem_desc->size > mrioc->cfg_page_sz) {
+		mem_desc->addr = dma_alloc_coherent(&mrioc->pdev->dev,
+		    mem_desc->size, &mem_desc->dma_addr, GFP_KERNEL);
+		if (!mem_desc->addr)
+			return -ENOMEM;
+	} else {
+		mem_desc->addr = mrioc->cfg_page;
+		mem_desc->dma_addr = mrioc->cfg_page_dma;
+		memset(mem_desc->addr, 0, mrioc->cfg_page_sz);
+	}
+	return 0;
+}
+
+/**
+ * mpi3mr_post_cfg_req - Issue config requests and wait
+ * @mrioc: Adapter instance reference
+ * @cfg_req: Configuration request
+ * @timeout: Timeout in seconds
+ * @ioc_status: Pointer to return ioc status
+ *
+ * A generic function for posting MPI3 configuration request to
+ * the firmware. This blocks for the completion of request for
+ * timeout seconds and if the request times out this function
+ * faults the controller with proper reason code.
+ *
+ * On successful completion of the request this function returns
+ * appropriate ioc status from the firmware back to the caller.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+static int mpi3mr_post_cfg_req(struct mpi3mr_ioc *mrioc,
+	struct mpi3_config_request *cfg_req, int timeout, u16 *ioc_status)
+{
+	int retval = 0;
+
+	mutex_lock(&mrioc->cfg_cmds.mutex);
+	if (mrioc->cfg_cmds.state & MPI3MR_CMD_PENDING) {
+		retval = -1;
+		ioc_err(mrioc, "sending config request failed due to command in use\n");
+		mutex_unlock(&mrioc->cfg_cmds.mutex);
+		goto out;
+	}
+	mrioc->cfg_cmds.state = MPI3MR_CMD_PENDING;
+	mrioc->cfg_cmds.is_waiting = 1;
+	mrioc->cfg_cmds.callback = NULL;
+	mrioc->cfg_cmds.ioc_status = 0;
+	mrioc->cfg_cmds.ioc_loginfo = 0;
+
+	cfg_req->host_tag = cpu_to_le16(MPI3MR_HOSTTAG_CFG_CMDS);
+	cfg_req->function = MPI3_FUNCTION_CONFIG;
+
+	init_completion(&mrioc->cfg_cmds.done);
+	dprint_cfg_info(mrioc, "posting config request\n");
+	if (mrioc->logging_level & MPI3_DEBUG_CFG_INFO)
+		dprint_dump(cfg_req, sizeof(struct mpi3_config_request),
+		    "mpi3_cfg_req");
+	retval = mpi3mr_admin_request_post(mrioc, cfg_req, sizeof(*cfg_req), 1);
+	if (retval) {
+		ioc_err(mrioc, "posting config request failed\n");
+		goto out_unlock;
+	}
+	wait_for_completion_timeout(&mrioc->cfg_cmds.done, (timeout * HZ));
+	if (!(mrioc->cfg_cmds.state & MPI3MR_CMD_COMPLETE)) {
+		mpi3mr_check_rh_fault_ioc(mrioc,
+		    MPI3MR_RESET_FROM_CFG_REQ_TIMEOUT);
+		ioc_err(mrioc, "config request timed out\n");
+		retval = -1;
+		goto out_unlock;
+	}
+	*ioc_status = mrioc->cfg_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK;
+	if ((*ioc_status) != MPI3_IOCSTATUS_SUCCESS)
+		dprint_cfg_err(mrioc,
+		    "cfg_page request returned with ioc_status(0x%04x), log_info(0x%08x)\n",
+		    *ioc_status, mrioc->cfg_cmds.ioc_loginfo);
+
+out_unlock:
+	mrioc->cfg_cmds.state = MPI3MR_CMD_NOTUSED;
+	mutex_unlock(&mrioc->cfg_cmds.mutex);
+
+out:
+	return retval;
+}
+
+/**
+ * mpi3mr_process_cfg_req - config page request processor
+ * @mrioc: Adapter instance reference
+ * @cfg_req: Configuration request
+ * @cfg_hdr: Configuration page header
+ * @timeout: Timeout in seconds
+ * @ioc_status: Pointer to return ioc status
+ * @cfg_buf: Memory pointer to copy config page or header
+ * @cfg_buf_sz: Size of the memory to get config page or header
+ *
+ * This is handler for config page read, write and config page
+ * header read operations.
+ *
+ * This function expects the cfg_req to be populated with page
+ * type, page number, action for the header read and with page
+ * address for all other operations.
+ *
+ * The cfg_hdr can be passed as null for reading required header
+ * details for read/write pages the cfg_hdr should point valid
+ * configuration page header.
+ *
+ * This allocates dmaable memory based on the size of the config
+ * buffer and set the SGE of the cfg_req.
+ *
+ * For write actions, the config page data has to be passed in
+ * the cfg_buf and size of the data has to be mentioned in the
+ * cfg_buf_sz.
+ *
+ * For read/header actions, on successful completion of the
+ * request with successful ioc_status the data will be copied
+ * into the cfg_buf limited to a minimum of actual page size and
+ * cfg_buf_sz
+ *
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+static int mpi3mr_process_cfg_req(struct mpi3mr_ioc *mrioc,
+	struct mpi3_config_request *cfg_req,
+	struct mpi3_config_page_header *cfg_hdr, int timeout, u16 *ioc_status,
+	void *cfg_buf, u32 cfg_buf_sz)
+{
+	struct dma_memory_desc mem_desc;
+	int retval = -1;
+	u8 invalid_action = 0;
+	u8 sgl_flags = MPI3MR_SGEFLAGS_SYSTEM_SIMPLE_END_OF_LIST;
+
+	memset(&mem_desc, 0, sizeof(struct dma_memory_desc));
+
+	if (cfg_req->action == MPI3_CONFIG_ACTION_PAGE_HEADER)
+		mem_desc.size = sizeof(struct mpi3_config_page_header);
+	else {
+		if (!cfg_hdr) {
+			ioc_err(mrioc, "null config header passed for config action(%d), page_type(0x%02x), page_num(%d)\n",
+			    cfg_req->action, cfg_req->page_type,
+			    cfg_req->page_number);
+			goto out;
+		}
+		switch (cfg_hdr->page_attribute & MPI3_CONFIG_PAGEATTR_MASK) {
+		case MPI3_CONFIG_PAGEATTR_READ_ONLY:
+			if (cfg_req->action
+			    != MPI3_CONFIG_ACTION_READ_CURRENT)
+				invalid_action = 1;
+			break;
+		case MPI3_CONFIG_PAGEATTR_CHANGEABLE:
+			if ((cfg_req->action ==
+			     MPI3_CONFIG_ACTION_READ_PERSISTENT) ||
+			    (cfg_req->action ==
+			     MPI3_CONFIG_ACTION_WRITE_PERSISTENT))
+				invalid_action = 1;
+			break;
+		case MPI3_CONFIG_PAGEATTR_PERSISTENT:
+		default:
+			break;
+		}
+		if (invalid_action) {
+			ioc_err(mrioc,
+			    "config action(%d) is not allowed for page_type(0x%02x), page_num(%d) with page_attribute(0x%02x)\n",
+			    cfg_req->action, cfg_req->page_type,
+			    cfg_req->page_number, cfg_hdr->page_attribute);
+			goto out;
+		}
+		mem_desc.size = le16_to_cpu(cfg_hdr->page_length) * 4;
+		cfg_req->page_length = cfg_hdr->page_length;
+		cfg_req->page_version = cfg_hdr->page_version;
+	}
+	if (mpi3mr_alloc_config_dma_memory(mrioc, &mem_desc))
+		goto out;
+
+	mpi3mr_add_sg_single(&cfg_req->sgl, sgl_flags, mem_desc.size,
+	    mem_desc.dma_addr);
+
+	if ((cfg_req->action == MPI3_CONFIG_ACTION_WRITE_PERSISTENT) ||
+	    (cfg_req->action == MPI3_CONFIG_ACTION_WRITE_CURRENT)) {
+		memcpy(mem_desc.addr, cfg_buf, min_t(u16, mem_desc.size,
+		    cfg_buf_sz));
+		dprint_cfg_info(mrioc, "config buffer to be written\n");
+		if (mrioc->logging_level & MPI3_DEBUG_CFG_INFO)
+			dprint_dump(mem_desc.addr, mem_desc.size, "cfg_buf");
+	}
+
+	if (mpi3mr_post_cfg_req(mrioc, cfg_req, timeout, ioc_status))
+		goto out;
+
+	retval = 0;
+	if ((*ioc_status == MPI3_IOCSTATUS_SUCCESS) &&
+	    (cfg_req->action != MPI3_CONFIG_ACTION_WRITE_PERSISTENT) &&
+	    (cfg_req->action != MPI3_CONFIG_ACTION_WRITE_CURRENT)) {
+		memcpy(cfg_buf, mem_desc.addr, min_t(u16, mem_desc.size,
+		    cfg_buf_sz));
+		dprint_cfg_info(mrioc, "config buffer read\n");
+		if (mrioc->logging_level & MPI3_DEBUG_CFG_INFO)
+			dprint_dump(mem_desc.addr, mem_desc.size, "cfg_buf");
+	}
+
+out:
+	mpi3mr_free_config_dma_memory(mrioc, &mem_desc);
+	return retval;
+}
diff --git a/drivers/scsi/mpi3mr/mpi3mr_os.c b/drivers/scsi/mpi3mr/mpi3mr_os.c
index 8bdf927..40bed22 100644
--- a/drivers/scsi/mpi3mr/mpi3mr_os.c
+++ b/drivers/scsi/mpi3mr/mpi3mr_os.c
@@ -4574,6 +4574,7 @@  mpi3mr_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	mpi3mr_init_drv_cmd(&mrioc->init_cmds, MPI3MR_HOSTTAG_INITCMDS);
 	mpi3mr_init_drv_cmd(&mrioc->host_tm_cmds, MPI3MR_HOSTTAG_BLK_TMS);
 	mpi3mr_init_drv_cmd(&mrioc->bsg_cmds, MPI3MR_HOSTTAG_BSG_CMDS);
+	mpi3mr_init_drv_cmd(&mrioc->cfg_cmds, MPI3MR_HOSTTAG_CFG_CMDS);
 
 	for (i = 0; i < MPI3MR_NUM_DEVRMCMD; i++)
 		mpi3mr_init_drv_cmd(&mrioc->dev_rmhs_cmds[i],