diff mbox series

[3/7] mpi3mr: controller management application support

Message ID 20210921184600.64427-4-kashyap.desai@broadcom.com (mailing list archive)
State New
Headers show
Series adding application support | expand

Commit Message

Kashyap Desai Sept. 21, 2021, 6:45 p.m. UTC
This patch series implements various IOCTL interfaces in the mpi3mr driver
for supporting management applications developed specifically to manage
the Avenger series of the RAID and I/O controllers and devices attached to
those controllers

Signed-off-by: Kashyap Desai <kashyap.desai@broadcom.com>

Cc: sathya.prakash@broadcom.com
---
 drivers/scsi/mpi3mr/Makefile     |   1 +
 drivers/scsi/mpi3mr/mpi3mr.h     |  23 +
 drivers/scsi/mpi3mr/mpi3mr_app.c | 850 +++++++++++++++++++++++++++++++
 drivers/scsi/mpi3mr/mpi3mr_app.h | 369 ++++++++++++++
 drivers/scsi/mpi3mr/mpi3mr_fw.c  |  24 +
 drivers/scsi/mpi3mr/mpi3mr_os.c  |   9 +-
 6 files changed, 1274 insertions(+), 2 deletions(-)
 create mode 100644 drivers/scsi/mpi3mr/mpi3mr_app.c
 create mode 100644 drivers/scsi/mpi3mr/mpi3mr_app.h
diff mbox series

Patch

diff --git a/drivers/scsi/mpi3mr/Makefile b/drivers/scsi/mpi3mr/Makefile
index 7c2063e04c81..7c1f79186679 100644
--- a/drivers/scsi/mpi3mr/Makefile
+++ b/drivers/scsi/mpi3mr/Makefile
@@ -2,3 +2,4 @@ 
 obj-m += mpi3mr.o
 mpi3mr-y +=  mpi3mr_os.o     \
 		mpi3mr_fw.o \
