All of lore.kernel.org
 help / color / mirror / Atom feed
From: Kashyap Desai <kashyap.desai@broadcom.com>
To: linux-scsi@vger.kernel.org
Cc: jejb@linux.ibm.com, martin.petersen@oracle.com,
	steve.hagan@broadcom.com, mpi3mr-linuxdrv.pdl@broadcom.com,
	Kashyap Desai <kashyap.desai@broadcom.com>,
	sathya.prakash@broadcom.com
Subject: [PATCH 3/7] mpi3mr: controller management application support
Date: Wed, 22 Sep 2021 00:15:56 +0530	[thread overview]
Message-ID: <20210921184600.64427-4-kashyap.desai@broadcom.com> (raw)
In-Reply-To: <20210921184600.64427-1-kashyap.desai@broadcom.com>

[-- Attachment #1: Type: text/plain, Size: 45166 bytes --]

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 --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);
 }
 
-- 
2.18.1


[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 4212 bytes --]

  parent reply	other threads:[~2021-09-21 18:46 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-09-21 18:45 [PATCH 0/7] adding application support Kashyap Desai
2021-09-21 18:45 ` [PATCH 1/7] mpi3mr: upgrade mpi30 Rev-V Kashyap Desai
2021-09-21 18:45 ` [PATCH 2/7] miscdevice: adding support for MPI3MR_MINOR(243) Kashyap Desai
2021-10-05  4:27   ` Martin K. Petersen
2021-10-26 11:16     ` Kashyap Desai
2021-10-27  3:38       ` Martin K. Petersen
2021-10-28 18:34         ` Kashyap Desai
2021-12-13 12:23           ` [mpi3mr] " Sumit Saxena
2021-12-17  3:57             ` Martin K. Petersen
2021-09-21 18:45 ` Kashyap Desai [this message]
2021-09-21 18:45 ` [PATCH 4/7] mpi3mr: misc changes and use __builtin_return_address for debug Kashyap Desai
2021-09-21 18:45 ` [PATCH 5/7] mpi3mr: PEL support added Kashyap Desai
2021-09-21 18:45 ` [PATCH 6/7] mpi3mr: nvme pass-through support Kashyap Desai
2021-09-21 18:46 ` [PATCH 7/7] mpi3mr: task management for pass through command Kashyap Desai

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210921184600.64427-4-kashyap.desai@broadcom.com \
    --to=kashyap.desai@broadcom.com \
    --cc=jejb@linux.ibm.com \
    --cc=linux-scsi@vger.kernel.org \
    --cc=martin.petersen@oracle.com \
    --cc=mpi3mr-linuxdrv.pdl@broadcom.com \
    --cc=sathya.prakash@broadcom.com \
    --cc=steve.hagan@broadcom.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.