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 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 +#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