+		mpi3mr_app.o
diff --git a/drivers/scsi/mpi3mr/mpi3mr.h b/drivers/scsi/mpi3mr/mpi3mr.h
index 9787b53a2b59..5ae73927590c 100644
--- a/drivers/scsi/mpi3mr/mpi3mr.h
+++ b/drivers/scsi/mpi3mr/mpi3mr.h
@@ -538,6 +538,7 @@  struct mpi3mr_sdev_priv_data {
  * @ioc_status: IOC status from the firmware
  * @ioc_loginfo:IOC log info from the firmware
  * @is_waiting: Is the command issued in block mode
+ * @is_sense: Is Sense data present
  * @retry_count: Retry count for retriable commands
  * @host_tag: Host tag used by the command
  * @callback: Callback for non blocking commands
@@ -553,6 +554,7 @@  struct mpi3mr_drv_cmd {
 	u16 ioc_status;
 	u32 ioc_loginfo;
 	u8 is_waiting;
+	bool is_sense;
 	u8 retry_count;
 	u16 host_tag;
 
@@ -679,6 +681,7 @@  struct scmd_priv {
  * @chain_bitmap_sz: Chain buffer allocator bitmap size
  * @chain_bitmap: Chain buffer allocator bitmap
  * @chain_buf_lock: Chain buffer list lock
+ * @ioctl_cmds: Command tracker for IOCTL command
  * @host_tm_cmds: Command tracker for task management commands
  * @dev_rmhs_cmds: Command tracker for device removal commands
  * @devrem_bitmap_sz: Device removal bitmap size
@@ -690,6 +693,7 @@  struct scmd_priv {
  * @fault_dbg: Fault debug flag
  * @reset_in_progress: Reset in progress flag
  * @unrecoverable: Controller unrecoverable flag
+ * @block_ioctls: Block IOCTL flag
  * @reset_mutex: Controller reset mutex
  * @reset_waitq: Controller reset  wait queue
  * @diagsave_timeout: Diagnostic information save timeout
@@ -699,6 +703,9 @@  struct scmd_priv {
  * @driver_info: Driver, Kernel, OS information to firmware
  * @change_count: Topology change count
  * @op_reply_q_offset: Operational reply queue offset with MSIx
+ * @logdata_buf: Circular buffer to store log data entries
+ * @logdata_buf_idx: Index of entry in buffer to store
+ * @logdata_entry_sz: log data entry size
  */
 struct mpi3mr_ioc {
 	struct list_head list;
@@ -803,6 +810,7 @@  struct mpi3mr_ioc {
 	void *chain_bitmap;
 	spinlock_t chain_buf_lock;
 
+	struct mpi3mr_drv_cmd ioctl_cmds;
 	struct mpi3mr_drv_cmd host_tm_cmds;
 	struct mpi3mr_drv_cmd dev_rmhs_cmds[MPI3MR_NUM_DEVRMCMD];
 	u16 devrem_bitmap_sz;
@@ -815,6 +823,7 @@  struct mpi3mr_ioc {
 	u8 fault_dbg;
 	u8 reset_in_progress;
 	u8 unrecoverable;
+	u8 block_ioctls;
 	struct mutex reset_mutex;
 	wait_queue_head_t reset_waitq;
 
@@ -826,6 +835,10 @@  struct mpi3mr_ioc {
 	struct mpi3_driver_info_layout driver_info;
 	u16 change_count;
 	u16 op_reply_q_offset;
+
+	u8 *logdata_buf;
+	u16 logdata_buf_idx;
+	u16 logdata_entry_sz;
 };
 
 /**
@@ -889,6 +902,13 @@  void mpi3mr_repost_sense_buf(struct mpi3mr_ioc *mrioc,
 void mpi3mr_memset_buffers(struct mpi3mr_ioc *mrioc);
 void mpi3mr_os_handle_events(struct mpi3mr_ioc *mrioc,
 			     struct mpi3_event_notification_reply *event_reply);
+struct mpi3mr_tgt_dev *mpi3mr_get_tgtdev_by_handle(
+				struct mpi3mr_ioc *mrioc, u16 handle);
+struct mpi3mr_tgt_dev *mpi3mr_get_tgtdev_by_perst_id(
+				struct mpi3mr_ioc *mrioc, u16 persist_id);
+struct mpi3mr_tgt_dev *mpi3mr_get_tgtdev_from_tgtpriv(
+				struct mpi3mr_ioc *mrioc,
+				struct mpi3mr_stgt_priv_data *tgt_priv);
 void mpi3mr_process_op_reply_desc(struct mpi3mr_ioc *mrioc,
 				  struct mpi3_default_reply_descriptor *reply_desc,
 				  u64 *reply_dma, u16 qidx);
@@ -913,4 +933,7 @@  void mpi3mr_invalidate_devhandles(struct mpi3mr_ioc *mrioc);
 void mpi3mr_rfresh_tgtdevs(struct mpi3mr_ioc *mrioc);
 void mpi3mr_flush_delayed_rmhs_list(struct mpi3mr_ioc *mrioc);
 
+void mpi3mr_app_init(void);
+void mpi3mr_app_exit(void);
+
 #endif /*MPI3MR_H_INCLUDED*/
diff --git a/drivers/scsi/mpi3mr/mpi3mr_app.c b/drivers/scsi/mpi3mr/mpi3mr_app.c
new file mode 100644
index 000000000000..021615889dcd
--- /dev/null
+++ b/drivers/scsi/mpi3mr/mpi3mr_app.c
@@ -0,0 +1,850 @@ 
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for Broadcom MPI3 Storage Controllers
+ *
+ * Copyright (C) 2017-2021 Broadcom Inc.
+ *  (mailto: mpi3mr-linuxdrv.pdl@broadcom.com)
+ *
+ */
+
+#include "mpi3mr.h"
+#include "mpi3mr_app.h"
+
+static struct fasync_struct *mpi3mr_app_async_queue;
+
+/**
+ * mpi3mr_verify_adapter - verify adapter number is valid
+ * @ioc_number: Adapter number
+ * @mriocpp: Pointer to hold per adapter instance
+ *
+ * This function checks whether given adapter number matches
+ * with an adapter id in the driver's list and if so fills
+ * pointer to the per adapter instance in mriocpp else set that
+ * to NULL.
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_verify_adapter(int ioc_number, struct mpi3mr_ioc **mriocpp)
+{
+	struct mpi3mr_ioc *mrioc;
+
+	spin_lock(&mrioc_list_lock);
+	list_for_each_entry(mrioc, &mrioc_list, list) {
+		if (mrioc->id != ioc_number)
+			continue;
+		spin_unlock(&mrioc_list_lock);
+		*mriocpp = mrioc;
+		return;
+	}
+	spin_unlock(&mrioc_list_lock);
+	*mriocpp = NULL;
+}
+
+/**
+ * mpi3mr_get_all_tgt_info - Get all target information
+ * @mrioc: Adapter instance reference
+ * @data_in_buf: User buffer to copy the target information
+ * @data_in_sz: length of the user buffer.
+ *
+ * This function copies the driver managed target devices device
+ * handle, persistent ID, bus ID and taret ID to the user
+ * provided buffer for the specific controller. This function
+ * also provides the number of devices managed by the driver for
+ * the specific controller.
+ *
+ * Return: 0 on success and proper error codes on failure
+ */
+static long mpi3mr_get_all_tgt_info(struct mpi3mr_ioc *mrioc,
+		void __user *data_in_buf, uint32_t data_in_sz)
+{
+	long rval = 0, devmap_info_sz;
+	u16 num_devices = 0, i = 0;
+	unsigned long flags;
+	struct mpi3mr_tgt_dev *tgtdev;
+	struct mpi3mr_device_map_info *devmap_info = NULL;
+	struct mpi3mr_all_tgt_info __user *all_tgt_info =
+		(struct mpi3mr_all_tgt_info *)data_in_buf;
+	u32 min_entrylen, kern_entrylen = 0, usr_entrylen;
+
+	if (data_in_sz < sizeof(u32)) {
+		dbgprint(mrioc, "failure at %s:%d/%s()!\n",
+			 __FILE__, __LINE__, __func__);
+		return -EINVAL;
+	}
+
+	devmap_info_sz = sizeof(struct mpi3mr_device_map_info);
+
+	spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
+	list_for_each_entry(tgtdev, &mrioc->tgtdev_list, list)
+		num_devices++;
+	spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
+
+	usr_entrylen = (data_in_sz - sizeof(u32)) / devmap_info_sz;
+	usr_entrylen *= devmap_info_sz;
+
+	if (!num_devices || !usr_entrylen)
+		goto copy_usrbuf;
+
+	devmap_info = kcalloc(num_devices, devmap_info_sz, GFP_KERNEL);
+	if (!devmap_info)
+		return -ENOMEM;
+
+	kern_entrylen = num_devices * devmap_info_sz;
+	memset((u8 *)devmap_info, 0xFF, kern_entrylen);
+	spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
+	list_for_each_entry(tgtdev, &mrioc->tgtdev_list, list) {
+		if (i >= num_devices)
+			break;
+		devmap_info[i].handle = tgtdev->dev_handle;
+		devmap_info[i].perst_id = tgtdev->perst_id;
+		if (tgtdev->host_exposed && tgtdev->starget) {
+			devmap_info[i].target_id = tgtdev->starget->id;
+			devmap_info[i].bus_id = tgtdev->starget->channel;
+		}
+		i++;
+	}
+	num_devices = i;
+	spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
+
+copy_usrbuf:
+	if (copy_to_user(&all_tgt_info->num_devices, &num_devices,
+			 sizeof(num_devices)))
+		rval = -EFAULT;
+	else {
+		min_entrylen = min(usr_entrylen, kern_entrylen);
+		if (min_entrylen &&
+			copy_to_user(&all_tgt_info->dmi,
+				      devmap_info, min_entrylen))
+			rval = -EFAULT;
+	}
+
+	kfree(devmap_info);
+	return rval;
+}
+
+/**
+ * mpi3mr_enable_logdata - Handler for log data enable IOCTL
+ * @mrioc: Adapter instance reference
+ * @data_in_buf: User buffer to copy the max logdata entry count
+ * @data_in_sz: length of the user buffer.
+ *
+ * This function enables log data caching in the driver if not
+ * already enabled and return the maximum number of log data
+ * entries that can be cached in the driver.
+ *
+ * Return: 0 on success and proper error codes on failure
+ */
+static long mpi3mr_enable_logdata(struct mpi3mr_ioc *mrioc,
+		void __user *data_in_buf, uint32_t data_in_sz)
+{
+	struct mpi3mr_logdata_enable logdata_enable;
+	u16 entry_size;
+
+	entry_size = mrioc->facts.reply_sz -
+			(sizeof(struct mpi3_event_notification_reply) - 4);
+	entry_size += MPI3MR_IOCTL_LOGDATA_ENTRY_HEADER_SZ;
+	logdata_enable.max_entries = MPI3MR_IOCTL_LOGDATA_MAX_ENTRIES;
+
+	if (!mrioc->logdata_buf) {
+		mrioc->logdata_buf_idx = 0;
+		mrioc->logdata_entry_sz = entry_size;
+		mrioc->logdata_buf =
+			kcalloc(MPI3MR_IOCTL_LOGDATA_MAX_ENTRIES,
+					entry_size, GFP_KERNEL);
+		if (!mrioc->logdata_buf)
+			return -ENOMEM;
+	}
+
+	if (copy_to_user(data_in_buf, &logdata_enable, sizeof(logdata_enable)))
+		return -EFAULT;
+
+	return 0;
+}
+
+/**
+ * mpi3mr_get_logdata - Handler for get log data  IOCTL
+ * @mrioc: Adapter instance reference
+ * @data_in_buf: User buffer to copy the logdata entries
+ * @data_in_sz: length of the user buffer.
+ *
+ * This function copies the log data entries to the user buffer
+ * when log caching is enabled in the driver.
+ *
+ * Return: 0 on success and proper error codes on failure
+ */
+static long mpi3mr_get_logdata(struct mpi3mr_ioc *mrioc,
+		void __user *data_in_buf, uint32_t data_in_sz)
+{
+	u16 num_entries, sz, entry_sz;
+
+	entry_sz = mrioc->logdata_entry_sz;
+	if ((!mrioc->logdata_buf) || (data_in_sz < entry_sz))
+		return -EINVAL;
+
+	num_entries = data_in_sz / entry_sz;
+	num_entries = min_t(int, num_entries,
+				MPI3MR_IOCTL_LOGDATA_MAX_ENTRIES);
+	sz = num_entries * entry_sz;
+
+	if (copy_to_user(data_in_buf, mrioc->logdata_buf, sz))
+		return -EFAULT;
+
+	return 0;
+}
+
+/**
+ * mpi3mr_get_change_count - Get topology change count
+ * @mrioc: Adapter instance reference
+ * @data_in_buf: User buffer to copy the change count
+ * @data_in_sz: length of the user buffer.
+ *
+ * This function copies the topology change count provided by the
+ * driver in events and cached in the driver to the user
+ * provided buffer for the specific controller.
+ *
+ * Return: 0 on success and proper error codes on failure
+ */
+static long mpi3mr_get_change_count(struct mpi3mr_ioc *mrioc,
+		void __user *data_in_buf, uint32_t data_in_sz)
+{
+	struct mpi3mr_change_count chgcnt;
+
+	chgcnt.change_count = mrioc->change_count;
+	if (copy_to_user(data_in_buf, &chgcnt, sizeof(chgcnt)))
+		return -EFAULT;
+
+	return 0;
+}
+
+/**
+ * mpi3mr_ioctl_adp_reset - Issue controller reset
+ * @mrioc: Adapter instance reference
+ * @data_out_buf: User buffer with reset type
+ * @data_out_sz: length of the user buffer.
+ *
+ * This function identifies the user provided reset type and
+ * issues approporiate reset to the controller and.wait for that
+ * to complete and reinitialize the controller and then returns
+ *
+ * Return: 0 on success and proper error codes on failure
+ */
+static long mpi3mr_ioctl_adp_reset(struct mpi3mr_ioc *mrioc,
+		void __user *data_out_buf, uint32_t data_out_sz)
+{
+	long rval = 0;
+	struct mpi3mr_ioctl_adp_reset adpreset;
+	u8 save_snapdump;
+
+	if (copy_from_user(&adpreset, data_out_buf, sizeof(adpreset)))
+		return -EFAULT;
+
+	switch (adpreset.reset_type) {
+	case MPI3MR_IOCTL_ADPRESET_SOFT:
+		save_snapdump = 0;
+		break;
+	case MPI3MR_IOCTL_ADPRESET_DIAG_FAULT:
+		save_snapdump = 1;
+		break;
+	default:
+		dbgprint(mrioc, "%s: unknown reset_type(%d)\n",
+		    __func__, adpreset.reset_type);
+		return -EINVAL;
+	}
+
+	rval = mpi3mr_soft_reset_handler(mrioc, MPI3MR_RESET_FROM_IOCTL,
+	    save_snapdump);
+
+	if (rval)
+		dbgprint(mrioc,
+		    "%s: reset handler returned error(%ld) for reset type %d\n",
+		    __func__, rval, adpreset.reset_type);
+
+	return rval;
+}
+
+/**
+ * mpi3mr_populate_adpinfo - Get adapter info IOCTL handler
+ * @mrioc: Adapter instance reference
+ * @data_in_buf: User buffer to hold adapter information
+ * @data_in_sz: length of the user buffer.
+ *
+ * This function provides adapter information for the given
+ * controller
+ *
+ * Return: 0 on success and proper error codes on failure
+ */
+static long mpi3mr_populate_adpinfo(struct mpi3mr_ioc *mrioc,
+		void __user *data_in_buf, uint32_t data_in_sz)
+{
+	struct mpi3mr_adp_info adpinfo;
+
+	memset(&adpinfo, 0, sizeof(adpinfo));
+	adpinfo.adp_type = MPI3MR_IOCTL_ADPTYPE_AVGFAMILY;
+	adpinfo.pci_dev_id = mrioc->pdev->device;
+	adpinfo.pci_dev_hw_rev = mrioc->pdev->revision;
+	adpinfo.pci_subsys_dev_id = mrioc->pdev->subsystem_device;
+	adpinfo.pci_subsys_ven_id = mrioc->pdev->subsystem_vendor;
+	adpinfo.pci_bus = mrioc->pdev->bus->number;
+	adpinfo.pci_dev = PCI_SLOT(mrioc->pdev->devfn);
+	adpinfo.pci_func = PCI_FUNC(mrioc->pdev->devfn);
+	adpinfo.pci_seg_id = pci_domain_nr(mrioc->pdev->bus);
+	adpinfo.ioctl_ver = MPI3MR_IOCTL_VERSION;
+	memcpy((u8 *)&adpinfo.driver_info, (u8 *)&mrioc->driver_info,
+	       sizeof(adpinfo.driver_info));
+
+	if (copy_to_user(data_in_buf, &adpinfo, sizeof(adpinfo)))
+		return -EFAULT;
+
+	return 0;
+}
+
+/**
+ * mpi3mr_ioctl_process_drv_cmds - Driver IOCTL handler
+ * @mrioc: Adapter instance reference
+ * @arg: User data payload buffer for the IOCTL
+ *
+ * This function is the top level handler for driver commands,
+ * this does basic validation of the buffer and identifies the
+ * opcode and switches to correct sub handler.
+ *
+ * Return: 0 on success and proper error codes on failure
+ */
+static long
+mpi3mr_ioctl_process_drv_cmds(struct file *file, void __user *arg)
+{
+	long rval = 0;
+	struct mpi3mr_ioc *mrioc = NULL;
+	struct mpi3mr_ioctl_drv_cmd karg;
+
+	if (copy_from_user(&karg, arg, sizeof(karg)))
+		return -EFAULT;
+
+	mpi3mr_verify_adapter(karg.mrioc_id, &mrioc);
+	if (!mrioc)
+		return -ENODEV;
+
+	if (file->f_flags & O_NONBLOCK) {
+		if (!mutex_trylock(&mrioc->ioctl_cmds.mutex))
+			return -EAGAIN;
+	} else if (mutex_lock_interruptible(&mrioc->ioctl_cmds.mutex))
+		return -ERESTARTSYS;
+
+	switch (karg.opcode) {
+	case MPI3MR_DRVIOCTL_OPCODE_ADPINFO:
+		rval = mpi3mr_populate_adpinfo(mrioc, karg.data_in_buf,
+					karg.data_in_size);
+		break;
+	case MPI3MR_DRVIOCTL_OPCODE_ADPRESET:
+		rval = mpi3mr_ioctl_adp_reset(mrioc, karg.data_out_buf,
+					karg.data_out_size);
+		break;
+	case MPI3MR_DRVIOCTL_OPCODE_ALLTGTDEVINFO:
+		rval = mpi3mr_get_all_tgt_info(mrioc, karg.data_in_buf,
+					karg.data_in_size);
+		break;
+	case MPI3MR_DRVIOCTL_OPCODE_LOGDATAENABLE:
+		rval = mpi3mr_enable_logdata(mrioc, karg.data_in_buf,
+					karg.data_in_size);
+		break;
+	case MPI3MR_DRVIOCTL_OPCODE_GETLOGDATA:
+		rval = mpi3mr_get_logdata(mrioc, karg.data_in_buf,
+					karg.data_in_size);
+		break;
+	case MPI3MR_DRVIOCTL_OPCODE_GETCHGCNT:
+		rval = mpi3mr_get_change_count(mrioc, karg.data_in_buf,
+					karg.data_in_size);
+		break;
+	case MPI3MR_DRVIOCTL_OPCODE_UNKNOWN:
+	default:
+		rval = -EINVAL;
+		dbgprint(mrioc, "Unsupported drv ioctl opcode 0x%x\n",
+			 karg.opcode);
+		break;
+	}
+	mutex_unlock(&mrioc->ioctl_cmds.mutex);
+	return rval;
+}
+
+/**
+ * mpi3mr_ioctl_build_sgl - SGL consturction for MPI IOCTLs
+ * @mpi_req: MPI request
+ * @sgl_offset: offset to start SGL in the MPI request
+ * @dma_buffers: DMA address of the buffers to be placed in SGL
+ * @bufcnt: Number of DMA buffers
+ * @is_rmc: Does the buffer list has management command buffer
+ * @is_rmr: Does the buffer list has management response buffer
+ * @num_data_sges: Number of data buffers in the list
+ *
+ * This function places the DMA address of the given buffers in
+ * proper format as SGEs in the given MPI request.
+ *
+ * Return: Nothing
+ */
+static void mpi3mr_ioctl_build_sgl(u8 *mpi_req, uint32_t sgl_offset,
+		struct mpi3mr_buf_map *dma_buffers,
+		u8 bufcnt, bool is_rmc, bool is_rmr, u8 num_data_sges)
+{
+	u8 *sgl;
+	u8 sgl_flags, sgl_flags_last, count = 0;
+	struct mpi3_mgmt_passthrough_request *mgmt_pt_req;
+	struct mpi3mr_buf_map *dma_buff;
+
+	sgl = (mpi_req + sgl_offset);
+	mgmt_pt_req = (struct mpi3_mgmt_passthrough_request *)mpi_req;
+	dma_buff = dma_buffers;
+
+	sgl_flags = MPI3_SGE_FLAGS_ELEMENT_TYPE_SIMPLE |
+			MPI3_SGE_FLAGS_DLAS_SYSTEM |
+			MPI3_SGE_FLAGS_END_OF_BUFFER;
+
+	sgl_flags_last = sgl_flags | MPI3_SGE_FLAGS_END_OF_LIST;
+
+	if (is_rmc) {
+		mpi3mr_add_sg_single(&mgmt_pt_req->command_sgl,
+				     sgl_flags_last, dma_buff->kern_buf_len,
+				     dma_buff->kern_buf_dma);
+		sgl = (u8 *)dma_buff->kern_buf + dma_buff->user_buf_len;
+		dma_buff++;
+		count++;
+		if (is_rmr) {
+			mpi3mr_add_sg_single(&mgmt_pt_req->response_sgl,
+					sgl_flags_last,
+					dma_buff->kern_buf_len,
+					dma_buff->kern_buf_dma);
+			dma_buff++;
+			count++;
+		} else
+			mpi3mr_build_zero_len_sge(&mgmt_pt_req->response_sgl);
+	}
+	if (!num_data_sges) {
+		mpi3mr_build_zero_len_sge(sgl);
+		return;
+	}
+	for (; count < bufcnt; count++, dma_buff++) {
+		if (dma_buff->data_dir == DMA_BIDIRECTIONAL)
+			continue;
+		if (num_data_sges == 1 || !is_rmc)
+			mpi3mr_add_sg_single(sgl, sgl_flags_last,
+					     dma_buff->kern_buf_len,
+					     dma_buff->kern_buf_dma);
+		else
+			mpi3mr_add_sg_single(sgl, sgl_flags,
+					     dma_buff->kern_buf_len,
+					     dma_buff->kern_buf_dma);
+		sgl += sizeof(struct mpi3_sge_common);
+		num_data_sges--;
+	}
+}
+
+/**
+ * mpi3mr_ioctl_process_mpt_cmds - MPI Pass through IOCTL handler
+ * @mrioc: Adapter instance reference
+ * @arg: User data payload buffer for the IOCTL
+ *
+ * This function is the top level handler for MPI Pass through
+ * IOCTL, this does basic validation of the input data buffers,
+ * identifies the given buffer types and MPI command, allocates
+ * DMAable memory for user given buffers, constructs SGL
+ * properly and passes the command to the firmware.
+ *
+ * Once the MPI command is completed the driver copies the data
+ * if any and reply, sense information to user provided buffers.
+ * If the command is timed out then issues controller reset
+ * prior to returning.
+ *
+ * Return: 0 on success and proper error codes on failure
+ */
+static long mpi3mr_ioctl_process_mpt_cmds(struct file *file,
+	void __user *arg)
+{
+	long rval = -EINVAL;
+	struct mpi3mr_ioc *mrioc = NULL;
+	struct mpi3mr_ioctl_mptcmd karg;
+	struct mpi3mr_ioctl_buf_entry_list *buffer_list = NULL;
+	struct mpi3mr_buf_entry *buf_entries = NULL;
+	struct mpi3mr_buf_map *dma_buffers = NULL, *dma_buff;
+	struct mpi3_request_header *mpi_header = NULL;
+	struct mpi3_status_reply_descriptor *status_desc;
+	struct mpi3mr_ioctl_reply_buf *ioctl_reply_buf = NULL;
+	u8 *mpi_req = NULL, *sense_buff_k = NULL;
+	u8 count, bufcnt, din_cnt = 0, dout_cnt = 0;
+	u8 erb_offset = 0xFF, reply_offset = 0xFF, sg_entries = 0;
+	bool invalid_be = false, is_rmcb = false, is_rmrb = false;
+	u32 tmplen;
+
+	if (copy_from_user(&karg, arg, sizeof(karg)))
+		return -EFAULT;
+
+	mpi3mr_verify_adapter(karg.mrioc_id, &mrioc);
+	if (!mrioc)
+		return -ENODEV;
+
+	if (karg.timeout < MPI3MR_IOCTL_DEFAULT_TIMEOUT)
+		karg.timeout = MPI3MR_IOCTL_DEFAULT_TIMEOUT;
+
+	if (!karg.buf_entry_list_size || !karg.mpi_msg_size)
+		return -EINVAL;
+
+	if ((karg.mpi_msg_size * 4) > MPI3MR_ADMIN_REQ_FRAME_SZ)
+		return -EINVAL;
+
+	mpi_req = kzalloc(MPI3MR_ADMIN_REQ_FRAME_SZ, GFP_KERNEL);
+	if (!mpi_req)
+		return -ENOMEM;
+
+	mpi_header = (struct mpi3_request_header *)mpi_req;
+
+	if (copy_from_user(mpi_req, karg.mpi_msg_buf,
+			   (karg.mpi_msg_size * 4)))
+		goto out;
+
+	buffer_list = kzalloc(karg.buf_entry_list_size, GFP_KERNEL);
+	if (!buffer_list) {
+		rval = -ENOMEM;
+		goto out;
+	}
+
+	if (copy_from_user(buffer_list, karg.buf_entry_list,
+			   karg.buf_entry_list_size)) {
+		rval = -EFAULT;
+		goto out;
+	}
+
+	if (!buffer_list->num_of_entries) {
+		rval = -EINVAL;
+		goto out;
+	}
+
+	bufcnt = buffer_list->num_of_entries;
+	dma_buffers = kzalloc((sizeof(struct mpi3mr_buf_map) * bufcnt), GFP_KERNEL);
+	if (!dma_buffers) {
+		rval = -ENOMEM;
+		goto out;
+	}
+
+	buf_entries = buffer_list->buf_entry;
+	dma_buff = dma_buffers;
+	for (count = 0; count < bufcnt; count++, buf_entries++, dma_buff++) {
+		dma_buff->user_buf = buf_entries->buffer;
+		dma_buff->user_buf_len = buf_entries->buf_len;
+
+		switch (buf_entries->buf_type) {
+		case MPI3MR_IOCTL_BUFTYPE_RAIDMGMT_CMD:
+			is_rmcb = true;
+			if (count != 0)
+				invalid_be = true;
+			dma_buff->data_dir = DMA_FROM_DEVICE;
+			break;
+		case MPI3MR_IOCTL_BUFTYPE_RAIDMGMT_RESP:
+			is_rmrb = true;
+			if (count != 1 || !is_rmcb)
+				invalid_be = true;
+			dma_buff->data_dir = DMA_TO_DEVICE;
+			break;
+		case MPI3MR_IOCTL_BUFTYPE_DATA_IN:
+			din_cnt++;
+			if ((din_cnt > 1) && !is_rmcb)
+				invalid_be = true;
+			dma_buff->data_dir = DMA_TO_DEVICE;
+			break;
+		case MPI3MR_IOCTL_BUFTYPE_DATA_OUT:
+			dout_cnt++;
+			if ((dout_cnt > 1) && !is_rmcb)
+				invalid_be = true;
+			dma_buff->data_dir = DMA_FROM_DEVICE;
+			break;
+		case MPI3MR_IOCTL_BUFTYPE_MPI_REPLY:
+			reply_offset = count;
+			dma_buff->data_dir = DMA_BIDIRECTIONAL;
+			break;
+		case MPI3MR_IOCTL_BUFTYPE_ERR_RESPONSE:
+			erb_offset = count;
+			dma_buff->data_dir = DMA_BIDIRECTIONAL;
+			break;
+		default:
+			invalid_be = true;
+			break;
+		}
+		if (invalid_be)
+			break;
+	}
+	if (invalid_be) {
+		rval = -EINVAL;
+		goto out;
+	}
+
+	if (!is_rmcb && (dout_cnt || din_cnt)) {
+		sg_entries = dout_cnt + din_cnt;
+		if (((karg.mpi_msg_size * 4) + (sg_entries *
+		      sizeof(struct mpi3_sge_common))) > MPI3MR_ADMIN_REQ_FRAME_SZ) {
+			rval = -EINVAL;
+			goto out;
+		}
+	}
+
+	dma_buff = dma_buffers;
+	for (count = 0; count < bufcnt; count++, dma_buff++) {
+		dma_buff->kern_buf_len = dma_buff->user_buf_len;
+		if (is_rmcb && !count)
+			dma_buff->kern_buf_len += ((dout_cnt + din_cnt) *
+			    sizeof(struct mpi3_sge_common));
+		if ((count == reply_offset) || (count == erb_offset)) {
+			dma_buff->kern_buf_len = 0;
+			continue;
+		}
+		if (!dma_buff->kern_buf_len)
+			continue;
+
+		dma_buff->kern_buf = dma_alloc_coherent(&mrioc->pdev->dev,
+						dma_buff->kern_buf_len,
+						&dma_buff->kern_buf_dma,
+						GFP_KERNEL);
+		if (!dma_buff->kern_buf) {
+			rval = -ENOMEM;
+			goto out;
+		}
+		if (dma_buff->data_dir == DMA_FROM_DEVICE) {
+			tmplen = min(dma_buff->kern_buf_len,
+					dma_buff->user_buf_len);
+			if (copy_from_user(dma_buff->kern_buf,
+					dma_buff->user_buf, tmplen)) {
+				rval = -EFAULT;
+				goto out;
+			}
+		}
+	}
+	if (erb_offset != 0xFF) {
+		sense_buff_k = kzalloc(MPI3MR_SENSE_BUF_SZ, GFP_KERNEL);
+		if (!sense_buff_k) {
+			rval = -ENOMEM;
+			goto out;
+		}
+	}
+
+	if (mpi_header->function != MPI3_FUNCTION_NVME_ENCAPSULATED)
+		mpi3mr_ioctl_build_sgl(mpi_req, (karg.mpi_msg_size * 4),
+					dma_buffers, bufcnt, is_rmcb,
+					is_rmrb, (dout_cnt + din_cnt));
+
+	if (file->f_flags & O_NONBLOCK) {
+		if (!mutex_trylock(&mrioc->ioctl_cmds.mutex)) {
+			rval = -EAGAIN;
+			goto out;
+		}
+	} else if (mutex_lock_interruptible(&mrioc->ioctl_cmds.mutex)) {
+		rval = -ERESTARTSYS;
+		goto out;
+	}
+	if (mrioc->ioctl_cmds.state & MPI3MR_CMD_PENDING) {
+		rval = -EAGAIN;
+		dbgprint(mrioc, "%s command is in use\n", __func__);
+		mutex_unlock(&mrioc->ioctl_cmds.mutex);
+		goto out;
+	}
+	if (mrioc->reset_in_progress) {
+		dbgprint(mrioc, "%s reset in progress\n", __func__);
+		rval = -EAGAIN;
+		mutex_unlock(&mrioc->ioctl_cmds.mutex);
+		goto out;
+	}
+	if (mrioc->block_ioctls) {
+		dbgprint(mrioc, "%s IOCTLs are blocked\n", __func__);
+		rval = -EAGAIN;
+		mutex_unlock(&mrioc->ioctl_cmds.mutex);
+		goto out;
+	}
+
+	mrioc->ioctl_cmds.state = MPI3MR_CMD_PENDING;
+	mrioc->ioctl_cmds.is_waiting = 1;
+	mrioc->ioctl_cmds.callback = NULL;
+	mrioc->ioctl_cmds.is_sense = false;
+	mrioc->ioctl_cmds.sensebuf = sense_buff_k;
+	memset(mrioc->ioctl_cmds.reply, 0, mrioc->facts.reply_sz);
+	mpi_header->host_tag = cpu_to_le16(MPI3MR_HOSTTAG_IOCTLCMDS);
+	init_completion(&mrioc->ioctl_cmds.done);
+	rval = mpi3mr_admin_request_post(mrioc, mpi_req,
+					 MPI3MR_ADMIN_REQ_FRAME_SZ, 0);
+	if (rval) {
+		rval = -EAGAIN;
+		goto out_unlock;
+	}
+	wait_for_completion_timeout(&mrioc->ioctl_cmds.done,
+				    (karg.timeout * HZ));
+	if (!(mrioc->ioctl_cmds.state & MPI3MR_CMD_COMPLETE)) {
+		mrioc->ioctl_cmds.is_waiting = 0;
+		dbgprint(mrioc, "%s command timed out\n", __func__);
+		rval = -EFAULT;
+		mpi3mr_soft_reset_handler(mrioc,
+				MPI3MR_RESET_FROM_IOCTL_TIMEOUT, 1);
+		goto out_unlock;
+	}
+
+	if ((mrioc->ioctl_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK)
+	     != MPI3_IOCSTATUS_SUCCESS) {
+		dbgprint(mrioc,
+			"%s ioc_status(0x%04x)  Loginfo(0x%08x)\n", __func__,
+			(mrioc->ioctl_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK),
+			mrioc->ioctl_cmds.ioc_loginfo);
+	}
+
+	if ((reply_offset != 0xFF) && dma_buffers[reply_offset].user_buf_len) {
+		dma_buff = &dma_buffers[reply_offset];
+		dma_buff->kern_buf_len =
+			(sizeof(struct mpi3mr_ioctl_reply_buf) - 1
+			+ mrioc->facts.reply_sz);
+		ioctl_reply_buf = kzalloc(dma_buff->kern_buf_len, GFP_KERNEL);
+
+		if (!ioctl_reply_buf) {
+			rval = -ENOMEM;
+			goto out_unlock;
+		}
+		if (mrioc->ioctl_cmds.state & MPI3MR_CMD_REPLY_VALID) {
+			ioctl_reply_buf->mpi_reply_type =
+				MPI3MR_IOCTL_MPI_REPLY_BUFTYPE_ADDRESS;
+			memcpy(ioctl_reply_buf->ioctl_reply_buf,
+					mrioc->ioctl_cmds.reply,
+					mrioc->facts.reply_sz);
+		} else {
+			ioctl_reply_buf->mpi_reply_type =
+				MPI3MR_IOCTL_MPI_REPLY_BUFTYPE_STATUS;
+			status_desc = (struct mpi3_status_reply_descriptor *)
+				ioctl_reply_buf->ioctl_reply_buf;
+			status_desc->ioc_status = mrioc->ioctl_cmds.ioc_status;
+			status_desc->ioc_log_info = mrioc->ioctl_cmds.ioc_loginfo;
+		}
+		tmplen = min(dma_buff->kern_buf_len, dma_buff->user_buf_len);
+		if (copy_to_user(dma_buff->user_buf, ioctl_reply_buf, tmplen)) {
+			rval = -EFAULT;
+			goto out_unlock;
+		}
+	}
+
+	if (erb_offset != 0xFF && mrioc->ioctl_cmds.sensebuf &&
+	    mrioc->ioctl_cmds.is_sense) {
+		dma_buff = &dma_buffers[erb_offset];
+		tmplen = min_t(int, dma_buff->user_buf_len,
+				MPI3MR_SENSE_BUF_SZ);
+		if (copy_to_user(dma_buff->user_buf, sense_buff_k, tmplen)) {
+			rval = -EFAULT;
+			goto out_unlock;
+		}
+	}
+
+	dma_buff = dma_buffers;
+	for (count = 0; count < bufcnt; count++, dma_buff++) {
+		if (dma_buff->data_dir == DMA_TO_DEVICE) {
+			tmplen = min(dma_buff->kern_buf_len,
+					dma_buff->user_buf_len);
+			if (copy_to_user(dma_buff->user_buf,
+					dma_buff->kern_buf, tmplen))
+				rval = -EFAULT;
+		}
+	}
+
+out_unlock:
+	mrioc->ioctl_cmds.is_sense = false;
+	mrioc->ioctl_cmds.sensebuf = NULL;
+	mrioc->ioctl_cmds.state = MPI3MR_CMD_NOTUSED;
+	mutex_unlock(&mrioc->ioctl_cmds.mutex);
+out:
+	kfree(sense_buff_k);
+	kfree(buffer_list);
+	kfree(mpi_req);
+	if (dma_buffers) {
+		dma_buff = dma_buffers;
+		for (count = 0; count < bufcnt; count++, dma_buff++) {
+			if (dma_buff->kern_buf && dma_buff->kern_buf_dma)
+				dma_free_coherent(&mrioc->pdev->dev,
+						dma_buff->kern_buf_len,
+						dma_buff->kern_buf,
+						dma_buff->kern_buf_dma);
+		}
+		kfree(dma_buffers);
+	}
+	kfree(ioctl_reply_buf);
+	return rval;
+}
+
+/**
+ * mpi3mr_ioctl - Main IOCTL handler
+ * @file: File pointer
+ * @cmd: IOCTL command
+ * @arg: User data payload buffer for the IOCTL
+ *
+ * This is main IOCTL handler which checks the command type and
+ * executes proper sub handler specific for the command.
+ *
+ * Return: 0 on success and proper error codes on failure
+ */
+static long mpi3mr_ioctl(struct file *file, unsigned int cmd,
+			     unsigned long arg)
+{
+	long rval = -EINVAL;
+
+	switch (cmd) {
+	case MPI3MRDRVCMD:
+		if (_IOC_SIZE(cmd) == sizeof(struct mpi3mr_ioctl_drv_cmd))
+			rval = mpi3mr_ioctl_process_drv_cmds(file,
+					(void __user *)arg);
+		break;
+	case MPI3MRMPTCMD:
+		if (_IOC_SIZE(cmd) == sizeof(struct mpi3mr_ioctl_mptcmd))
+			rval = mpi3mr_ioctl_process_mpt_cmds(file,
+					(void __user *)arg);
+		break;
+	default:
+		pr_err("%s:Unsupported ioctl cmd (0x%08x)\n", __func__, cmd);
+		break;
+	}
+	return rval;
+}
+
+/**
+ * mpi3mr_app_fasync - fasync callback
+ * @fd: File descriptor
+ * @filep: File pointer
+ * @mode: Mode
+ *
+ * Return: fasync_helper() returned value
+ */
+static int mpi3mr_app_fasync(int fd, struct file *filep, int mode)
+{
+	return fasync_helper(fd, filep, mode, &mpi3mr_app_async_queue);
+}
+
+static const struct file_operations mpi3mr_app_fops = {
+	.owner = THIS_MODULE,
+	.unlocked_ioctl = mpi3mr_ioctl,
+	.fasync = mpi3mr_app_fasync,
+};
+
+static struct miscdevice mpi3mr_app_dev = {
+	.minor  = MPI3MR_MINOR,
+	.name   = MPI3MR_DEV_NAME,
+	.fops   = &mpi3mr_app_fops,
+};
+
+/**
+ * mpi3mr_app_init - Character driver interface initializer
+ *
+ */
+void mpi3mr_app_init(void)
+{
+	mpi3mr_app_async_queue = NULL;
+
+	if (misc_register(&mpi3mr_app_dev) < 0)
+		pr_err("%s can't register misc device [minor=%d]\n",
+		       MPI3MR_DRIVER_NAME, MPI3MR_MINOR);
+}
+
+/**
+ * mpi3mr_app_exit - Character driver interface cleanup function
+ *
+ */
+void mpi3mr_app_exit(void)
+{
+	misc_deregister(&mpi3mr_app_dev);
+}
+
+MODULE_ALIAS_MISCDEV(MPI3MR_MINOR);
+MODULE_ALIAS("devname:" MPI3MR_DEV_NAME);
diff --git a/drivers/scsi/mpi3mr/mpi3mr_app.h b/drivers/scsi/mpi3mr/mpi3mr_app.h
new file mode 100644
index 000000000000..ec714d210b9e
--- /dev/null
+++ b/drivers/scsi/mpi3mr/mpi3mr_app.h
@@ -0,0 +1,369 @@ 
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Driver for Broadcom MPI3 Storage Controllers
+ *
+ * Copyright (C) 2017-2021 Broadcom Inc.
+ *  (mailto: mpi3mr-linuxdrv.pdl@broadcom.com)
+ *
+ */
+
+#ifndef MPI3MR_APP_INTFC_H_INCLUDED
+#define MPI3MR_APP_INTFC_H_INCLUDED
+
+#ifdef __KERNEL__
+#include <linux/miscdevice.h>
+#endif
+
+/*Definitions for IOCTL commands*/
+#define MPI3MR_DEV_NAME			"mpi3mrctl"
+#define MPI3MR_MAGIC_NUMBER		'B'
+
+#define MPI3MR_IOCTL_VERSION		0x05
+
+#define MPI3MR_IOCTL_DEFAULT_TIMEOUT	(60) /*seconds*/
+
+#define MPI3MR_IOCTL_ADPTYPE_UNKNOWN		0
+#define MPI3MR_IOCTL_ADPTYPE_AVGFAMILY		1
+
+#define MPI3MR_IOCTL_ADPRESET_UNKNOWN		0
+#define MPI3MR_IOCTL_ADPRESET_SOFT		1
+#define MPI3MR_IOCTL_ADPRESET_DIAG_FAULT	2
+
+#define MPI3MR_IOCTL_LOGDATA_MAX_ENTRIES	400
+#define MPI3MR_IOCTL_LOGDATA_ENTRY_HEADER_SZ	4
+
+#define MPI3MR_DRVIOCTL_OPCODE_UNKNOWN			0
+#define MPI3MR_DRVIOCTL_OPCODE_ADPINFO			1
+#define MPI3MR_DRVIOCTL_OPCODE_ADPRESET			2
+#define MPI3MR_DRVIOCTL_OPCODE_ALLTGTDEVINFO		4
+#define MPI3MR_DRVIOCTL_OPCODE_GETCHGCNT		5
+#define MPI3MR_DRVIOCTL_OPCODE_LOGDATAENABLE		6
+#define MPI3MR_DRVIOCTL_OPCODE_PELENABLE		7
+#define MPI3MR_DRVIOCTL_OPCODE_GETLOGDATA		8
+#define MPI3MR_DRVIOCTL_OPCODE_QUERY_HDB		9
+#define MPI3MR_DRVIOCTL_OPCODE_REPOST_HDB		10
+#define MPI3MR_DRVIOCTL_OPCODE_UPLOAD_HDB		11
+#define MPI3MR_DRVIOCTL_OPCODE_REFRESH_HDB_TRIGGERS	12
+
+
+#define MPI3MR_IOCTL_BUFTYPE_UNKNOWN		0
+#define MPI3MR_IOCTL_BUFTYPE_RAIDMGMT_CMD	1
+#define MPI3MR_IOCTL_BUFTYPE_RAIDMGMT_RESP	2
+#define MPI3MR_IOCTL_BUFTYPE_DATA_IN		3
+#define MPI3MR_IOCTL_BUFTYPE_DATA_OUT		4
+#define MPI3MR_IOCTL_BUFTYPE_MPI_REPLY		5
+#define MPI3MR_IOCTL_BUFTYPE_ERR_RESPONSE	6
+
+#define MPI3MR_IOCTL_MPI_REPLY_BUFTYPE_UNKNOWN	0
+#define MPI3MR_IOCTL_MPI_REPLY_BUFTYPE_STATUS	1
+#define MPI3MR_IOCTL_MPI_REPLY_BUFTYPE_ADDRESS	2
+
+#define MPI3MR_HDB_BUFTYPE_UNKNOWN		0
+#define MPI3MR_HDB_BUFTYPE_TRACE		1
+#define MPI3MR_HDB_BUFTYPE_FIRMWARE		2
+#define MPI3MR_HDB_BUFTYPE_RESERVED		3
+
+#define MPI3MR_HDB_BUFSTATUS_UNKNOWN		0
+#define MPI3MR_HDB_BUFSTATUS_NOT_ALLOCATED	1
+#define MPI3MR_HDB_BUFSTATUS_POSTED_UNPAUSED	2
+#define MPI3MR_HDB_BUFSTATUS_POSTED_PAUSED	3
+#define MPI3MR_HDB_BUFSTATUS_RELEASED		4
+
+#define MPI3MR_HDB_TRIGGER_TYPE_UNKNOWN		0
+#define MPI3MR_HDB_TRIGGER_TYPE_DIAGFAULT	1
+#define MPI3MR_HDB_TRIGGER_TYPE_ELEMENT		2
+#define MPI3MR_HDB_TRIGGER_TYPE_MASTER		3
+
+/**
+ * struct mpi3mr_adp_info - Adapter information IOCTL
+ * data returned by the driver.
+ *
+ * @adp_type: Adapter type
+ * @rsvd1: Reserved
+ * @pci_dev_id: PCI device ID of the adapter
+ * @pci_dev_hw_rev: PCI revision of the adapter
+ * @pci_subsys_dev_id: PCI subsystem device ID of the adapter
+ * @pci_subsys_ven_id: PCI subsystem vendor ID of the adapter
+ * @pci_dev: PCI device
+ * @pci_func: PCI function
+ * @pci_bus: PCI bus
+ * @pci_seg_id: PCI segment ID
+ * @ioctl_ver: version of the IOCTL definition
+ * @rsvd2: Reserved
+ * @driver_info: Driver Information (Version/Name)
+ */
+struct mpi3mr_adp_info {
+	uint32_t adp_type;
+	uint32_t rsvd1;
+	uint32_t pci_dev_id;
+	uint32_t pci_dev_hw_rev;
+	uint32_t pci_subsys_dev_id;
+	uint32_t pci_subsys_ven_id;
+	uint32_t pci_dev:5;
+	uint32_t pci_func:3;
+	uint32_t pci_bus:24;
+	uint32_t pci_seg_id;
+	uint32_t ioctl_ver;
+	uint32_t rsvd2[3];
+	struct mpi3_driver_info_layout driver_info;
+};
+
+/**
+ * struct mpi3mr_buf_map -  local structure to
+ * track kernel and user buffers associated with an IOCTL
+ * structure.
+ *
+ * @user_buf: User buffer virtual address
+ * @kern_buf: Kernel buffer virtual address
+ * @kern_buf_dma: Kernel buffer DMA address
+ * @user_buf_len: User buffer length
+ * @kern_buf_len: Kernel buffer length
+ * @data_dir: Data direction.
+ */
+struct mpi3mr_buf_map {
+	void __user *user_buf;
+	void *kern_buf;
+	dma_addr_t kern_buf_dma;
+	u32 user_buf_len;
+	u32 kern_buf_len;
+	u8 data_dir;
+};
+
+/**
+ * struct mpi3mr_ioctl_adp_reset - Adapter reset IOCTL
+ * payload data to the driver.
+ *
+ * @reset_type: Reset type
+ * @rsvd1: Reserved
+ * @rsvd2: Reserved
+ */
+struct mpi3mr_ioctl_adp_reset {
+	uint8_t reset_type;
+	uint8_t rsvd1;
+	uint16_t rsvd2;
+};
+
+/**
+ * struct mpi3mr_change_count - Topology change count
+ * returned by the driver.
+ *
+ * @change_count: Topology change count
+ * @rsvd: Reserved
+ */
+struct mpi3mr_change_count {
+	uint16_t change_count;
+	uint16_t rsvd;
+};
+
+/**
+ * struct mpi3mr_device_map_info - Target device mapping
+ * information
+ *
+ * @handle: Firmware device handle
+ * @perst_id: Persistent ID assigned by the firmware
+ * @target_id: Target ID assigned by the driver
+ * @bus_id: Bus ID assigned by the driver
+ * @rsvd1: Reserved
+ * @rsvd2: Reserved
+ */
+struct mpi3mr_device_map_info {
+	uint16_t handle;
+	uint16_t perst_id;
+	uint32_t target_id;
+	uint8_t bus_id;
+	uint8_t rsvd1;
+	uint16_t rsvd2;
+};
+
+/**
+ * struct mpi3mr_all_tgt_info - Target device mapping
+ * information returned by the driver
+ *
+ * @num_devices: The number of devices in driver's inventory
+ * @rsvd1: Reserved
+ * @rsvd2: Reserved
+ * @dmi: Variable length array of mapping information of targets
+ */
+struct mpi3mr_all_tgt_info {
+	uint16_t num_devices; //The number of devices in driver's inventory
+	uint16_t rsvd1;
+	uint32_t rsvd2;
+	struct mpi3mr_device_map_info dmi[1]; //Variable length Array
+};
+
+/**
+ * struct mpi3mr_logdata_enable - Number of log data
+ * entries saved by the driver returned as payload data for
+ * enable logdata IOCTL by the driver.
+ *
+ * @max_entries: Number of log data entries cached by the driver
+ * @rsvd: Reserved
+ */
+struct mpi3mr_logdata_enable {
+	uint16_t max_entries;
+	uint16_t rsvd;
+};
+
+/**
+ * struct mpi3mr_ioctl_out_pel_enable - PEL enable IOCTL payload
+ * data to the driver.
+ *
+ * @pel_locale: PEL locale to the firmware
+ * @pel_class: PEL class to the firmware
+ * @rsvd: Reserved
+ */
+struct mpi3mr_ioctl_out_pel_enable {
+	uint16_t pel_locale;
+	uint8_t pel_class;
+	uint8_t rsvd;
+};
+
+/**
+ * struct mpi3mr_logdata_entry - Log data entry cached by the
+ * driver.
+ *
+ * @valid_entry: Is the entry valid
+ * @rsvd1: Reserved
+ * @rsvd2: Reserved
+ * @data: Log entry data of controller specific size
+ */
+struct mpi3mr_logdata_entry {
+	uint8_t valid_entry;
+	uint8_t rsvd1;
+	uint16_t rsvd2;
+	uint8_t data[1]; //Variable length Array
+};
+
+/**
+ * struct mpi3mr_ioctl_in_log_data - Log data entries saved by
+ * the driver returned as payload data for Get logdata IOCTL
+ * by the driver.
+ *
+ * @entry: Log data entry
+ */
+struct mpi3mr_ioctl_in_log_data {
+	struct mpi3mr_logdata_entry entry[1]; //Variable length Array
+};
+
+
+/**
+ * struct mpi3mr_ioctl_drv_cmd -  Generic IOCTL payload data
+ * structure for all driver specific IOCTLS .
+ *
+ * @mrioc_id: Controller ID
+ * @opcode: Driver IOCTL specific opcode
+ * @rsvd1: Reserved
+ * @rsvd2: Reserved
+ * @data_in_buf: User data buffer pointer of data from driver
+ * @data_out_buf: User data buffer pointer of data to driver
+ * @data_in_size: Data in buffer size
+ * @data_out_size: Data out buffer size
+ */
+struct mpi3mr_ioctl_drv_cmd {
+	uint8_t mrioc_id;
+	uint8_t opcode;
+	uint16_t rsvd1;
+	uint32_t rsvd2;
+#ifdef __KERNEL__
+	void __user *data_in_buf;
+	void __user *data_out_buf;
+#else
+	void *data_in_buf;
+	void *data_out_buf;
+#endif
+	uint32_t data_in_size;
+	uint32_t data_out_size;
+};
+
+/**
+ * struct mpi3mr_ioctl_reply_buf - MPI reply buffer returned
+ * for MPI Passthrough IOCTLs .
+ *
+ * @mpi_reply_type: Type of MPI reply
+ * @rsvd1: Reserved
+ * @rsvd2: Reserved
+ * @ioctl_reply_buf: Variable Length buffer based on mpirep type
+ */
+struct mpi3mr_ioctl_reply_buf {
+	uint8_t mpi_reply_type;
+	uint8_t rsvd1;
+	uint16_t rsvd2;
+	uint8_t ioctl_reply_buf[1]; /*Variable Length buffer based on mpirep type*/
+};
+
+
+/**
+ * struct mpi3mr_buf_entry - User buffer descriptor for MPI
+ * Passthrough IOCTLs.
+ *
+ * @buf_type: Buffer type
+ * @rsvd1: Reserved
+ * @rsvd2: Reserved
+ * @buf_len: Buffer length
+ * @buffer: User space buffer address
+ */
+struct mpi3mr_buf_entry {
+	uint8_t buf_type;
+	uint8_t rsvd1;
+	uint16_t rsvd2;
+	uint32_t buf_len;
+#ifdef __KERNEL__
+	void __user *buffer;
+#else
+	void *buffer;
+#endif
+};
+
+/**
+ * struct mpi3mr_ioctl_buf_entry_list - list of user buffer
+ * descriptor for MPI Passthrough IOCTLs.
+ *
+ * @num_of_entries: Number of buffer descriptors
+ * @rsvd1: Reserved
+ * @rsvd2: Reserved
+ * @rsvd3: Reserved
+ * @buf_entry: Variable length array of buffer descriptors
+ */
+struct mpi3mr_ioctl_buf_entry_list {
+	uint8_t num_of_entries;
+	uint8_t rsvd1;
+	uint16_t rsvd2;
+	uint32_t rsvd3;
+	struct mpi3mr_buf_entry buf_entry[1]; //Variable length Array
+};
+
+/**
+ * struct mpi3mr_ioctl_mptcmd -  Generic IOCTL payload data
+ * structure for all MPI Passthrough IOCTLS .
+ *
+ * @mrioc_id: Controller ID
+ * @rsvd1: Reserved
+ * @timeout: MPI command timeout
+ * @rsvd2: Reserved
+ * @mpi_msg_size: MPI message size
+ * @mpi_msg_buf: MPI message
+ * @buf_entry_list: Buffer descriptor list
+ * @buf_entry_list_size: Buffer descriptor list size
+ */
+struct mpi3mr_ioctl_mptcmd {
+	uint8_t mrioc_id;
+	uint8_t rsvd1;
+	uint16_t timeout;
+	uint16_t rsvd2;
+	uint16_t mpi_msg_size;
+#ifdef __KERNEL__
+	void __user *mpi_msg_buf;
+	void __user *buf_entry_list;
+#else
+	void *mpi_msg_buf;
+	void *buf_entry_list;
+#endif
+	uint32_t buf_entry_list_size;
+};
+
+#define MPI3MRDRVCMD	_IOWR(MPI3MR_MAGIC_NUMBER, 1, \
+	struct mpi3mr_ioctl_drv_cmd)
+#define MPI3MRMPTCMD	_IOWR(MPI3MR_MAGIC_NUMBER, 2, \
+	struct mpi3mr_ioctl_mptcmd)
+
+#endif
diff --git a/drivers/scsi/mpi3mr/mpi3mr_fw.c b/drivers/scsi/mpi3mr/mpi3mr_fw.c
index 878a4963e2ad..337cbb3ffaaa 100644
--- a/drivers/scsi/mpi3mr/mpi3mr_fw.c
+++ b/drivers/scsi/mpi3mr/mpi3mr_fw.c
@@ -287,6 +287,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_IOCTLCMDS:
+		return &mrioc->ioctl_cmds;
 	case MPI3MR_HOSTTAG_BLK_TMS:
 		return &mrioc->host_tm_cmds;
 	case MPI3MR_HOSTTAG_INVALID:
@@ -371,6 +373,11 @@  static void mpi3mr_process_admin_reply_desc(struct mpi3mr_ioc *mrioc,
 				memcpy((u8 *)cmdptr->reply, (u8 *)def_reply,
 				    mrioc->facts.reply_sz);
 			}
+			if (sense_buf && cmdptr->sensebuf) {
+				cmdptr->is_sense = true;
+				memcpy(cmdptr->sensebuf, sense_buf,
+				    MPI3MR_SENSE_BUF_SZ);
+			}
 			if (cmdptr->is_waiting) {
 				complete(&cmdptr->done);
 				cmdptr->is_waiting = 0;
@@ -2454,6 +2461,10 @@  static int mpi3mr_alloc_reply_sense_bufs(struct mpi3mr_ioc *mrioc)
 	if (!mrioc->init_cmds.reply)
 		goto out_failed;
 
+	mrioc->ioctl_cmds.reply = kzalloc(mrioc->facts.reply_sz, GFP_KERNEL);
+	if (!mrioc->ioctl_cmds.reply)
+		goto out_failed;
+
 	for (i = 0; i < MPI3MR_NUM_DEVRMCMD; i++) {
 		mrioc->dev_rmhs_cmds[i].reply = kzalloc(mrioc->facts.reply_sz,
 		    GFP_KERNEL);
@@ -3505,6 +3516,7 @@  void mpi3mr_memset_buffers(struct mpi3mr_ioc *mrioc)
 	memset(mrioc->admin_reply_base, 0, mrioc->admin_reply_q_sz);
 
 	memset(mrioc->init_cmds.reply, 0, sizeof(*mrioc->init_cmds.reply));
+	memset(mrioc->ioctl_cmds.reply, 0, sizeof(*mrioc->ioctl_cmds.reply));
 	memset(mrioc->host_tm_cmds.reply, 0,
 	    sizeof(*mrioc->host_tm_cmds.reply));
 	for (i = 0; i < MPI3MR_NUM_DEVRMCMD; i++)
@@ -3601,6 +3613,9 @@  static void mpi3mr_free_mem(struct mpi3mr_ioc *mrioc)
 	kfree(mrioc->init_cmds.reply);
 	mrioc->init_cmds.reply = NULL;
 
+	kfree(mrioc->ioctl_cmds.reply);
+	mrioc->ioctl_cmds.reply = NULL;
+
 	kfree(mrioc->host_tm_cmds.reply);
 	mrioc->host_tm_cmds.reply = NULL;
 
@@ -3644,6 +3659,9 @@  static void mpi3mr_free_mem(struct mpi3mr_ioc *mrioc)
 		    mrioc->admin_req_base, mrioc->admin_req_dma);
 		mrioc->admin_req_base = NULL;
 	}
+
+	kfree(mrioc->logdata_buf);
+	mrioc->logdata_buf = NULL;
 }
 
 /**
@@ -3788,6 +3806,10 @@  static void mpi3mr_flush_drv_cmds(struct mpi3mr_ioc *mrioc)
 
 	cmdptr = &mrioc->init_cmds;
 	mpi3mr_drv_cmd_comp_reset(mrioc, cmdptr);
+
+	cmdptr = &mrioc->ioctl_cmds;
+	mpi3mr_drv_cmd_comp_reset(mrioc, cmdptr);
+
 	cmdptr = &mrioc->host_tm_cmds;
 	mpi3mr_drv_cmd_comp_reset(mrioc, cmdptr);
 
@@ -3875,6 +3897,7 @@  int mpi3mr_soft_reset_handler(struct mpi3mr_ioc *mrioc,
 		return -1;
 	}
 	mrioc->reset_in_progress = 1;
+	mrioc->block_ioctls = 1;
 
 	if ((!snapdump) && (reset_reason != MPI3MR_RESET_FROM_FAULT_WATCH) &&
 	    (reset_reason != MPI3MR_RESET_FROM_CIACTIV_FAULT)) {
@@ -3945,6 +3968,7 @@  int mpi3mr_soft_reset_handler(struct mpi3mr_ioc *mrioc,
 			    &mrioc->watchdog_work,
 			    msecs_to_jiffies(MPI3MR_WATCHDOG_INTERVAL));
 		spin_unlock_irqrestore(&mrioc->watchdog_lock, flags);
+		mrioc->block_ioctls = 0;
 	} else {
 		mpi3mr_issue_reset(mrioc,
 		    MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT, reset_reason);
diff --git a/drivers/scsi/mpi3mr/mpi3mr_os.c b/drivers/scsi/mpi3mr/mpi3mr_os.c
index a17d2172bddf..cd53c4920207 100644
--- a/drivers/scsi/mpi3mr/mpi3mr_os.c
+++ b/drivers/scsi/mpi3mr/mpi3mr_os.c
@@ -537,7 +537,7 @@  static struct mpi3mr_tgt_dev  *__mpi3mr_get_tgtdev_by_handle(
  *
  * Return: Target device reference.
  */
-static struct mpi3mr_tgt_dev *mpi3mr_get_tgtdev_by_handle(
+struct mpi3mr_tgt_dev *mpi3mr_get_tgtdev_by_handle(
 	struct mpi3mr_ioc *mrioc, u16 handle)
 {
 	struct mpi3mr_tgt_dev *tgtdev;
@@ -585,7 +585,7 @@  static struct mpi3mr_tgt_dev  *__mpi3mr_get_tgtdev_by_perst_id(
  *
  * Return: Target device reference.
  */
-static struct mpi3mr_tgt_dev *mpi3mr_get_tgtdev_by_perst_id(
+struct mpi3mr_tgt_dev *mpi3mr_get_tgtdev_by_perst_id(
 	struct mpi3mr_ioc *mrioc, u16 persist_id)
 {
 	struct mpi3mr_tgt_dev *tgtdev;
@@ -3074,6 +3074,7 @@  static int mpi3mr_scan_finished(struct Scsi_Host *shost,
 	ioc_info(mrioc, "%s :port enable: SUCCESS\n", __func__);
 	mpi3mr_start_watchdog(mrioc);
 	mrioc->is_driver_loading = 0;
+	mrioc->block_ioctls = 0;
 
 	return 1;
 }
@@ -3717,6 +3718,7 @@  mpi3mr_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 
 	mutex_init(&mrioc->reset_mutex);
 	mpi3mr_init_drv_cmd(&mrioc->init_cmds, MPI3MR_HOSTTAG_INITCMDS);
+	mpi3mr_init_drv_cmd(&mrioc->ioctl_cmds, MPI3MR_HOSTTAG_IOCTLCMDS);
 	mpi3mr_init_drv_cmd(&mrioc->host_tm_cmds, MPI3MR_HOSTTAG_BLK_TMS);
 
 	for (i = 0; i < MPI3MR_NUM_DEVRMCMD; i++)
@@ -3730,6 +3732,7 @@  mpi3mr_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	mrioc->logging_level = logging_level;
 	mrioc->shost = shost;
 	mrioc->pdev = pdev;
+	mrioc->block_ioctls = 1;
 
 	/* init shost parameters */
 	shost->max_cmd_len = MPI3MR_MAX_CDB_LENGTH;
@@ -4006,6 +4009,7 @@  static int __init mpi3mr_init(void)
 	pr_info("Loading %s version %s\n", MPI3MR_DRIVER_NAME,
 	    MPI3MR_DRIVER_VERSION);
 
+	mpi3mr_app_init();
 	ret_val = pci_register_driver(&mpi3mr_pci_driver);
 
 	return ret_val;
@@ -4021,6 +4025,7 @@  static void __exit mpi3mr_exit(void)
 		pr_info("Unloading %s version %s\n", MPI3MR_DRIVER_NAME,
 		    MPI3MR_DRIVER_VERSION);
 
+	mpi3mr_app_exit();
 	pci_unregister_driver(&mpi3mr_pci_driver);
 }