diff options
Diffstat (limited to 'drivers/scsi/megaraid/megaraid_sas_base.c')
| -rw-r--r-- | drivers/scsi/megaraid/megaraid_sas_base.c | 2184 |
1 files changed, 1586 insertions, 598 deletions
diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index fcbff83c0097..abbbc4b36cd1 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Linux MegaRAID driver for SAS based RAID controllers * @@ -5,19 +6,6 @@ * Copyright (c) 2013-2016 Avago Technologies * Copyright (c) 2016-2018 Broadcom Inc. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * * Authors: Broadcom Inc. * Sreenivas Bagalkote * Sumant Patro @@ -41,19 +29,21 @@ #include <linux/uio.h> #include <linux/slab.h> #include <linux/uaccess.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <linux/fs.h> #include <linux/compat.h> #include <linux/blkdev.h> #include <linux/mutex.h> #include <linux/poll.h> #include <linux/vmalloc.h> +#include <linux/irq_poll.h> #include <scsi/scsi.h> #include <scsi/scsi_cmnd.h> #include <scsi/scsi_device.h> #include <scsi/scsi_host.h> #include <scsi/scsi_tcq.h> +#include <scsi/scsi_dbg.h> #include "megaraid_sas_fusion.h" #include "megaraid_sas.h" @@ -62,47 +52,80 @@ * Will be set in megasas_init_mfi if user does not provide */ static unsigned int max_sectors; -module_param_named(max_sectors, max_sectors, int, 0); +module_param_named(max_sectors, max_sectors, int, 0444); MODULE_PARM_DESC(max_sectors, "Maximum number of sectors per IO command"); static int msix_disable; -module_param(msix_disable, int, S_IRUGO); +module_param(msix_disable, int, 0444); MODULE_PARM_DESC(msix_disable, "Disable MSI-X interrupt handling. Default: 0"); static unsigned int msix_vectors; -module_param(msix_vectors, int, S_IRUGO); +module_param(msix_vectors, int, 0444); MODULE_PARM_DESC(msix_vectors, "MSI-X max vector count. Default: Set by FW"); static int allow_vf_ioctls; -module_param(allow_vf_ioctls, int, S_IRUGO); +module_param(allow_vf_ioctls, int, 0444); MODULE_PARM_DESC(allow_vf_ioctls, "Allow ioctls in SR-IOV VF mode. Default: 0"); static unsigned int throttlequeuedepth = MEGASAS_THROTTLE_QUEUE_DEPTH; -module_param(throttlequeuedepth, int, S_IRUGO); +module_param(throttlequeuedepth, int, 0444); MODULE_PARM_DESC(throttlequeuedepth, "Adapter queue depth when throttled due to I/O timeout. Default: 16"); unsigned int resetwaittime = MEGASAS_RESET_WAIT_TIME; -module_param(resetwaittime, int, S_IRUGO); +module_param(resetwaittime, int, 0444); MODULE_PARM_DESC(resetwaittime, "Wait time in (1-180s) after I/O timeout before resetting adapter. Default: 180s"); -int smp_affinity_enable = 1; -module_param(smp_affinity_enable, int, S_IRUGO); +static int smp_affinity_enable = 1; +module_param(smp_affinity_enable, int, 0444); MODULE_PARM_DESC(smp_affinity_enable, "SMP affinity feature enable/disable Default: enable(1)"); -int rdpq_enable = 1; -module_param(rdpq_enable, int, S_IRUGO); +static int rdpq_enable = 1; +module_param(rdpq_enable, int, 0444); MODULE_PARM_DESC(rdpq_enable, "Allocate reply queue in chunks for large queue depth enable/disable Default: enable(1)"); unsigned int dual_qdepth_disable; -module_param(dual_qdepth_disable, int, S_IRUGO); +module_param(dual_qdepth_disable, int, 0444); MODULE_PARM_DESC(dual_qdepth_disable, "Disable dual queue depth feature. Default: 0"); -unsigned int scmd_timeout = MEGASAS_DEFAULT_CMD_TIMEOUT; -module_param(scmd_timeout, int, S_IRUGO); +static unsigned int scmd_timeout = MEGASAS_DEFAULT_CMD_TIMEOUT; +module_param(scmd_timeout, int, 0444); MODULE_PARM_DESC(scmd_timeout, "scsi command timeout (10-90s), default 90s. See megasas_reset_timer."); +static int perf_mode = -1; +module_param(perf_mode, int, 0444); +MODULE_PARM_DESC(perf_mode, "Performance mode (only for Aero adapters), options:\n\t\t" + "0 - balanced: High iops and low latency queues are allocated &\n\t\t" + "interrupt coalescing is enabled only on high iops queues\n\t\t" + "1 - iops: High iops queues are not allocated &\n\t\t" + "interrupt coalescing is enabled on all queues\n\t\t" + "2 - latency: High iops queues are not allocated &\n\t\t" + "interrupt coalescing is disabled on all queues\n\t\t" + "default mode is 'balanced'" + ); + +static int event_log_level = MFI_EVT_CLASS_CRITICAL; +module_param(event_log_level, int, 0644); +MODULE_PARM_DESC(event_log_level, "Asynchronous event logging level- range is: -2(CLASS_DEBUG) to 4(CLASS_DEAD), Default: 2(CLASS_CRITICAL)"); + +static unsigned int enable_sdev_max_qd; +module_param(enable_sdev_max_qd, int, 0444); +MODULE_PARM_DESC(enable_sdev_max_qd, "Enable sdev max qd as can_queue. Default: 0"); + +static int poll_queues; +module_param(poll_queues, int, 0444); +MODULE_PARM_DESC(poll_queues, "Number of queues to be use for io_uring poll mode.\n\t\t" + "This parameter is effective only if host_tagset_enable=1 &\n\t\t" + "It is not applicable for MFI_SERIES. &\n\t\t" + "Driver will work in latency mode. &\n\t\t" + "High iops queues are not allocated &\n\t\t" + ); + +static int host_tagset_enable = 1; +module_param(host_tagset_enable, int, 0444); +MODULE_PARM_DESC(host_tagset_enable, "Shared host tagset enable/disable Default: enable(1)"); + MODULE_LICENSE("GPL"); MODULE_VERSION(MEGASAS_VERSION); MODULE_AUTHOR("megaraidlinux.pdl@broadcom.com"); @@ -117,11 +140,13 @@ static int megasas_register_aen(struct megasas_instance *instance, u32 seq_num, u32 class_locale_word); static void megasas_get_pd_info(struct megasas_instance *instance, struct scsi_device *sdev); +static void +megasas_set_ld_removed_by_fw(struct megasas_instance *instance); /* * PCI ID table for all supported controllers */ -static struct pci_device_id megasas_pci_table[] = { +static const struct pci_device_id megasas_pci_table[] = { {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS1064R)}, /* xscale IOP */ @@ -166,6 +191,10 @@ static struct pci_device_id megasas_pci_table[] = { {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_AERO_10E2)}, {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_AERO_10E5)}, {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_AERO_10E6)}, + {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_AERO_10E0)}, + {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_AERO_10E3)}, + {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_AERO_10E4)}, + {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_AERO_10E7)}, {} }; @@ -182,9 +211,13 @@ static u32 support_poll_for_event; u32 megasas_dbg_lvl; static u32 support_device_change; static bool support_nvme_encapsulation; +static bool support_pci_lane_margining; /* define lock for aen poll */ -spinlock_t poll_aen_lock; +static DEFINE_SPINLOCK(poll_aen_lock); + +extern struct dentry *megasas_debugfs_root; +extern int megasas_blk_mq_poll(struct Scsi_Host *shost, unsigned int queue_num); void megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, @@ -229,13 +262,13 @@ u32 megasas_readl(struct megasas_instance *instance, * Fusion registers could intermittently return all zeroes. * This behavior is transient in nature and subsequent reads will * return valid value. As a workaround in driver, retry readl for - * upto three times until a non-zero value is read. + * up to thirty times until a non-zero value is read. */ if (instance->adapter_type == AERO_SERIES) { do { ret_val = readl(addr); i++; - } while (ret_val == 0 && i < 3); + } while (ret_val == 0 && i < 30); return ret_val; } else { return readl(addr); @@ -267,7 +300,7 @@ void megasas_set_dma_settings(struct megasas_instance *instance, } } -void +static void megasas_issue_dcmd(struct megasas_instance *instance, struct megasas_cmd *cmd) { instance->instancet->fire_cmd(instance, @@ -391,23 +424,35 @@ megasas_decode_evt(struct megasas_instance *instance) union megasas_evt_class_locale class_locale; class_locale.word = le32_to_cpu(evt_detail->cl.word); - if (class_locale.members.class >= MFI_EVT_CLASS_CRITICAL) + if ((event_log_level < MFI_EVT_CLASS_DEBUG) || + (event_log_level > MFI_EVT_CLASS_DEAD)) { + printk(KERN_WARNING "megaraid_sas: provided event log level is out of range, setting it to default 2(CLASS_CRITICAL), permissible range is: -2 to 4\n"); + event_log_level = MFI_EVT_CLASS_CRITICAL; + } + + if (class_locale.members.class >= event_log_level) dev_info(&instance->pdev->dev, "%d (%s/0x%04x/%s) - %s\n", le32_to_cpu(evt_detail->seq_num), format_timestamp(le32_to_cpu(evt_detail->time_stamp)), (class_locale.members.locale), format_class(class_locale.members.class), evt_detail->description); + + if (megasas_dbg_lvl & LD_PD_DEBUG) + dev_info(&instance->pdev->dev, + "evt_detail.args.ld.target_id/index %d/%d\n", + evt_detail->args.ld.target_id, evt_detail->args.ld.ld_index); + } -/** -* The following functions are defined for xscale -* (deviceid : 1064R, PERC5) controllers -*/ +/* + * The following functions are defined for xscale + * (deviceid : 1064R, PERC5) controllers + */ /** * megasas_enable_intr_xscale - Enables interrupts - * @regs: MFI register set + * @instance: Adapter soft state */ static inline void megasas_enable_intr_xscale(struct megasas_instance *instance) @@ -423,7 +468,7 @@ megasas_enable_intr_xscale(struct megasas_instance *instance) /** * megasas_disable_intr_xscale -Disables interrupt - * @regs: MFI register set + * @instance: Adapter soft state */ static inline void megasas_disable_intr_xscale(struct megasas_instance *instance) @@ -439,7 +484,7 @@ megasas_disable_intr_xscale(struct megasas_instance *instance) /** * megasas_read_fw_status_reg_xscale - returns the current FW status value - * @regs: MFI register set + * @instance: Adapter soft state */ static u32 megasas_read_fw_status_reg_xscale(struct megasas_instance *instance) @@ -447,8 +492,8 @@ megasas_read_fw_status_reg_xscale(struct megasas_instance *instance) return readl(&instance->reg_set->outbound_msg_0); } /** - * megasas_clear_interrupt_xscale - Check & clear interrupt - * @regs: MFI register set + * megasas_clear_intr_xscale - Check & clear interrupt + * @instance: Adapter soft state */ static int megasas_clear_intr_xscale(struct megasas_instance *instance) @@ -482,9 +527,10 @@ megasas_clear_intr_xscale(struct megasas_instance *instance) /** * megasas_fire_cmd_xscale - Sends command to the FW - * @frame_phys_addr : Physical address of cmd - * @frame_count : Number of frames for the command - * @regs : MFI register set + * @instance: Adapter soft state + * @frame_phys_addr : Physical address of cmd + * @frame_count : Number of frames for the command + * @regs : MFI register set */ static inline void megasas_fire_cmd_xscale(struct megasas_instance *instance, @@ -502,7 +548,8 @@ megasas_fire_cmd_xscale(struct megasas_instance *instance, /** * megasas_adp_reset_xscale - For controller reset - * @regs: MFI register set + * @instance: Adapter soft state + * @regs: MFI register set */ static int megasas_adp_reset_xscale(struct megasas_instance *instance, @@ -543,7 +590,8 @@ megasas_adp_reset_xscale(struct megasas_instance *instance, /** * megasas_check_reset_xscale - For controller reset check - * @regs: MFI register set + * @instance: Adapter soft state + * @regs: MFI register set */ static int megasas_check_reset_xscale(struct megasas_instance *instance, @@ -572,19 +620,19 @@ static struct megasas_instance_template megasas_instance_template_xscale = { .issue_dcmd = megasas_issue_dcmd, }; -/** -* This is the end of set of functions & definitions specific -* to xscale (deviceid : 1064R, PERC5) controllers -*/ +/* + * This is the end of set of functions & definitions specific + * to xscale (deviceid : 1064R, PERC5) controllers + */ -/** -* The following functions are defined for ppc (deviceid : 0x60) -* controllers -*/ +/* + * The following functions are defined for ppc (deviceid : 0x60) + * controllers + */ /** * megasas_enable_intr_ppc - Enables interrupts - * @regs: MFI register set + * @instance: Adapter soft state */ static inline void megasas_enable_intr_ppc(struct megasas_instance *instance) @@ -602,7 +650,7 @@ megasas_enable_intr_ppc(struct megasas_instance *instance) /** * megasas_disable_intr_ppc - Disable interrupt - * @regs: MFI register set + * @instance: Adapter soft state */ static inline void megasas_disable_intr_ppc(struct megasas_instance *instance) @@ -618,7 +666,7 @@ megasas_disable_intr_ppc(struct megasas_instance *instance) /** * megasas_read_fw_status_reg_ppc - returns the current FW status value - * @regs: MFI register set + * @instance: Adapter soft state */ static u32 megasas_read_fw_status_reg_ppc(struct megasas_instance *instance) @@ -627,8 +675,8 @@ megasas_read_fw_status_reg_ppc(struct megasas_instance *instance) } /** - * megasas_clear_interrupt_ppc - Check & clear interrupt - * @regs: MFI register set + * megasas_clear_intr_ppc - Check & clear interrupt + * @instance: Adapter soft state */ static int megasas_clear_intr_ppc(struct megasas_instance *instance) @@ -661,9 +709,10 @@ megasas_clear_intr_ppc(struct megasas_instance *instance) /** * megasas_fire_cmd_ppc - Sends command to the FW - * @frame_phys_addr : Physical address of cmd - * @frame_count : Number of frames for the command - * @regs : MFI register set + * @instance: Adapter soft state + * @frame_phys_addr: Physical address of cmd + * @frame_count: Number of frames for the command + * @regs: MFI register set */ static inline void megasas_fire_cmd_ppc(struct megasas_instance *instance, @@ -681,7 +730,8 @@ megasas_fire_cmd_ppc(struct megasas_instance *instance, /** * megasas_check_reset_ppc - For controller reset check - * @regs: MFI register set + * @instance: Adapter soft state + * @regs: MFI register set */ static int megasas_check_reset_ppc(struct megasas_instance *instance, @@ -711,7 +761,7 @@ static struct megasas_instance_template megasas_instance_template_ppc = { /** * megasas_enable_intr_skinny - Enables interrupts - * @regs: MFI register set + * @instance: Adapter soft state */ static inline void megasas_enable_intr_skinny(struct megasas_instance *instance) @@ -729,7 +779,7 @@ megasas_enable_intr_skinny(struct megasas_instance *instance) /** * megasas_disable_intr_skinny - Disables interrupt - * @regs: MFI register set + * @instance: Adapter soft state */ static inline void megasas_disable_intr_skinny(struct megasas_instance *instance) @@ -745,7 +795,7 @@ megasas_disable_intr_skinny(struct megasas_instance *instance) /** * megasas_read_fw_status_reg_skinny - returns the current FW status value - * @regs: MFI register set + * @instance: Adapter soft state */ static u32 megasas_read_fw_status_reg_skinny(struct megasas_instance *instance) @@ -754,8 +804,8 @@ megasas_read_fw_status_reg_skinny(struct megasas_instance *instance) } /** - * megasas_clear_interrupt_skinny - Check & clear interrupt - * @regs: MFI register set + * megasas_clear_intr_skinny - Check & clear interrupt + * @instance: Adapter soft state */ static int megasas_clear_intr_skinny(struct megasas_instance *instance) @@ -798,9 +848,10 @@ megasas_clear_intr_skinny(struct megasas_instance *instance) /** * megasas_fire_cmd_skinny - Sends command to the FW - * @frame_phys_addr : Physical address of cmd - * @frame_count : Number of frames for the command - * @regs : MFI register set + * @instance: Adapter soft state + * @frame_phys_addr: Physical address of cmd + * @frame_count: Number of frames for the command + * @regs: MFI register set */ static inline void megasas_fire_cmd_skinny(struct megasas_instance *instance, @@ -815,13 +866,13 @@ megasas_fire_cmd_skinny(struct megasas_instance *instance, &(regs)->inbound_high_queue_port); writel((lower_32_bits(frame_phys_addr) | (frame_count<<1))|1, &(regs)->inbound_low_queue_port); - mmiowb(); spin_unlock_irqrestore(&instance->hba_lock, flags); } /** * megasas_check_reset_skinny - For controller reset check - * @regs: MFI register set + * @instance: Adapter soft state + * @regs: MFI register set */ static int megasas_check_reset_skinny(struct megasas_instance *instance, @@ -850,14 +901,14 @@ static struct megasas_instance_template megasas_instance_template_skinny = { }; -/** -* The following functions are defined for gen2 (deviceid : 0x78 0x79) -* controllers -*/ +/* + * The following functions are defined for gen2 (deviceid : 0x78 0x79) + * controllers + */ /** * megasas_enable_intr_gen2 - Enables interrupts - * @regs: MFI register set + * @instance: Adapter soft state */ static inline void megasas_enable_intr_gen2(struct megasas_instance *instance) @@ -876,7 +927,7 @@ megasas_enable_intr_gen2(struct megasas_instance *instance) /** * megasas_disable_intr_gen2 - Disables interrupt - * @regs: MFI register set + * @instance: Adapter soft state */ static inline void megasas_disable_intr_gen2(struct megasas_instance *instance) @@ -892,7 +943,7 @@ megasas_disable_intr_gen2(struct megasas_instance *instance) /** * megasas_read_fw_status_reg_gen2 - returns the current FW status value - * @regs: MFI register set + * @instance: Adapter soft state */ static u32 megasas_read_fw_status_reg_gen2(struct megasas_instance *instance) @@ -901,8 +952,8 @@ megasas_read_fw_status_reg_gen2(struct megasas_instance *instance) } /** - * megasas_clear_interrupt_gen2 - Check & clear interrupt - * @regs: MFI register set + * megasas_clear_intr_gen2 - Check & clear interrupt + * @instance: Adapter soft state */ static int megasas_clear_intr_gen2(struct megasas_instance *instance) @@ -935,11 +986,13 @@ megasas_clear_intr_gen2(struct megasas_instance *instance) return mfiStatus; } + /** * megasas_fire_cmd_gen2 - Sends command to the FW - * @frame_phys_addr : Physical address of cmd - * @frame_count : Number of frames for the command - * @regs : MFI register set + * @instance: Adapter soft state + * @frame_phys_addr: Physical address of cmd + * @frame_count: Number of frames for the command + * @regs: MFI register set */ static inline void megasas_fire_cmd_gen2(struct megasas_instance *instance, @@ -957,7 +1010,8 @@ megasas_fire_cmd_gen2(struct megasas_instance *instance, /** * megasas_adp_reset_gen2 - For controller reset - * @regs: MFI register set + * @instance: Adapter soft state + * @reg_set: MFI register set */ static int megasas_adp_reset_gen2(struct megasas_instance *instance, @@ -1017,7 +1071,8 @@ megasas_adp_reset_gen2(struct megasas_instance *instance, /** * megasas_check_reset_gen2 - For controller reset check - * @regs: MFI register set + * @instance: Adapter soft state + * @regs: MFI register set */ static int megasas_check_reset_gen2(struct megasas_instance *instance, @@ -1045,10 +1100,10 @@ static struct megasas_instance_template megasas_instance_template_gen2 = { .issue_dcmd = megasas_issue_dcmd, }; -/** -* This is the end of set of functions & definitions -* specific to gen2 (deviceid : 0x78, 0x79) controllers -*/ +/* + * This is the end of set of functions & definitions + * specific to gen2 (deviceid : 0x78, 0x79) controllers + */ /* * Template added for TB (Fusion) @@ -1073,7 +1128,7 @@ megasas_issue_polled(struct megasas_instance *instance, struct megasas_cmd *cmd) if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR) { dev_err(&instance->pdev->dev, "Failed from %s %d\n", __func__, __LINE__); - return DCMD_NOT_FIRED; + return DCMD_INIT; } instance->instancet->issue_dcmd(instance, cmd); @@ -1097,30 +1152,30 @@ megasas_issue_blocked_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, int timeout) { int ret = 0; - cmd->cmd_status_drv = MFI_STAT_INVALID_STATUS; + cmd->cmd_status_drv = DCMD_INIT; if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR) { dev_err(&instance->pdev->dev, "Failed from %s %d\n", __func__, __LINE__); - return DCMD_NOT_FIRED; + return DCMD_INIT; } instance->instancet->issue_dcmd(instance, cmd); if (timeout) { ret = wait_event_timeout(instance->int_cmd_wait_q, - cmd->cmd_status_drv != MFI_STAT_INVALID_STATUS, timeout * HZ); + cmd->cmd_status_drv != DCMD_INIT, timeout * HZ); if (!ret) { - dev_err(&instance->pdev->dev, "Failed from %s %d DCMD Timed out\n", - __func__, __LINE__); + dev_err(&instance->pdev->dev, + "DCMD(opcode: 0x%x) is timed out, func:%s\n", + cmd->frame->dcmd.opcode, __func__); return DCMD_TIMEOUT; } } else wait_event(instance->int_cmd_wait_q, - cmd->cmd_status_drv != MFI_STAT_INVALID_STATUS); + cmd->cmd_status_drv != DCMD_INIT); - return (cmd->cmd_status_drv == MFI_STAT_OK) ? - DCMD_SUCCESS : DCMD_FAILED; + return cmd->cmd_status_drv; } /** @@ -1141,6 +1196,7 @@ megasas_issue_blocked_abort_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd; struct megasas_abort_frame *abort_fr; int ret = 0; + u32 opcode; cmd = megasas_get_cmd(instance); @@ -1162,33 +1218,34 @@ megasas_issue_blocked_abort_cmd(struct megasas_instance *instance, cpu_to_le32(upper_32_bits(cmd_to_abort->frame_phys_addr)); cmd->sync_cmd = 1; - cmd->cmd_status_drv = MFI_STAT_INVALID_STATUS; + cmd->cmd_status_drv = DCMD_INIT; if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR) { dev_err(&instance->pdev->dev, "Failed from %s %d\n", __func__, __LINE__); - return DCMD_NOT_FIRED; + return DCMD_INIT; } instance->instancet->issue_dcmd(instance, cmd); if (timeout) { ret = wait_event_timeout(instance->abort_cmd_wait_q, - cmd->cmd_status_drv != MFI_STAT_INVALID_STATUS, timeout * HZ); + cmd->cmd_status_drv != DCMD_INIT, timeout * HZ); if (!ret) { - dev_err(&instance->pdev->dev, "Failed from %s %d Abort Timed out\n", - __func__, __LINE__); + opcode = cmd_to_abort->frame->dcmd.opcode; + dev_err(&instance->pdev->dev, + "Abort(to be aborted DCMD opcode: 0x%x) is timed out func:%s\n", + opcode, __func__); return DCMD_TIMEOUT; } } else wait_event(instance->abort_cmd_wait_q, - cmd->cmd_status_drv != MFI_STAT_INVALID_STATUS); + cmd->cmd_status_drv != DCMD_INIT); cmd->sync_cmd = 0; megasas_return_cmd(instance, cmd); - return (cmd->cmd_status_drv == MFI_STAT_OK) ? - DCMD_SUCCESS : DCMD_FAILED; + return cmd->cmd_status_drv; } /** @@ -1393,10 +1450,10 @@ megasas_build_dcdb(struct megasas_instance *instance, struct scsi_cmnd *scp, * pthru timeout to the os layer timeout value. */ if (scp->device->type == TYPE_TAPE) { - if ((scp->request->timeout / HZ) > 0xFFFF) + if (scsi_cmd_to_rq(scp)->timeout / HZ > 0xFFFF) pthru->timeout = cpu_to_le16(0xFFFF); else - pthru->timeout = cpu_to_le16(scp->request->timeout / HZ); + pthru->timeout = cpu_to_le16(scsi_cmd_to_rq(scp)->timeout / HZ); } /* @@ -1581,7 +1638,7 @@ megasas_build_ldio(struct megasas_instance *instance, struct scsi_cmnd *scp, /** * megasas_cmd_type - Checks if the cmd is for logical drive/sysPD * and whether it's RW or non RW - * @scmd: SCSI command + * @cmd: SCSI command * */ inline int megasas_cmd_type(struct scsi_cmnd *cmd) @@ -1702,7 +1759,7 @@ megasas_build_and_issue_cmd(struct megasas_instance *instance, goto out_return_cmd; cmd->scmd = scmd; - scmd->SCp.ptr = (char *)cmd; + megasas_priv(scmd)->cmd_priv = cmd; /* * Issue the command to the FW @@ -1721,21 +1778,22 @@ out_return_cmd: /** * megasas_queue_command - Queue entry point + * @shost: adapter SCSI host * @scmd: SCSI command to be queued - * @done: Callback entry point */ static int megasas_queue_command(struct Scsi_Host *shost, struct scsi_cmnd *scmd) { struct megasas_instance *instance; struct MR_PRIV_DEVICE *mr_device_priv_data; + u32 ld_tgt_id; instance = (struct megasas_instance *) scmd->device->host->hostdata; if (instance->unload == 1) { scmd->result = DID_NO_CONNECT << 16; - scmd->scsi_done(scmd); + scsi_done(scmd); return 0; } @@ -1750,22 +1808,26 @@ megasas_queue_command(struct Scsi_Host *shost, struct scsi_cmnd *scmd) return SCSI_MLQUEUE_HOST_BUSY; } else { scmd->result = DID_NO_CONNECT << 16; - scmd->scsi_done(scmd); + scsi_done(scmd); return 0; } } - if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR) { + mr_device_priv_data = scmd->device->hostdata; + if (!mr_device_priv_data || + (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR)) { scmd->result = DID_NO_CONNECT << 16; - scmd->scsi_done(scmd); + scsi_done(scmd); return 0; } - mr_device_priv_data = scmd->device->hostdata; - if (!mr_device_priv_data) { - scmd->result = DID_NO_CONNECT << 16; - scmd->scsi_done(scmd); - return 0; + if (MEGASAS_IS_LOGICAL(scmd->device)) { + ld_tgt_id = MEGASAS_TARGET_ID(scmd->device); + if (instance->ld_tgtid_status[ld_tgt_id] == LD_TARGET_ID_DELETED) { + scmd->result = DID_NO_CONNECT << 16; + scsi_done(scmd); + return 0; + } } if (atomic_read(&instance->adprecovery) != MEGASAS_HBA_OPERATIONAL) @@ -1794,7 +1856,7 @@ megasas_queue_command(struct Scsi_Host *shost, struct scsi_cmnd *scmd) return instance->instancet->build_and_issue_cmd(instance, scmd); out_done: - scmd->scsi_done(scmd); + scsi_done(scmd); return 0; } @@ -1825,7 +1887,7 @@ static struct megasas_instance *megasas_lookup_instance(u16 host_no) * Returns void */ void megasas_set_dynamic_target_properties(struct scsi_device *sdev, - bool is_target_prop) + struct queue_limits *lim, bool is_target_prop) { u16 pd_index = 0, ld; u32 device_id; @@ -1852,11 +1914,17 @@ void megasas_set_dynamic_target_properties(struct scsi_device *sdev, return; raid = MR_LdRaidGet(ld, local_map_ptr); - if (raid->capability.ldPiMode == MR_PROT_INFO_TYPE_CONTROLLER) - blk_queue_update_dma_alignment(sdev->request_queue, 0x7); + if (raid->capability.ldPiMode == MR_PROT_INFO_TYPE_CONTROLLER) { + if (lim) + lim->dma_alignment = 0x7; + } mr_device_priv_data->is_tm_capable = raid->capability.tmCapable; + + if (!raid->flags.isEPD) + sdev->no_write_same = 1; + } else if (instance->use_seqnum_jbod_fp) { pd_index = (sdev->channel * MEGASAS_MAX_DEV_PER_CHANNEL) + sdev->id; @@ -1900,7 +1968,8 @@ void megasas_set_dynamic_target_properties(struct scsi_device *sdev, * */ static inline void -megasas_set_nvme_device_properties(struct scsi_device *sdev, u32 max_io_size) +megasas_set_nvme_device_properties(struct scsi_device *sdev, + struct queue_limits *lim, u32 max_io_size) { struct megasas_instance *instance; u32 mr_nvme_pg_size; @@ -1909,32 +1978,23 @@ megasas_set_nvme_device_properties(struct scsi_device *sdev, u32 max_io_size) mr_nvme_pg_size = max_t(u32, instance->nvme_page_size, MR_DEFAULT_NVME_PAGE_SIZE); - blk_queue_max_hw_sectors(sdev->request_queue, (max_io_size / 512)); - - blk_queue_flag_set(QUEUE_FLAG_NOMERGES, sdev->request_queue); - blk_queue_virt_boundary(sdev->request_queue, mr_nvme_pg_size - 1); + lim->max_hw_sectors = max_io_size / 512; + lim->virt_boundary_mask = mr_nvme_pg_size - 1; } - /* - * megasas_set_static_target_properties - - * Device property set by driver are static and it is not required to be - * updated after OCR. - * - * set io timeout - * set device queue depth - * set nvme device properties. see - megasas_set_nvme_device_properties + * megasas_set_fw_assisted_qd - + * set device queue depth to can_queue + * set device queue depth to fw assisted qd * * @sdev: scsi device * @is_target_prop true, if fw provided target properties. */ -static void megasas_set_static_target_properties(struct scsi_device *sdev, +static void megasas_set_fw_assisted_qd(struct scsi_device *sdev, bool is_target_prop) { - u16 target_index = 0; u8 interface_type; u32 device_qd = MEGASAS_DEFAULT_CMD_PER_LUN; - u32 max_io_size_kb = MR_DEFAULT_NVME_MDTS_KB; u32 tgt_device_qd; struct megasas_instance *instance; struct MR_PRIV_DEVICE *mr_device_priv_data; @@ -1943,13 +2003,6 @@ static void megasas_set_static_target_properties(struct scsi_device *sdev, mr_device_priv_data = sdev->hostdata; interface_type = mr_device_priv_data->interface_type; - /* - * The RAID firmware may require extended timeouts. - */ - blk_queue_rq_timeout(sdev->request_queue, scmd_timeout * HZ); - - target_index = (sdev->channel * MEGASAS_MAX_DEV_PER_CHANNEL) + sdev->id; - switch (interface_type) { case SAS_PD: device_qd = MEGASAS_SAS_QD; @@ -1964,25 +2017,58 @@ static void megasas_set_static_target_properties(struct scsi_device *sdev, if (is_target_prop) { tgt_device_qd = le32_to_cpu(instance->tgt_prop->device_qdepth); - if (tgt_device_qd && - (tgt_device_qd <= instance->host->can_queue)) - device_qd = tgt_device_qd; - - /* max_io_size_kb will be set to non zero for - * nvme based vd and syspd. - */ - max_io_size_kb = le32_to_cpu(instance->tgt_prop->max_io_size_kb); + if (tgt_device_qd) + device_qd = min(instance->host->can_queue, + (int)tgt_device_qd); } - if (instance->nvme_page_size && max_io_size_kb) - megasas_set_nvme_device_properties(sdev, (max_io_size_kb << 10)); + if (instance->enable_sdev_max_qd && interface_type != UNKNOWN_DRIVE) + device_qd = instance->host->can_queue; scsi_change_queue_depth(sdev, device_qd); +} + +/* + * megasas_set_static_target_properties - + * Device property set by driver are static and it is not required to be + * updated after OCR. + * + * set io timeout + * set device queue depth + * set nvme device properties. see - megasas_set_nvme_device_properties + * + * @sdev: scsi device + * @is_target_prop true, if fw provided target properties. + */ +static void megasas_set_static_target_properties(struct scsi_device *sdev, + struct queue_limits *lim, bool is_target_prop) +{ + u32 max_io_size_kb = MR_DEFAULT_NVME_MDTS_KB; + struct megasas_instance *instance; + + instance = megasas_lookup_instance(sdev->host->host_no); + + /* + * The RAID firmware may require extended timeouts. + */ + blk_queue_rq_timeout(sdev->request_queue, scmd_timeout * HZ); + + /* max_io_size_kb will be set to non zero for + * nvme based vd and syspd. + */ + if (is_target_prop) + max_io_size_kb = le32_to_cpu(instance->tgt_prop->max_io_size_kb); + if (instance->nvme_page_size && max_io_size_kb) + megasas_set_nvme_device_properties(sdev, lim, + max_io_size_kb << 10); + + megasas_set_fw_assisted_qd(sdev, is_target_prop); } -static int megasas_slave_configure(struct scsi_device *sdev) +static int megasas_sdev_configure(struct scsi_device *sdev, + struct queue_limits *lim) { u16 pd_index = 0; struct megasas_instance *instance; @@ -2012,19 +2098,22 @@ static int megasas_slave_configure(struct scsi_device *sdev) ret_target_prop = megasas_get_target_prop(instance, sdev); is_target_prop = (ret_target_prop == DCMD_SUCCESS) ? true : false; - megasas_set_static_target_properties(sdev, is_target_prop); + megasas_set_static_target_properties(sdev, lim, is_target_prop); /* This sdev property may change post OCR */ - megasas_set_dynamic_target_properties(sdev, is_target_prop); + megasas_set_dynamic_target_properties(sdev, lim, is_target_prop); + + if (!MEGASAS_IS_LOGICAL(sdev)) + sdev->no_vpd_size = 1; mutex_unlock(&instance->reset_mutex); return 0; } -static int megasas_slave_alloc(struct scsi_device *sdev) +static int megasas_sdev_init(struct scsi_device *sdev) { - u16 pd_index = 0; + u16 pd_index = 0, ld_tgt_id; struct megasas_instance *instance ; struct MR_PRIV_DEVICE *mr_device_priv_data; @@ -2042,6 +2131,9 @@ static int megasas_slave_alloc(struct scsi_device *sdev) goto scan_target; } return -ENXIO; + } else if (!MEGASAS_IS_LUN_VALID(sdev)) { + sdev_printk(KERN_INFO, sdev, "%s: invalid LUN\n", __func__); + return -ENXIO; } scan_target: @@ -2049,6 +2141,14 @@ scan_target: GFP_KERNEL); if (!mr_device_priv_data) return -ENOMEM; + + if (MEGASAS_IS_LOGICAL(sdev)) { + ld_tgt_id = MEGASAS_TARGET_ID(sdev); + instance->ld_tgtid_status[ld_tgt_id] = LD_TARGET_ID_ACTIVE; + if (megasas_dbg_lvl & LD_PD_DEBUG) + sdev_printk(KERN_INFO, sdev, "LD target ID %d created.\n", ld_tgt_id); + } + sdev->hostdata = mr_device_priv_data; atomic_set(&mr_device_priv_data->r1_ldio_hint, @@ -2056,8 +2156,25 @@ scan_target: return 0; } -static void megasas_slave_destroy(struct scsi_device *sdev) +static void megasas_sdev_destroy(struct scsi_device *sdev) { + u16 ld_tgt_id; + struct megasas_instance *instance; + + instance = megasas_lookup_instance(sdev->host->host_no); + + if (MEGASAS_IS_LOGICAL(sdev)) { + if (!MEGASAS_IS_LUN_VALID(sdev)) { + sdev_printk(KERN_INFO, sdev, "%s: invalid LUN\n", __func__); + return; + } + ld_tgt_id = MEGASAS_TARGET_ID(sdev); + instance->ld_tgtid_status[ld_tgt_id] = LD_TARGET_ID_DELETED; + if (megasas_dbg_lvl & LD_PD_DEBUG) + sdev_printk(KERN_INFO, sdev, + "LD target ID %d removed from OS stack\n", ld_tgt_id); + } + kfree(sdev->hostdata); sdev->hostdata = NULL; } @@ -2103,6 +2220,12 @@ static void megasas_complete_outstanding_ioctls(struct megasas_instance *instanc void megaraid_sas_kill_hba(struct megasas_instance *instance) { + if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR) { + dev_warn(&instance->pdev->dev, + "Adapter already dead, skipping kill HBA\n"); + return; + } + /* Set critical error to block I/O & ioctls in case caller didn't */ atomic_set(&instance->adprecovery, MEGASAS_HW_CRITICAL_ERROR); /* Wait 1 second to ensure IO or ioctls in build have posted */ @@ -2224,7 +2347,7 @@ megasas_internal_reset_defer_cmds(struct megasas_instance *instance); static void process_fw_state_change_wq(struct work_struct *work); -void megasas_do_ocr(struct megasas_instance *instance) +static void megasas_do_ocr(struct megasas_instance *instance) { if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS1064R) || (instance->pdev->device == PCI_DEVICE_ID_DELL_PERC5) || @@ -2601,7 +2724,7 @@ out: static void megasas_sriov_heartbeat_handler(struct timer_list *t) { struct megasas_instance *instance = - from_timer(instance, t, sriov_heartbeat_timer); + timer_container_of(instance, t, sriov_heartbeat_timer); if (instance->hb_host_mem->HB.fwCounter != instance->hb_host_mem->HB.driverCounter) { @@ -2672,14 +2795,14 @@ static int megasas_wait_for_outstanding(struct megasas_instance *instance) reset_index, reset_cmd, reset_cmd->scmd->cmnd[0]); - reset_cmd->scmd->scsi_done(reset_cmd->scmd); + scsi_done(reset_cmd->scmd); megasas_return_cmd(instance, reset_cmd); } else if (reset_cmd->sync_cmd) { dev_notice(&instance->pdev->dev, "%p synch cmds" "reset queue\n", reset_cmd); - reset_cmd->cmd_status_drv = MFI_STAT_INVALID_STATUS; + reset_cmd->cmd_status_drv = DCMD_INIT; instance->instancet->fire_cmd(instance, reset_cmd->frame_phys_addr, 0, instance->reg_set); @@ -2725,7 +2848,7 @@ static int megasas_wait_for_outstanding(struct megasas_instance *instance) do { if ((fw_state == MFI_STATE_FAULT) || atomic_read(&instance->fw_outstanding)) { dev_info(&instance->pdev->dev, - "%s:%d waiting_for_outstanding: before issue OCR. FW state = 0x%x, oustanding 0x%x\n", + "%s:%d waiting_for_outstanding: before issue OCR. FW state = 0x%x, outstanding 0x%x\n", __func__, __LINE__, fw_state, atomic_read(&instance->fw_outstanding)); if (i == 3) goto kill_hba_and_failed; @@ -2809,15 +2932,14 @@ static int megasas_generic_reset(struct scsi_cmnd *scmd) * Sets the FW busy flag and reduces the host->can_queue if the * cmd has not been completed within the timeout period. */ -static enum -blk_eh_timer_return megasas_reset_timer(struct scsi_cmnd *scmd) +static enum scsi_timeout_action megasas_reset_timer(struct scsi_cmnd *scmd) { struct megasas_instance *instance; unsigned long flags; if (time_after(jiffies, scmd->jiffies_at_alloc + (scmd_timeout * 2) * HZ)) { - return BLK_EH_DONE; + return SCSI_EH_NOT_HANDLED; } instance = (struct megasas_instance *)scmd->device->host->hostdata; @@ -2831,29 +2953,113 @@ blk_eh_timer_return megasas_reset_timer(struct scsi_cmnd *scmd) spin_unlock_irqrestore(instance->host->host_lock, flags); } - return BLK_EH_RESET_TIMER; + return SCSI_EH_RESET_TIMER; } /** - * megasas_dump_frame - This function will dump MPT/MFI frame + * megasas_dump - This function will print hexdump of provided buffer. + * @buf: Buffer to be dumped + * @sz: Size in bytes + * @format: Different formats of dumping e.g. format=n will + * cause only 'n' 32 bit words to be dumped in a single + * line. */ -static inline void -megasas_dump_frame(void *mpi_request, int sz) +inline void +megasas_dump(void *buf, int sz, int format) { int i; - __le32 *mfp = (__le32 *)mpi_request; + __le32 *buf_loc = (__le32 *)buf; + + for (i = 0; i < (sz / sizeof(__le32)); i++) { + if ((i % format) == 0) { + if (i != 0) + printk(KERN_CONT "\n"); + printk(KERN_CONT "%08x: ", (i * 4)); + } + printk(KERN_CONT "%08x ", le32_to_cpu(buf_loc[i])); + } + printk(KERN_CONT "\n"); +} + +/** + * megasas_dump_reg_set - This function will print hexdump of register set + * @reg_set: Register set to be dumped + */ +inline void +megasas_dump_reg_set(void __iomem *reg_set) +{ + unsigned int i, sz = 256; + u32 __iomem *reg = (u32 __iomem *)reg_set; + + for (i = 0; i < (sz / sizeof(u32)); i++) + printk("%08x: %08x\n", (i * 4), readl(®[i])); +} + +/** + * megasas_dump_fusion_io - This function will print key details + * of SCSI IO + * @scmd: SCSI command pointer of SCSI IO + */ +void +megasas_dump_fusion_io(struct scsi_cmnd *scmd) +{ + struct megasas_cmd_fusion *cmd = megasas_priv(scmd)->cmd_priv; + union MEGASAS_REQUEST_DESCRIPTOR_UNION *req_desc; + struct megasas_instance *instance; + + instance = (struct megasas_instance *)scmd->device->host->hostdata; + + scmd_printk(KERN_INFO, scmd, + "scmd: (0x%p) retries: 0x%x allowed: 0x%x\n", + scmd, scmd->retries, scmd->allowed); + scsi_print_command(scmd); + + if (cmd) { + req_desc = (union MEGASAS_REQUEST_DESCRIPTOR_UNION *)cmd->request_desc; + scmd_printk(KERN_INFO, scmd, "Request descriptor details:\n"); + scmd_printk(KERN_INFO, scmd, + "RequestFlags:0x%x MSIxIndex:0x%x SMID:0x%x LMID:0x%x DevHandle:0x%x\n", + req_desc->SCSIIO.RequestFlags, + req_desc->SCSIIO.MSIxIndex, req_desc->SCSIIO.SMID, + req_desc->SCSIIO.LMID, req_desc->SCSIIO.DevHandle); + + printk(KERN_INFO "IO request frame:\n"); + megasas_dump(cmd->io_request, + MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE, 8); + printk(KERN_INFO "Chain frame:\n"); + megasas_dump(cmd->sg_frame, + instance->max_chain_frame_sz, 8); + } + +} + +/* + * megasas_dump_sys_regs - This function will dump system registers through + * sysfs. + * @reg_set: Pointer to System register set. + * @buf: Buffer to which output is to be written. + * @return: Number of bytes written to buffer. + */ +static inline ssize_t +megasas_dump_sys_regs(void __iomem *reg_set, char *buf) +{ + unsigned int i, sz = 256; + int bytes_wrote = 0; + char *loc = (char *)buf; + u32 __iomem *reg = (u32 __iomem *)reg_set; - printk(KERN_INFO "IO request frame:\n\t"); - for (i = 0; i < sz / sizeof(__le32); i++) { - if (i && ((i % 8) == 0)) - printk("\n\t"); - printk("%08x ", le32_to_cpu(mfp[i])); + for (i = 0; i < sz / sizeof(u32); i++) { + bytes_wrote += scnprintf(loc + bytes_wrote, + PAGE_SIZE - bytes_wrote, + "%08x: %08x\n", (i * 4), + readl(®[i])); } - printk("\n"); + return bytes_wrote; } /** * megasas_reset_bus_host - Bus & host reset handler entry point + * @scmd: Mid-layer SCSI command */ static int megasas_reset_bus_host(struct scsi_cmnd *scmd) { @@ -2863,24 +3069,20 @@ static int megasas_reset_bus_host(struct scsi_cmnd *scmd) instance = (struct megasas_instance *)scmd->device->host->hostdata; scmd_printk(KERN_INFO, scmd, - "Controller reset is requested due to IO timeout\n" - "SCSI command pointer: (%p)\t SCSI host state: %d\t" - " SCSI host busy: %d\t FW outstanding: %d\n", - scmd, scmd->device->host->shost_state, + "OCR is requested due to IO timeout!!\n"); + + scmd_printk(KERN_INFO, scmd, + "SCSI host state: %d SCSI host busy: %d FW outstanding: %d\n", + scmd->device->host->shost_state, scsi_host_busy(scmd->device->host), atomic_read(&instance->fw_outstanding)); - /* * First wait for all commands to complete */ if (instance->adapter_type == MFI_SERIES) { ret = megasas_generic_reset(scmd); } else { - struct megasas_cmd_fusion *cmd; - cmd = (struct megasas_cmd_fusion *)scmd->SCp.ptr; - if (cmd) - megasas_dump_frame(cmd->io_request, - MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE); + megasas_dump_fusion_io(scmd); ret = megasas_reset_fusion(scmd->device->host, SCSIIO_TIMEOUT_OCR); } @@ -2935,12 +3137,12 @@ static int megasas_reset_target(struct scsi_cmnd *scmd) /** * megasas_bios_param - Returns disk geometry for a disk * @sdev: device handle - * @bdev: block device + * @unused: gendisk * @capacity: drive capacity * @geom: geometry parameters */ static int -megasas_bios_param(struct scsi_device *sdev, struct block_device *bdev, +megasas_bios_param(struct scsi_device *sdev, struct gendisk *unused, sector_t capacity, int geom[]) { int heads; @@ -2976,6 +3178,43 @@ megasas_bios_param(struct scsi_device *sdev, struct block_device *bdev, return 0; } +static void megasas_map_queues(struct Scsi_Host *shost) +{ + struct megasas_instance *instance; + int qoff = 0, offset; + struct blk_mq_queue_map *map; + + instance = (struct megasas_instance *)shost->hostdata; + + if (shost->nr_hw_queues == 1) + return; + + offset = instance->low_latency_index_start; + + /* Setup Default hctx */ + map = &shost->tag_set.map[HCTX_TYPE_DEFAULT]; + map->nr_queues = instance->msix_vectors - offset; + map->queue_offset = 0; + blk_mq_map_hw_queues(map, &instance->pdev->dev, offset); + qoff += map->nr_queues; + offset += map->nr_queues; + + /* we never use READ queue, so can't cheat blk-mq */ + shost->tag_set.map[HCTX_TYPE_READ].nr_queues = 0; + + /* Setup Poll hctx */ + map = &shost->tag_set.map[HCTX_TYPE_POLL]; + map->nr_queues = instance->iopoll_q_count; + if (map->nr_queues) { + /* + * The poll queue(s) doesn't have an IRQ (and hence IRQ + * affinity), so use the regular blk-mq cpu mapping + */ + map->queue_offset = qoff; + blk_mq_map_queues(map); + } +} + static void megasas_aen_polling(struct work_struct *work); /** @@ -3030,71 +3269,69 @@ megasas_service_aen(struct megasas_instance *instance, struct megasas_cmd *cmd) } static ssize_t -megasas_fw_crash_buffer_store(struct device *cdev, +fw_crash_buffer_store(struct device *cdev, struct device_attribute *attr, const char *buf, size_t count) { struct Scsi_Host *shost = class_to_shost(cdev); struct megasas_instance *instance = (struct megasas_instance *) shost->hostdata; int val = 0; - unsigned long flags; if (kstrtoint(buf, 0, &val) != 0) return -EINVAL; - spin_lock_irqsave(&instance->crashdump_lock, flags); + mutex_lock(&instance->crashdump_lock); instance->fw_crash_buffer_offset = val; - spin_unlock_irqrestore(&instance->crashdump_lock, flags); + mutex_unlock(&instance->crashdump_lock); return strlen(buf); } static ssize_t -megasas_fw_crash_buffer_show(struct device *cdev, +fw_crash_buffer_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); struct megasas_instance *instance = (struct megasas_instance *) shost->hostdata; u32 size; - unsigned long buff_addr; unsigned long dmachunk = CRASH_DMA_BUF_SIZE; + unsigned long chunk_left_bytes; unsigned long src_addr; - unsigned long flags; u32 buff_offset; - spin_lock_irqsave(&instance->crashdump_lock, flags); + mutex_lock(&instance->crashdump_lock); buff_offset = instance->fw_crash_buffer_offset; - if (!instance->crash_dump_buf && + if (!instance->crash_dump_buf || !((instance->fw_crash_state == AVAILABLE) || (instance->fw_crash_state == COPYING))) { dev_err(&instance->pdev->dev, "Firmware crash dump is not available\n"); - spin_unlock_irqrestore(&instance->crashdump_lock, flags); + mutex_unlock(&instance->crashdump_lock); return -EINVAL; } - buff_addr = (unsigned long) buf; - if (buff_offset > (instance->fw_crash_buffer_size * dmachunk)) { dev_err(&instance->pdev->dev, "Firmware crash dump offset is out of range\n"); - spin_unlock_irqrestore(&instance->crashdump_lock, flags); + mutex_unlock(&instance->crashdump_lock); return 0; } size = (instance->fw_crash_buffer_size * dmachunk) - buff_offset; + chunk_left_bytes = dmachunk - (buff_offset % dmachunk); + size = (size > chunk_left_bytes) ? chunk_left_bytes : size; size = (size >= PAGE_SIZE) ? (PAGE_SIZE - 1) : size; src_addr = (unsigned long)instance->crash_buf[buff_offset / dmachunk] + (buff_offset % dmachunk); memcpy(buf, (void *)src_addr, size); - spin_unlock_irqrestore(&instance->crashdump_lock, flags); + mutex_unlock(&instance->crashdump_lock); return size; } static ssize_t -megasas_fw_crash_buffer_size_show(struct device *cdev, +fw_crash_buffer_size_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); @@ -3106,14 +3343,13 @@ megasas_fw_crash_buffer_size_show(struct device *cdev, } static ssize_t -megasas_fw_crash_state_store(struct device *cdev, +fw_crash_state_store(struct device *cdev, struct device_attribute *attr, const char *buf, size_t count) { struct Scsi_Host *shost = class_to_shost(cdev); struct megasas_instance *instance = (struct megasas_instance *) shost->hostdata; int val = 0; - unsigned long flags; if (kstrtoint(buf, 0, &val) != 0) return -EINVAL; @@ -3127,9 +3363,9 @@ megasas_fw_crash_state_store(struct device *cdev, instance->fw_crash_state = val; if ((val == COPIED) || (val == COPY_ERROR)) { - spin_lock_irqsave(&instance->crashdump_lock, flags); + mutex_lock(&instance->crashdump_lock); megasas_free_host_crash_buffer(instance); - spin_unlock_irqrestore(&instance->crashdump_lock, flags); + mutex_unlock(&instance->crashdump_lock); if (val == COPY_ERROR) dev_info(&instance->pdev->dev, "application failed to " "copy Firmware crash dump\n"); @@ -3141,7 +3377,7 @@ megasas_fw_crash_state_store(struct device *cdev, } static ssize_t -megasas_fw_crash_state_show(struct device *cdev, +fw_crash_state_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); @@ -3152,14 +3388,14 @@ megasas_fw_crash_state_show(struct device *cdev, } static ssize_t -megasas_page_size_show(struct device *cdev, +page_size_show(struct device *cdev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "%ld\n", (unsigned long)PAGE_SIZE - 1); } static ssize_t -megasas_ldio_outstanding_show(struct device *cdev, struct device_attribute *attr, +ldio_outstanding_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); @@ -3169,7 +3405,7 @@ megasas_ldio_outstanding_show(struct device *cdev, struct device_attribute *attr } static ssize_t -megasas_fw_cmds_outstanding_show(struct device *cdev, +fw_cmds_outstanding_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); @@ -3178,49 +3414,119 @@ megasas_fw_cmds_outstanding_show(struct device *cdev, return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&instance->fw_outstanding)); } -static DEVICE_ATTR(fw_crash_buffer, S_IRUGO | S_IWUSR, - megasas_fw_crash_buffer_show, megasas_fw_crash_buffer_store); -static DEVICE_ATTR(fw_crash_buffer_size, S_IRUGO, - megasas_fw_crash_buffer_size_show, NULL); -static DEVICE_ATTR(fw_crash_state, S_IRUGO | S_IWUSR, - megasas_fw_crash_state_show, megasas_fw_crash_state_store); -static DEVICE_ATTR(page_size, S_IRUGO, - megasas_page_size_show, NULL); -static DEVICE_ATTR(ldio_outstanding, S_IRUGO, - megasas_ldio_outstanding_show, NULL); -static DEVICE_ATTR(fw_cmds_outstanding, S_IRUGO, - megasas_fw_cmds_outstanding_show, NULL); - -struct device_attribute *megaraid_host_attrs[] = { - &dev_attr_fw_crash_buffer_size, - &dev_attr_fw_crash_buffer, - &dev_attr_fw_crash_state, - &dev_attr_page_size, - &dev_attr_ldio_outstanding, - &dev_attr_fw_cmds_outstanding, +static ssize_t +enable_sdev_max_qd_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct megasas_instance *instance = (struct megasas_instance *)shost->hostdata; + + return snprintf(buf, PAGE_SIZE, "%d\n", instance->enable_sdev_max_qd); +} + +static ssize_t +enable_sdev_max_qd_store(struct device *cdev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct megasas_instance *instance = (struct megasas_instance *)shost->hostdata; + u32 val = 0; + bool is_target_prop; + int ret_target_prop = DCMD_FAILED; + struct scsi_device *sdev; + + if (kstrtou32(buf, 0, &val) != 0) { + pr_err("megasas: could not set enable_sdev_max_qd\n"); + return -EINVAL; + } + + mutex_lock(&instance->reset_mutex); + if (val) + instance->enable_sdev_max_qd = true; + else + instance->enable_sdev_max_qd = false; + + shost_for_each_device(sdev, shost) { + ret_target_prop = megasas_get_target_prop(instance, sdev); + is_target_prop = (ret_target_prop == DCMD_SUCCESS) ? true : false; + megasas_set_fw_assisted_qd(sdev, is_target_prop); + } + mutex_unlock(&instance->reset_mutex); + + return strlen(buf); +} + +static ssize_t +dump_system_regs_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct megasas_instance *instance = + (struct megasas_instance *)shost->hostdata; + + return megasas_dump_sys_regs(instance->reg_set, buf); +} + +static ssize_t +raid_map_id_show(struct device *cdev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct megasas_instance *instance = + (struct megasas_instance *)shost->hostdata; + + return snprintf(buf, PAGE_SIZE, "%ld\n", + (unsigned long)instance->map_id); +} + +static DEVICE_ATTR_RW(fw_crash_buffer); +static DEVICE_ATTR_RO(fw_crash_buffer_size); +static DEVICE_ATTR_RW(fw_crash_state); +static DEVICE_ATTR_RO(page_size); +static DEVICE_ATTR_RO(ldio_outstanding); +static DEVICE_ATTR_RO(fw_cmds_outstanding); +static DEVICE_ATTR_RW(enable_sdev_max_qd); +static DEVICE_ATTR_RO(dump_system_regs); +static DEVICE_ATTR_RO(raid_map_id); + +static struct attribute *megaraid_host_attrs[] = { + &dev_attr_fw_crash_buffer_size.attr, + &dev_attr_fw_crash_buffer.attr, + &dev_attr_fw_crash_state.attr, + &dev_attr_page_size.attr, + &dev_attr_ldio_outstanding.attr, + &dev_attr_fw_cmds_outstanding.attr, + &dev_attr_enable_sdev_max_qd.attr, + &dev_attr_dump_system_regs.attr, + &dev_attr_raid_map_id.attr, NULL, }; +ATTRIBUTE_GROUPS(megaraid_host); + /* * Scsi host template for megaraid_sas driver */ -static struct scsi_host_template megasas_template = { +static const struct scsi_host_template megasas_template = { .module = THIS_MODULE, .name = "Avago SAS based MegaRAID driver", .proc_name = "megaraid_sas", - .slave_configure = megasas_slave_configure, - .slave_alloc = megasas_slave_alloc, - .slave_destroy = megasas_slave_destroy, + .sdev_configure = megasas_sdev_configure, + .sdev_init = megasas_sdev_init, + .sdev_destroy = megasas_sdev_destroy, .queuecommand = megasas_queue_command, .eh_target_reset_handler = megasas_reset_target, .eh_abort_handler = megasas_task_abort, .eh_host_reset_handler = megasas_reset_bus_host, .eh_timed_out = megasas_reset_timer, - .shost_attrs = megaraid_host_attrs, + .shost_groups = megaraid_host_groups, .bios_param = megasas_bios_param, + .map_queues = megasas_map_queues, + .mq_poll = megasas_blk_mq_poll, .change_queue_depth = scsi_change_queue_depth, - .no_write_same = 1, + .max_segment_size = 0xffffffff, + .cmd_size = sizeof(struct megasas_cmd_priv), }; /** @@ -3236,7 +3542,11 @@ static void megasas_complete_int_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd) { - cmd->cmd_status_drv = cmd->frame->io.cmd_status; + if (cmd->cmd_status_drv == DCMD_INIT) + cmd->cmd_status_drv = + (cmd->frame->io.cmd_status == MFI_STAT_OK) ? + DCMD_SUCCESS : DCMD_FAILED; + wake_up(&instance->int_cmd_wait_q); } @@ -3255,11 +3565,27 @@ megasas_complete_abort(struct megasas_instance *instance, { if (cmd->sync_cmd) { cmd->sync_cmd = 0; - cmd->cmd_status_drv = 0; + cmd->cmd_status_drv = DCMD_SUCCESS; wake_up(&instance->abort_cmd_wait_q); } } +static void +megasas_set_ld_removed_by_fw(struct megasas_instance *instance) +{ + uint i; + + for (i = 0; (i < MEGASAS_MAX_LD_IDS); i++) { + if (instance->ld_ids_prev[i] != 0xff && + instance->ld_ids_from_raidmap[i] == 0xff) { + if (megasas_dbg_lvl & LD_PD_DEBUG) + dev_info(&instance->pdev->dev, + "LD target ID %d removed from RAID map\n", i); + instance->ld_tgtid_status[i] = LD_TARGET_ID_DELETED; + } + } +} + /** * megasas_complete_cmd - Completes a command * @instance: Adapter soft state @@ -3284,7 +3610,7 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, cmd->retry_for_fw_reset = 0; if (cmd->scmd) - cmd->scmd->SCp.ptr = NULL; + megasas_priv(cmd->scmd)->cmd_priv = NULL; switch (hdr->cmd) { case MFI_CMD_INVALID: @@ -3310,7 +3636,7 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, megasas_complete_int_cmd(instance, cmd); break; } - /* fall through */ + fallthrough; case MFI_CMD_LD_READ: case MFI_CMD_LD_WRITE: @@ -3325,7 +3651,7 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, atomic_dec(&instance->fw_outstanding); scsi_dma_unmap(cmd->scmd); - cmd->scmd->scsi_done(cmd->scmd); + scsi_done(cmd->scmd); megasas_return_cmd(instance, cmd); break; @@ -3339,8 +3665,10 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, case MFI_STAT_SCSI_IO_FAILED: case MFI_STAT_LD_INIT_IN_PROGRESS: - cmd->scmd->result = - (DID_ERROR << 16) | hdr->scsi_status; + if (hdr->scsi_status == 0xf0) + cmd->scmd->result = (DID_ERROR << 16) | SAM_STAT_CHECK_CONDITION; + else + cmd->scmd->result = (DID_ERROR << 16) | hdr->scsi_status; break; case MFI_STAT_SCSI_DONE_WITH_ERROR: @@ -3352,8 +3680,6 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, SCSI_SENSE_BUFFERSIZE); memcpy(cmd->scmd->sense_buffer, cmd->sense, hdr->sense_len); - - cmd->scmd->result |= DRIVER_SENSE << 24; } break; @@ -3373,7 +3699,7 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, atomic_dec(&instance->fw_outstanding); scsi_dma_unmap(cmd->scmd); - cmd->scmd->scsi_done(cmd->scmd); + scsi_done(cmd->scmd); megasas_return_cmd(instance, cmd); break; @@ -3381,6 +3707,7 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, case MFI_CMD_SMP: case MFI_CMD_STP: case MFI_CMD_NVME: + case MFI_CMD_TOOLBOX: megasas_complete_int_cmd(instance, cmd); break; @@ -3421,9 +3748,13 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, fusion->fast_path_io = 0; } + if (instance->adapter_type >= INVADER_SERIES) + megasas_set_ld_removed_by_fw(instance); + megasas_sync_map_info(instance); spin_unlock_irqrestore(instance->host->host_lock, flags); + break; } if (opcode == MR_DCMD_CTRL_EVENT_GET_INFO || @@ -3530,7 +3861,7 @@ megasas_issue_pending_cmds_again(struct megasas_instance *instance) dev_notice(&instance->pdev->dev, "%p synchronous cmd" "on the internal reset queue," "issue it again.\n", cmd); - cmd->cmd_status_drv = MFI_STAT_INVALID_STATUS; + cmd->cmd_status_drv = DCMD_INIT; instance->instancet->fire_cmd(instance, cmd->frame_phys_addr, 0, instance->reg_set); @@ -3568,7 +3899,7 @@ megasas_issue_pending_cmds_again(struct megasas_instance *instance) megasas_register_aen(instance, seq_num, class_locale.word); } -/** +/* * Move the internal reset pending commands to a deferred queue. * * We move the commands pending at internal reset time to a @@ -3576,7 +3907,7 @@ megasas_issue_pending_cmds_again(struct megasas_instance *instance) * completion of the internal reset sequence. if the internal reset * did not complete in time, the kernel reset handler would flush * these commands. - **/ + */ static void megasas_internal_reset_defer_cmds(struct megasas_instance *instance) { @@ -3620,9 +3951,9 @@ process_fw_state_change_wq(struct work_struct *work) u32 wait; unsigned long flags; - if (atomic_read(&instance->adprecovery) != MEGASAS_ADPRESET_SM_INFAULT) { + if (atomic_read(&instance->adprecovery) != MEGASAS_ADPRESET_SM_INFAULT) { dev_notice(&instance->pdev->dev, "error, recovery st %x\n", - atomic_read(&instance->adprecovery)); + atomic_read(&instance->adprecovery)); return ; } @@ -3693,10 +4024,8 @@ megasas_deplete_reply_queue(struct megasas_instance *instance, u32 mfiStatus; u32 fw_state; - if ((mfiStatus = instance->instancet->check_reset(instance, - instance->reg_set)) == 1) { + if (instance->instancet->check_reset(instance, instance->reg_set) == 1) return IRQ_HANDLED; - } mfiStatus = instance->instancet->clear_intr(instance); if (mfiStatus == 0) { @@ -3754,8 +4083,11 @@ megasas_deplete_reply_queue(struct megasas_instance *instance, tasklet_schedule(&instance->isr_tasklet); return IRQ_HANDLED; } + /** * megasas_isr - isr entry point + * @irq: IRQ number + * @devp: IRQ context address */ static irqreturn_t megasas_isr(int irq, void *devp) { @@ -3777,6 +4109,7 @@ static irqreturn_t megasas_isr(int irq, void *devp) /** * megasas_transition_to_ready - Move the FW to READY state * @instance: Adapter soft state + * @ocr: Adapter reset state * * During the initialization, FW passes can potentially be in any one of * several possible states. If the FW in operational, waiting-for-handshake @@ -3789,7 +4122,6 @@ megasas_transition_to_ready(struct megasas_instance *instance, int ocr) int i; u8 max_wait; u32 fw_state; - u32 cur_state; u32 abs_state, curr_abs_state; abs_state = instance->instancet->read_fw_status_reg(instance); @@ -3804,13 +4136,18 @@ megasas_transition_to_ready(struct megasas_instance *instance, int ocr) switch (fw_state) { case MFI_STATE_FAULT: - dev_printk(KERN_DEBUG, &instance->pdev->dev, "FW in FAULT state!!\n"); + dev_printk(KERN_ERR, &instance->pdev->dev, + "FW in FAULT state, Fault code:0x%x subcode:0x%x func:%s\n", + abs_state & MFI_STATE_FAULT_CODE, + abs_state & MFI_STATE_FAULT_SUBCODE, __func__); if (ocr) { max_wait = MEGASAS_RESET_WAIT_TIME; - cur_state = MFI_STATE_FAULT; break; - } else + } else { + dev_printk(KERN_DEBUG, &instance->pdev->dev, "System Register set:\n"); + megasas_dump_reg_set(instance->reg_set); return -ENODEV; + } case MFI_STATE_WAIT_HANDSHAKE: /* @@ -3830,7 +4167,6 @@ megasas_transition_to_ready(struct megasas_instance *instance, int ocr) &instance->reg_set->inbound_doorbell); max_wait = MEGASAS_RESET_WAIT_TIME; - cur_state = MFI_STATE_WAIT_HANDSHAKE; break; case MFI_STATE_BOOT_MESSAGE_PENDING: @@ -3846,7 +4182,6 @@ megasas_transition_to_ready(struct megasas_instance *instance, int ocr) &instance->reg_set->inbound_doorbell); max_wait = MEGASAS_RESET_WAIT_TIME; - cur_state = MFI_STATE_BOOT_MESSAGE_PENDING; break; case MFI_STATE_OPERATIONAL: @@ -3879,7 +4214,6 @@ megasas_transition_to_ready(struct megasas_instance *instance, int ocr) &instance->reg_set->inbound_doorbell); max_wait = MEGASAS_RESET_WAIT_TIME; - cur_state = MFI_STATE_OPERATIONAL; break; case MFI_STATE_UNDEFINED: @@ -3887,49 +4221,45 @@ megasas_transition_to_ready(struct megasas_instance *instance, int ocr) * This state should not last for more than 2 seconds */ max_wait = MEGASAS_RESET_WAIT_TIME; - cur_state = MFI_STATE_UNDEFINED; break; case MFI_STATE_BB_INIT: max_wait = MEGASAS_RESET_WAIT_TIME; - cur_state = MFI_STATE_BB_INIT; break; case MFI_STATE_FW_INIT: max_wait = MEGASAS_RESET_WAIT_TIME; - cur_state = MFI_STATE_FW_INIT; break; case MFI_STATE_FW_INIT_2: max_wait = MEGASAS_RESET_WAIT_TIME; - cur_state = MFI_STATE_FW_INIT_2; break; case MFI_STATE_DEVICE_SCAN: max_wait = MEGASAS_RESET_WAIT_TIME; - cur_state = MFI_STATE_DEVICE_SCAN; break; case MFI_STATE_FLUSH_CACHE: max_wait = MEGASAS_RESET_WAIT_TIME; - cur_state = MFI_STATE_FLUSH_CACHE; break; default: dev_printk(KERN_DEBUG, &instance->pdev->dev, "Unknown state 0x%x\n", fw_state); + dev_printk(KERN_DEBUG, &instance->pdev->dev, "System Register set:\n"); + megasas_dump_reg_set(instance->reg_set); return -ENODEV; } /* * The cur_state should not last for more than max_wait secs */ - for (i = 0; i < max_wait; i++) { + for (i = 0; i < max_wait * 50; i++) { curr_abs_state = instance->instancet-> read_fw_status_reg(instance); if (abs_state == curr_abs_state) { - msleep(1000); + msleep(20); } else break; } @@ -3940,6 +4270,8 @@ megasas_transition_to_ready(struct megasas_instance *instance, int ocr) if (curr_abs_state == abs_state) { dev_printk(KERN_DEBUG, &instance->pdev->dev, "FW state [%d] hasn't changed " "in %d secs\n", fw_state, max_wait); + dev_printk(KERN_DEBUG, &instance->pdev->dev, "System Register set:\n"); + megasas_dump_reg_set(instance->reg_set); return -ENODEV; } @@ -4003,23 +4335,12 @@ static int megasas_create_frame_pool(struct megasas_instance *instance) { int i; u16 max_cmd; - u32 sge_sz; u32 frame_count; struct megasas_cmd *cmd; max_cmd = instance->max_mfi_cmds; /* - * Size of our frame is 64 bytes for MFI frame, followed by max SG - * elements and finally SCSI_SENSE_BUFFERSIZE bytes for sense buffer - */ - sge_sz = (IS_DMA64) ? sizeof(struct megasas_sge64) : - sizeof(struct megasas_sge32); - - if (instance->flag_ieee) - sge_sz = sizeof(struct megasas_sge_skinny); - - /* * For MFI controllers. * max_num_sge = 60 * max_sge_sz = 16 byte (sizeof megasas_sge_skinny) @@ -4154,8 +4475,6 @@ int megasas_alloc_cmds(struct megasas_instance *instance) return -ENOMEM; } - memset(instance->cmd_list, 0, sizeof(struct megasas_cmd *) *max_cmd); - for (i = 0; i < max_cmd; i++) { instance->cmd_list[i] = kmalloc(sizeof(struct megasas_cmd), GFP_KERNEL); @@ -4188,6 +4507,7 @@ int megasas_alloc_cmds(struct megasas_instance *instance) if (megasas_create_frame_pool(instance)) { dev_printk(KERN_DEBUG, &instance->pdev->dev, "Error creating frame DMA pool\n"); megasas_free_cmds(instance); + return -ENOMEM; } return 0; @@ -4206,7 +4526,8 @@ dcmd_timeout_ocr_possible(struct megasas_instance *instance) { if (instance->adapter_type == MFI_SERIES) return KILL_ADAPTER; else if (instance->unload || - test_bit(MEGASAS_FUSION_IN_RESET, &instance->reset_flags)) + test_bit(MEGASAS_FUSION_OCR_NOT_POSSIBLE, + &instance->reset_flags)) return IGNORE_TIMEOUT; else return INITIATE_OCR; @@ -4267,8 +4588,10 @@ megasas_get_pd_info(struct megasas_instance *instance, struct scsi_device *sdev) switch (dcmd_timeout_ocr_possible(instance)) { case INITIATE_OCR: cmd->flags |= DRV_DCMD_SKIP_REFIRE; + mutex_unlock(&instance->reset_mutex); megasas_reset_fusion(instance->host, MFI_IO_TIMEOUT_OCR); + mutex_lock(&instance->reset_mutex); break; case KILL_ADAPTER: megaraid_sas_kill_hba(instance); @@ -4304,7 +4627,6 @@ megasas_get_pd_list(struct megasas_instance *instance) struct megasas_dcmd_frame *dcmd; struct MR_PD_LIST *ci; struct MR_PD_ADDRESS *pd_addr; - dma_addr_t ci_h = 0; if (instance->pd_list_not_supported) { dev_info(&instance->pdev->dev, "MR_DCMD_PD_LIST_QUERY " @@ -4313,7 +4635,6 @@ megasas_get_pd_list(struct megasas_instance *instance) } ci = instance->pd_list_buf; - ci_h = instance->pd_list_buf_h; cmd = megasas_get_cmd(instance); @@ -4386,6 +4707,9 @@ megasas_get_pd_list(struct megasas_instance *instance) case DCMD_SUCCESS: pd_addr = ci->addr; + if (megasas_dbg_lvl & LD_PD_DEBUG) + dev_info(&instance->pdev->dev, "%s, sysPD count: 0x%x\n", + __func__, le32_to_cpu(ci->count)); if ((le32_to_cpu(ci->count) > (MEGASAS_MAX_PD_CHANNELS * MEGASAS_MAX_DEV_PER_CHANNEL))) @@ -4401,6 +4725,11 @@ megasas_get_pd_list(struct megasas_instance *instance) pd_addr->scsiDevType; instance->local_pd_list[le16_to_cpu(pd_addr->deviceId)].driveState = MR_PD_STATE_SYSTEM; + if (megasas_dbg_lvl & LD_PD_DEBUG) + dev_info(&instance->pdev->dev, + "PD%d: targetID: 0x%03x deviceType:0x%x\n", + pd_index, le16_to_cpu(pd_addr->deviceId), + pd_addr->scsiDevType); pd_addr++; } @@ -4504,6 +4833,10 @@ megasas_get_ld_list(struct megasas_instance *instance) break; case DCMD_SUCCESS: + if (megasas_dbg_lvl & LD_PD_DEBUG) + dev_info(&instance->pdev->dev, "%s, LD count: 0x%x\n", + __func__, ld_count); + if (ld_count > instance->fw_supported_vd_count) break; @@ -4513,6 +4846,10 @@ megasas_get_ld_list(struct megasas_instance *instance) if (ci->ldList[ld_index].state != 0) { ids = ci->ldList[ld_index].ref.targetId; instance->ld_ids[ids] = ci->ldList[ld_index].ref.targetId; + if (megasas_dbg_lvl & LD_PD_DEBUG) + dev_info(&instance->pdev->dev, + "LD%d: targetID: 0x%03x\n", + ld_index, ids); } } @@ -4528,7 +4865,7 @@ megasas_get_ld_list(struct megasas_instance *instance) /** * megasas_ld_list_query - Returns FW's ld_list structure * @instance: Adapter soft state - * @ld_list: ld_list structure + * @query_type: ld_list structure type * * Issues an internal command (DCMD) to get the FW's controller PD * list structure. This information is mainly used to find out SYSTEM @@ -4616,6 +4953,10 @@ megasas_ld_list_query(struct megasas_instance *instance, u8 query_type) case DCMD_SUCCESS: tgtid_count = le32_to_cpu(ci->count); + if (megasas_dbg_lvl & LD_PD_DEBUG) + dev_info(&instance->pdev->dev, "%s, LD count: 0x%x\n", + __func__, tgtid_count); + if ((tgtid_count > (instance->fw_supported_vd_count))) break; @@ -4623,8 +4964,146 @@ megasas_ld_list_query(struct megasas_instance *instance, u8 query_type) for (ld_index = 0; ld_index < tgtid_count; ld_index++) { ids = ci->targetId[ld_index]; instance->ld_ids[ids] = ci->targetId[ld_index]; + if (megasas_dbg_lvl & LD_PD_DEBUG) + dev_info(&instance->pdev->dev, "LD%d: targetID: 0x%03x\n", + ld_index, ci->targetId[ld_index]); + } + + break; + } + + if (ret != DCMD_TIMEOUT) + megasas_return_cmd(instance, cmd); + + return ret; +} + +/** + * megasas_host_device_list_query + * dcmd.opcode - MR_DCMD_CTRL_DEVICE_LIST_GET + * dcmd.mbox - reserved + * dcmd.sge IN - ptr to return MR_HOST_DEVICE_LIST structure + * Desc: This DCMD will return the combined device list + * Status: MFI_STAT_OK - List returned successfully + * MFI_STAT_INVALID_CMD - Firmware support for the feature has been + * disabled + * @instance: Adapter soft state + * @is_probe: Driver probe check + * Return: 0 if DCMD succeeded + * non-zero if failed + */ +static int +megasas_host_device_list_query(struct megasas_instance *instance, + bool is_probe) +{ + int ret, i, target_id; + struct megasas_cmd *cmd; + struct megasas_dcmd_frame *dcmd; + struct MR_HOST_DEVICE_LIST *ci; + u32 count; + dma_addr_t ci_h; + + ci = instance->host_device_list_buf; + ci_h = instance->host_device_list_buf_h; + + cmd = megasas_get_cmd(instance); + + if (!cmd) { + dev_warn(&instance->pdev->dev, + "%s: failed to get cmd\n", + __func__); + return -ENOMEM; + } + + dcmd = &cmd->frame->dcmd; + + memset(ci, 0, sizeof(*ci)); + memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); + + dcmd->mbox.b[0] = is_probe ? 0 : 1; + dcmd->cmd = MFI_CMD_DCMD; + dcmd->cmd_status = MFI_STAT_INVALID_STATUS; + dcmd->sge_count = 1; + dcmd->flags = MFI_FRAME_DIR_READ; + dcmd->timeout = 0; + dcmd->pad_0 = 0; + dcmd->data_xfer_len = cpu_to_le32(HOST_DEVICE_LIST_SZ); + dcmd->opcode = cpu_to_le32(MR_DCMD_CTRL_DEVICE_LIST_GET); + + megasas_set_dma_settings(instance, dcmd, ci_h, HOST_DEVICE_LIST_SZ); + + if (!instance->mask_interrupts) { + ret = megasas_issue_blocked_cmd(instance, cmd, + MFI_IO_TIMEOUT_SECS); + } else { + ret = megasas_issue_polled(instance, cmd); + cmd->flags |= DRV_DCMD_SKIP_REFIRE; + } + + switch (ret) { + case DCMD_SUCCESS: + /* Fill the internal pd_list and ld_ids array based on + * targetIds returned by FW + */ + count = le32_to_cpu(ci->count); + + if (count > (MEGASAS_MAX_PD + MAX_LOGICAL_DRIVES_EXT)) + break; + + if (megasas_dbg_lvl & LD_PD_DEBUG) + dev_info(&instance->pdev->dev, "%s, Device count: 0x%x\n", + __func__, count); + + memset(instance->local_pd_list, 0, + MEGASAS_MAX_PD * sizeof(struct megasas_pd_list)); + memset(instance->ld_ids, 0xff, MAX_LOGICAL_DRIVES_EXT); + for (i = 0; i < count; i++) { + target_id = le16_to_cpu(ci->host_device_list[i].target_id); + if (ci->host_device_list[i].flags.u.bits.is_sys_pd) { + instance->local_pd_list[target_id].tid = target_id; + instance->local_pd_list[target_id].driveType = + ci->host_device_list[i].scsi_type; + instance->local_pd_list[target_id].driveState = + MR_PD_STATE_SYSTEM; + if (megasas_dbg_lvl & LD_PD_DEBUG) + dev_info(&instance->pdev->dev, + "Device %d: PD targetID: 0x%03x deviceType:0x%x\n", + i, target_id, ci->host_device_list[i].scsi_type); + } else { + instance->ld_ids[target_id] = target_id; + if (megasas_dbg_lvl & LD_PD_DEBUG) + dev_info(&instance->pdev->dev, + "Device %d: LD targetID: 0x%03x\n", + i, target_id); + } } + memcpy(instance->pd_list, instance->local_pd_list, + sizeof(instance->pd_list)); + break; + + case DCMD_TIMEOUT: + switch (dcmd_timeout_ocr_possible(instance)) { + case INITIATE_OCR: + cmd->flags |= DRV_DCMD_SKIP_REFIRE; + mutex_unlock(&instance->reset_mutex); + megasas_reset_fusion(instance->host, + MFI_IO_TIMEOUT_OCR); + mutex_lock(&instance->reset_mutex); + break; + case KILL_ADAPTER: + megaraid_sas_kill_hba(instance); + break; + case IGNORE_TIMEOUT: + dev_info(&instance->pdev->dev, "Ignore DCMD timeout: %s %d\n", + __func__, __LINE__); + break; + } + break; + case DCMD_FAILED: + dev_err(&instance->pdev->dev, + "%s: MR_DCMD_CTRL_DEVICE_LIST_GET failed\n", + __func__); break; } @@ -4677,9 +5156,9 @@ static void megasas_update_ext_vd_details(struct megasas_instance *instance) fusion->current_map_sz = ventura_map_sz; fusion->max_map_sz = ventura_map_sz; } else { - fusion->old_map_sz = sizeof(struct MR_FW_RAID_MAP) + - (sizeof(struct MR_LD_SPAN_MAP) * - (instance->fw_supported_vd_count - 1)); + fusion->old_map_sz = + struct_size_t(struct MR_FW_RAID_MAP, ldSpanMap, + instance->fw_supported_vd_count); fusion->new_map_sz = sizeof(struct MR_FW_RAID_MAP_EXT); fusion->max_map_sz = @@ -4758,8 +5237,10 @@ void megasas_get_snapdump_properties(struct megasas_instance *instance) switch (dcmd_timeout_ocr_possible(instance)) { case INITIATE_OCR: cmd->flags |= DRV_DCMD_SKIP_REFIRE; + mutex_unlock(&instance->reset_mutex); megasas_reset_fusion(instance->host, MFI_IO_TIMEOUT_OCR); + mutex_lock(&instance->reset_mutex); break; case KILL_ADAPTER: megaraid_sas_kill_hba(instance); @@ -4776,7 +5257,7 @@ void megasas_get_snapdump_properties(struct megasas_instance *instance) } /** - * megasas_get_controller_info - Returns FW's controller structure + * megasas_get_ctrl_info - Returns FW's controller structure * @instance: Adapter soft state * * Issues an internal command (DCMD) to get the FW's controller structure. @@ -4838,6 +5319,7 @@ megasas_get_ctrl_info(struct megasas_instance *instance) le32_to_cpus((u32 *)&ci->adapterOperations2); le32_to_cpus((u32 *)&ci->adapterOperations3); le16_to_cpus((u16 *)&ci->adapter_operations4); + le32_to_cpus((u32 *)&ci->adapter_operations5); /* Update the latest Ext VD info. * From Init path, store current firmware details. @@ -4845,12 +5327,14 @@ megasas_get_ctrl_info(struct megasas_instance *instance) * in case of Firmware upgrade without system reboot. */ megasas_update_ext_vd_details(instance); - instance->use_seqnum_jbod_fp = + instance->support_seqnum_jbod_fp = ci->adapterOperations3.useSeqNumJbodFP; instance->support_morethan256jbod = ci->adapter_operations4.support_pd_map_target_id; instance->support_nvme_passthru = ci->adapter_operations4.support_nvme_passthru; + instance->support_pci_lane_margining = + ci->adapter_operations5.support_pci_lane_margining; instance->task_abort_tmo = ci->TaskAbortTO; instance->max_reset_tmo = ci->MaxResetTO; @@ -4861,6 +5345,9 @@ megasas_get_ctrl_info(struct megasas_instance *instance) (ci->properties.on_off_properties2.enable_snap_dump ? MEGASAS_DEFAULT_SNAP_DUMP_WAIT_TIME : 0); + instance->enable_fw_dev_list = + ci->properties.on_off_properties2.enable_fw_dev_list; + dev_info(&instance->pdev->dev, "controller type\t: %s(%dMB)\n", instance->is_imr ? "iMR" : "MR", @@ -4879,6 +5366,10 @@ megasas_get_ctrl_info(struct megasas_instance *instance) dev_info(&instance->pdev->dev, "FW provided TM TaskAbort/Reset timeout\t: %d secs/%d secs\n", instance->task_abort_tmo, instance->max_reset_tmo); + dev_info(&instance->pdev->dev, "JBOD sequence map support\t: %s\n", + instance->support_seqnum_jbod_fp ? "Yes" : "No"); + dev_info(&instance->pdev->dev, "PCI Lane Margining support\t: %s\n", + instance->support_pci_lane_margining ? "Yes" : "No"); break; @@ -4886,8 +5377,10 @@ megasas_get_ctrl_info(struct megasas_instance *instance) switch (dcmd_timeout_ocr_possible(instance)) { case INITIATE_OCR: cmd->flags |= DRV_DCMD_SKIP_REFIRE; + mutex_unlock(&instance->reset_mutex); megasas_reset_fusion(instance->host, MFI_IO_TIMEOUT_OCR); + mutex_lock(&instance->reset_mutex); break; case KILL_ADAPTER: megaraid_sas_kill_hba(instance); @@ -5154,6 +5647,25 @@ fail_alloc_cmds: return 1; } +static +void megasas_setup_irq_poll(struct megasas_instance *instance) +{ + struct megasas_irq_context *irq_ctx; + u32 count, i; + + count = instance->msix_vectors > 0 ? instance->msix_vectors : 1; + + /* Initialize IRQ poll */ + for (i = 0; i < count; i++) { + irq_ctx = &instance->irq_context[i]; + irq_ctx->os_irq = pci_irq_vector(instance->pdev, i); + irq_ctx->irq_poll_scheduled = false; + irq_poll_init(&irq_ctx->irqpoll, + instance->threshold_reply_count, + megasas_irqpoll); + } +} + /* * megasas_setup_irqs_ioapic - register legacy interrupts. * @instance: Adapter soft state @@ -5170,14 +5682,18 @@ megasas_setup_irqs_ioapic(struct megasas_instance *instance) pdev = instance->pdev; instance->irq_context[0].instance = instance; instance->irq_context[0].MSIxIndex = 0; + snprintf(instance->irq_context->name, MEGASAS_MSIX_NAME_LEN, "%s%u", + "megasas", instance->host->host_no); if (request_irq(pci_irq_vector(pdev, 0), instance->instancet->service_isr, IRQF_SHARED, - "megasas", &instance->irq_context[0])) { + instance->irq_context->name, &instance->irq_context[0])) { dev_err(&instance->pdev->dev, "Failed to register IRQ from %s %d\n", __func__, __LINE__); return -1; } + instance->perf_mode = MR_LATENCY_PERF_MODE; + instance->low_latency_index_start = 0; return 0; } @@ -5202,16 +5718,23 @@ megasas_setup_irqs_msix(struct megasas_instance *instance, u8 is_probe) for (i = 0; i < instance->msix_vectors; i++) { instance->irq_context[i].instance = instance; instance->irq_context[i].MSIxIndex = i; + snprintf(instance->irq_context[i].name, MEGASAS_MSIX_NAME_LEN, "%s%u-msix%u", + "megasas", instance->host->host_no, i); if (request_irq(pci_irq_vector(pdev, i), - instance->instancet->service_isr, 0, "megasas", + instance->instancet->service_isr, 0, instance->irq_context[i].name, &instance->irq_context[i])) { dev_err(&instance->pdev->dev, "Failed to register IRQ for vector %d.\n", i); - for (j = 0; j < i; j++) + for (j = 0; j < i; j++) { + if (j < instance->low_latency_index_start) + irq_update_affinity_hint( + pci_irq_vector(pdev, j), NULL); free_irq(pci_irq_vector(pdev, j), &instance->irq_context[j]); + } /* Retry irq register for IO_APIC*/ instance->msix_vectors = 0; + instance->msix_load_balance = false; if (is_probe) { pci_free_irq_vectors(instance->pdev); return megasas_setup_irqs_ioapic(instance); @@ -5220,6 +5743,7 @@ megasas_setup_irqs_msix(struct megasas_instance *instance, u8 is_probe) } } } + return 0; } @@ -5232,9 +5756,22 @@ static void megasas_destroy_irqs(struct megasas_instance *instance) { int i; + int count; + struct megasas_irq_context *irq_ctx; + + count = instance->msix_vectors > 0 ? instance->msix_vectors : 1; + if (instance->adapter_type != MFI_SERIES) { + for (i = 0; i < count; i++) { + irq_ctx = &instance->irq_context[i]; + irq_poll_disable(&irq_ctx->irqpoll); + } + } if (instance->msix_vectors) for (i = 0; i < instance->msix_vectors; i++) { + if (i < instance->low_latency_index_start) + irq_update_affinity_hint( + pci_irq_vector(instance->pdev, i), NULL); free_irq(pci_irq_vector(instance->pdev, i), &instance->irq_context[i]); } @@ -5246,7 +5783,6 @@ megasas_destroy_irqs(struct megasas_instance *instance) { /** * megasas_setup_jbod_map - setup jbod map for FP seq_number. * @instance: Adapter soft state - * @is_probe: Driver probe check * * Return 0 on success. */ @@ -5255,15 +5791,17 @@ megasas_setup_jbod_map(struct megasas_instance *instance) { int i; struct fusion_context *fusion = instance->ctrl_context; - u32 pd_seq_map_sz; + size_t pd_seq_map_sz; - pd_seq_map_sz = sizeof(struct MR_PD_CFG_SEQ_NUM_SYNC) + - (sizeof(struct MR_PD_CFG_SEQ) * (MAX_PHYSICAL_DEVICES - 1)); + pd_seq_map_sz = struct_size_t(struct MR_PD_CFG_SEQ_NUM_SYNC, seq, + MAX_PHYSICAL_DEVICES); + instance->use_seqnum_jbod_fp = + instance->support_seqnum_jbod_fp; if (reset_devices || !fusion || - !instance->ctrl_info_buf->adapterOperations3.useSeqNumJbodFP) { + !instance->support_seqnum_jbod_fp) { dev_info(&instance->pdev->dev, - "Jbod map is not supported %s %d\n", + "JBOD sequence map is disabled %s %d\n", __func__, __LINE__); instance->use_seqnum_jbod_fp = false; return; @@ -5302,9 +5840,11 @@ skip_alloc: static void megasas_setup_reply_map(struct megasas_instance *instance) { const struct cpumask *mask; - unsigned int queue, cpu; + unsigned int queue, cpu, low_latency_index_start; + + low_latency_index_start = instance->low_latency_index_start; - for (queue = 0; queue < instance->msix_vectors; queue++) { + for (queue = low_latency_index_start; queue < instance->msix_vectors; queue++) { mask = pci_irq_get_affinity(instance->pdev, queue); if (!mask) goto fallback; @@ -5315,8 +5855,162 @@ static void megasas_setup_reply_map(struct megasas_instance *instance) return; fallback: - for_each_possible_cpu(cpu) - instance->reply_map[cpu] = cpu % instance->msix_vectors; + queue = low_latency_index_start; + for_each_possible_cpu(cpu) { + instance->reply_map[cpu] = queue; + if (queue == (instance->msix_vectors - 1)) + queue = low_latency_index_start; + else + queue++; + } +} + +/** + * megasas_get_device_list - Get the PD and LD device list from FW. + * @instance: Adapter soft state + * @return: Success or failure + * + * Issue DCMDs to Firmware to get the PD and LD list. + * Based on the FW support, driver sends the HOST_DEVICE_LIST or combination + * of PD_LIST/LD_LIST_QUERY DCMDs to get the device list. + */ +static +int megasas_get_device_list(struct megasas_instance *instance) +{ + if (instance->enable_fw_dev_list) { + if (megasas_host_device_list_query(instance, true)) + return FAILED; + } else { + if (megasas_get_pd_list(instance) < 0) { + dev_err(&instance->pdev->dev, "failed to get PD list\n"); + return FAILED; + } + + if (megasas_ld_list_query(instance, + MR_LD_QUERY_TYPE_EXPOSED_TO_HOST)) { + dev_err(&instance->pdev->dev, "failed to get LD list\n"); + return FAILED; + } + } + + return SUCCESS; +} + +/** + * megasas_set_high_iops_queue_affinity_and_hint - Set affinity and hint + * for high IOPS queues + * @instance: Adapter soft state + * return: void + */ +static inline void +megasas_set_high_iops_queue_affinity_and_hint(struct megasas_instance *instance) +{ + int i; + unsigned int irq; + const struct cpumask *mask; + + if (instance->perf_mode == MR_BALANCED_PERF_MODE) { + int nid = dev_to_node(&instance->pdev->dev); + + if (nid == NUMA_NO_NODE) + nid = 0; + mask = cpumask_of_node(nid); + + for (i = 0; i < instance->low_latency_index_start; i++) { + irq = pci_irq_vector(instance->pdev, i); + irq_set_affinity_and_hint(irq, mask); + } + } +} + +static int +__megasas_alloc_irq_vectors(struct megasas_instance *instance) +{ + int i, irq_flags; + struct irq_affinity desc = { .pre_vectors = instance->low_latency_index_start }; + struct irq_affinity *descp = &desc; + + irq_flags = PCI_IRQ_MSIX; + + if (instance->smp_affinity_enable) + irq_flags |= PCI_IRQ_AFFINITY | PCI_IRQ_ALL_TYPES; + else + descp = NULL; + + /* Do not allocate msix vectors for poll_queues. + * msix_vectors is always within a range of FW supported reply queue. + */ + i = pci_alloc_irq_vectors_affinity(instance->pdev, + instance->low_latency_index_start, + instance->msix_vectors - instance->iopoll_q_count, irq_flags, descp); + + return i; +} + +/** + * megasas_alloc_irq_vectors - Allocate IRQ vectors/enable MSI-x vectors + * @instance: Adapter soft state + * return: void + */ +static void +megasas_alloc_irq_vectors(struct megasas_instance *instance) +{ + int i; + unsigned int num_msix_req; + + instance->iopoll_q_count = 0; + if ((instance->adapter_type != MFI_SERIES) && + poll_queues) { + + instance->perf_mode = MR_LATENCY_PERF_MODE; + instance->low_latency_index_start = 1; + + /* reserve for default and non-mananged pre-vector. */ + if (instance->msix_vectors > (poll_queues + 2)) + instance->iopoll_q_count = poll_queues; + else + instance->iopoll_q_count = 0; + + num_msix_req = blk_mq_num_online_queues(0) + + instance->low_latency_index_start; + instance->msix_vectors = min(num_msix_req, + instance->msix_vectors); + + } + + i = __megasas_alloc_irq_vectors(instance); + + if (((instance->perf_mode == MR_BALANCED_PERF_MODE) + || instance->iopoll_q_count) && + (i != (instance->msix_vectors - instance->iopoll_q_count))) { + if (instance->msix_vectors) + pci_free_irq_vectors(instance->pdev); + /* Disable Balanced IOPS mode and try realloc vectors */ + instance->perf_mode = MR_LATENCY_PERF_MODE; + instance->low_latency_index_start = 1; + num_msix_req = blk_mq_num_online_queues(0) + + instance->low_latency_index_start; + + instance->msix_vectors = min(num_msix_req, + instance->msix_vectors); + + instance->iopoll_q_count = 0; + i = __megasas_alloc_irq_vectors(instance); + + } + + dev_info(&instance->pdev->dev, + "requested/available msix %d/%d poll_queue %d\n", + instance->msix_vectors - instance->iopoll_q_count, + i, instance->iopoll_q_count); + + if (i > 0) + instance->msix_vectors = i; + else + instance->msix_vectors = 0; + + if (instance->smp_affinity_enable) + megasas_set_high_iops_queue_affinity_and_hint(instance); } /** @@ -5332,12 +6026,15 @@ static int megasas_init_fw(struct megasas_instance *instance) u32 max_sectors_2, tmp_sectors, msix_enable; u32 scratch_pad_1, scratch_pad_2, scratch_pad_3, status_reg; resource_size_t base_addr; + void *base_addr_phys; struct megasas_ctrl_info *ctrl_info = NULL; unsigned long bar_list; - int i, j, loop, fw_msix_count = 0; + int i, j, loop; struct IOV_111 *iovPtr; struct fusion_context *fusion; - bool do_adp_reset = true; + bool intr_coalescing; + unsigned int num_msix_req; + u16 lnksta, speed; fusion = instance->ctrl_context; @@ -5351,13 +6048,18 @@ static int megasas_init_fw(struct megasas_instance *instance) } base_addr = pci_resource_start(instance->pdev, instance->bar); - instance->reg_set = ioremap_nocache(base_addr, 8192); + instance->reg_set = ioremap(base_addr, 8192); if (!instance->reg_set) { dev_printk(KERN_DEBUG, &instance->pdev->dev, "Failed to map IO mem\n"); goto fail_ioremap; } + base_addr_phys = &base_addr; + dev_printk(KERN_DEBUG, &instance->pdev->dev, + "BAR:0x%lx BAR's base_addr(phys):%pa mapped virt_addr:0x%p\n", + instance->bar, base_addr_phys, instance->reg_set); + if (instance->adapter_type != MFI_SERIES) instance->instancet = &megasas_instance_template_fusion; else { @@ -5384,29 +6086,35 @@ static int megasas_init_fw(struct megasas_instance *instance) } if (megasas_transition_to_ready(instance, 0)) { - if (instance->adapter_type >= INVADER_SERIES) { + dev_info(&instance->pdev->dev, + "Failed to transition controller to ready from %s!\n", + __func__); + if (instance->adapter_type != MFI_SERIES) { status_reg = instance->instancet->read_fw_status_reg( instance); - do_adp_reset = status_reg & MFI_RESET_ADAPTER; - } - - if (do_adp_reset) { + if (status_reg & MFI_RESET_ADAPTER) { + if (megasas_adp_reset_wait_for_ready + (instance, true, 0) == FAILED) + goto fail_ready_state; + } else { + goto fail_ready_state; + } + } else { atomic_set(&instance->fw_reset_no_pci_access, 1); instance->instancet->adp_reset (instance, instance->reg_set); atomic_set(&instance->fw_reset_no_pci_access, 0); - dev_info(&instance->pdev->dev, - "FW restarted successfully from %s!\n", - __func__); /*waiting for about 30 second before retry*/ ssleep(30); if (megasas_transition_to_ready(instance, 0)) goto fail_ready_state; - } else { - goto fail_ready_state; } + + dev_info(&instance->pdev->dev, + "FW restarted successfully from %s!\n", + __func__); } megasas_init_ctrl_params(instance); @@ -5431,11 +6139,23 @@ static int megasas_init_fw(struct megasas_instance *instance) MR_MAX_RAID_MAP_SIZE_MASK); } + instance->enable_sdev_max_qd = enable_sdev_max_qd; + + switch (instance->adapter_type) { + case VENTURA_SERIES: + fusion->pcie_bw_limitation = true; + break; + case AERO_SERIES: + fusion->r56_div_offload = true; + break; + default: + break; + } + /* Check if MSI-X is supported while in ready state */ msix_enable = (instance->instancet->read_fw_status_reg(instance) & 0x4000000) >> 0x1a; if (msix_enable && !msix_disable) { - int irq_flags = PCI_IRQ_MSIX; scratch_pad_1 = megasas_readl (instance, &instance->reg_set->outbound_scratch_pad_1); @@ -5445,7 +6165,6 @@ static int megasas_init_fw(struct megasas_instance *instance) /* Thunderbolt Series*/ instance->msix_vectors = (scratch_pad_1 & MR_MAX_REPLY_QUEUES_OFFSET) + 1; - fw_msix_count = instance->msix_vectors; } else { instance->msix_vectors = ((scratch_pad_1 & MR_MAX_REPLY_QUEUES_EXT_OFFSET) @@ -5474,7 +6193,13 @@ static int megasas_init_fw(struct megasas_instance *instance) if (rdpq_enable) instance->is_rdpq = (scratch_pad_1 & MR_RDPQ_MODE_OFFSET) ? 1 : 0; - fw_msix_count = instance->msix_vectors; + + if (instance->adapter_type >= INVADER_SERIES && + !instance->msix_combined) { + instance->msix_load_balance = true; + instance->smp_affinity_enable = false; + } + /* Save 1-15 reply post index address to local memory * Index 0 is already saved from reg offset * MPI2_REPLY_POST_HOST_INDEX_OFFSET @@ -5487,22 +6212,92 @@ static int megasas_init_fw(struct megasas_instance *instance) + (loop * 0x10)); } } + + dev_info(&instance->pdev->dev, + "firmware supports msix\t: (%d)", + instance->msix_vectors); if (msix_vectors) instance->msix_vectors = min(msix_vectors, instance->msix_vectors); } else /* MFI adapters */ instance->msix_vectors = 1; - /* Don't bother allocating more MSI-X vectors than cpus */ - instance->msix_vectors = min(instance->msix_vectors, - (unsigned int)num_online_cpus()); - if (smp_affinity_enable) - irq_flags |= PCI_IRQ_AFFINITY; - i = pci_alloc_irq_vectors(instance->pdev, 1, - instance->msix_vectors, irq_flags); - if (i > 0) - instance->msix_vectors = i; + + + /* + * For Aero (if some conditions are met), driver will configure a + * few additional reply queues with interrupt coalescing enabled. + * These queues with interrupt coalescing enabled are called + * High IOPS queues and rest of reply queues (based on number of + * logical CPUs) are termed as Low latency queues. + * + * Total Number of reply queues = High IOPS queues + low latency queues + * + * For rest of fusion adapters, 1 additional reply queue will be + * reserved for management commands, rest of reply queues + * (based on number of logical CPUs) will be used for IOs and + * referenced as IO queues. + * Total Number of reply queues = 1 + IO queues + * + * MFI adapters supports single MSI-x so single reply queue + * will be used for IO and management commands. + */ + + intr_coalescing = (scratch_pad_1 & MR_INTR_COALESCING_SUPPORT_OFFSET) ? + true : false; + if (intr_coalescing && + (blk_mq_num_online_queues(0) >= MR_HIGH_IOPS_QUEUE_COUNT) && + (instance->msix_vectors == MEGASAS_MAX_MSIX_QUEUES)) + instance->perf_mode = MR_BALANCED_PERF_MODE; else - instance->msix_vectors = 0; + instance->perf_mode = MR_LATENCY_PERF_MODE; + + + if (instance->adapter_type == AERO_SERIES) { + pcie_capability_read_word(instance->pdev, PCI_EXP_LNKSTA, &lnksta); + speed = lnksta & PCI_EXP_LNKSTA_CLS; + + /* + * For Aero, if PCIe link speed is <16 GT/s, then driver should operate + * in latency perf mode and enable R1 PCI bandwidth algorithm + */ + if (speed < 0x4) { + instance->perf_mode = MR_LATENCY_PERF_MODE; + fusion->pcie_bw_limitation = true; + } + + /* + * Performance mode settings provided through module parameter-perf_mode will + * take affect only for: + * 1. Aero family of adapters. + * 2. When user sets module parameter- perf_mode in range of 0-2. + */ + if ((perf_mode >= MR_BALANCED_PERF_MODE) && + (perf_mode <= MR_LATENCY_PERF_MODE)) + instance->perf_mode = perf_mode; + /* + * If intr coalescing is not supported by controller FW, then IOPS + * and Balanced modes are not feasible. + */ + if (!intr_coalescing) + instance->perf_mode = MR_LATENCY_PERF_MODE; + + } + + if (instance->perf_mode == MR_BALANCED_PERF_MODE) + instance->low_latency_index_start = + MR_HIGH_IOPS_QUEUE_COUNT; + else + instance->low_latency_index_start = 1; + + num_msix_req = blk_mq_num_online_queues(0) + + instance->low_latency_index_start; + + instance->msix_vectors = min(num_msix_req, + instance->msix_vectors); + + megasas_alloc_irq_vectors(instance); + if (!instance->msix_vectors) + instance->msix_load_balance = false; } /* * MSI-X host index 0 is common for all adapter. @@ -5519,7 +6314,7 @@ static int megasas_init_fw(struct megasas_instance *instance) } if (!instance->msix_vectors) { - i = pci_alloc_irq_vectors(instance->pdev, 1, 1, PCI_IRQ_LEGACY); + i = pci_alloc_irq_vectors(instance->pdev, 1, 1, PCI_IRQ_INTX); if (i < 0) goto fail_init_adapter; } @@ -5527,10 +6322,8 @@ static int megasas_init_fw(struct megasas_instance *instance) megasas_setup_reply_map(instance); dev_info(&instance->pdev->dev, - "firmware supports msix\t: (%d)", fw_msix_count); - dev_info(&instance->pdev->dev, - "current msix/online cpus\t: (%d/%d)\n", - instance->msix_vectors, (unsigned int)num_online_cpus()); + "current msix/max num queues\t: (%d/%u)\n", + instance->msix_vectors, blk_mq_num_online_queues(0)); dev_info(&instance->pdev->dev, "RDPQ mode\t: (%s)\n", instance->is_rdpq ? "enabled" : "disabled"); @@ -5565,24 +6358,22 @@ static int megasas_init_fw(struct megasas_instance *instance) megasas_setup_irqs_ioapic(instance)) goto fail_init_adapter; + if (instance->adapter_type != MFI_SERIES) + megasas_setup_irq_poll(instance); + instance->instancet->enable_intr(instance); dev_info(&instance->pdev->dev, "INIT adapter done\n"); megasas_setup_jbod_map(instance); - /** for passthrough - * the following function will get the PD LIST. - */ - memset(instance->pd_list, 0, - (MEGASAS_MAX_PD * sizeof(struct megasas_pd_list))); - if (megasas_get_pd_list(instance) < 0) { - dev_err(&instance->pdev->dev, "failed to get PD list\n"); + if (megasas_get_device_list(instance) != SUCCESS) { + dev_err(&instance->pdev->dev, + "%s: megasas_get_device_list failed\n", + __func__); goto fail_get_ld_pd_list; } - memset(instance->ld_ids, 0xff, MEGASAS_MAX_LD_IDS); - /* stream detection initialization */ if (instance->adapter_type >= VENTURA_SERIES) { fusion->stream_detect_by_ld = @@ -5600,7 +6391,7 @@ static int megasas_init_fw(struct megasas_instance *instance) GFP_KERNEL); if (!fusion->stream_detect_by_ld[i]) { dev_err(&instance->pdev->dev, - "unable to allocate stream detect by LD\n "); + "unable to allocate stream detect by LD\n"); for (j = 0; j < i; ++j) kfree(fusion->stream_detect_by_ld[j]); kfree(fusion->stream_detect_by_ld); @@ -5612,10 +6403,6 @@ static int megasas_init_fw(struct megasas_instance *instance) } } - if (megasas_ld_list_query(instance, - MR_LD_QUERY_TYPE_EXPOSED_TO_HOST)) - goto fail_get_ld_pd_list; - /* * Compute the max allowed sectors per IO: The controller info has two * limits on max sectors. Driver should use the minimum of these two. @@ -5700,8 +6487,8 @@ static int megasas_init_fw(struct megasas_instance *instance) instance->UnevenSpanSupport ? "yes" : "no"); dev_info(&instance->pdev->dev, "firmware crash dump : %s\n", instance->crash_dump_drv_support ? "yes" : "no"); - dev_info(&instance->pdev->dev, "jbod sync map : %s\n", - instance->use_seqnum_jbod_fp ? "yes" : "no"); + dev_info(&instance->pdev->dev, "JBOD sequence map : %s\n", + instance->use_seqnum_jbod_fp ? "enabled" : "disabled"); instance->max_sectors_per_req = instance->max_num_sge * SGE_BUFFER_SIZE / 512; @@ -5746,7 +6533,7 @@ static int megasas_init_fw(struct megasas_instance *instance) fail_start_watchdog: if (instance->requestorId && !instance->skip_heartbeat_timer_del) - del_timer_sync(&instance->sriov_heartbeat_timer); + timer_delete_sync(&instance->sriov_heartbeat_timer); fail_get_ld_pd_list: instance->instancet->disable_intr(instance); megasas_destroy_irqs(instance); @@ -5868,7 +6655,7 @@ dcmd_failed: * megasas_register_aen - Registers for asynchronous event notification * @instance: Adapter soft state * @seq_num: The starting sequence number - * @class_locale: Class of the event + * @class_locale_word: Class of the event * * This function subscribes for AEN for events beyond the @seq_num. It requests * to be notified if and only if the event is of type @class_locale @@ -6022,7 +6809,8 @@ megasas_get_target_prop(struct megasas_instance *instance, int ret; struct megasas_cmd *cmd; struct megasas_dcmd_frame *dcmd; - u16 targetId = (sdev->channel % 2) + sdev->id; + u16 targetId = ((sdev->channel % 2) * MEGASAS_MAX_DEV_PER_CHANNEL) + + sdev->id; cmd = megasas_get_cmd(instance); @@ -6064,8 +6852,10 @@ megasas_get_target_prop(struct megasas_instance *instance, switch (dcmd_timeout_ocr_possible(instance)) { case INITIATE_OCR: cmd->flags |= DRV_DCMD_SKIP_REFIRE; + mutex_unlock(&instance->reset_mutex); megasas_reset_fusion(instance->host, MFI_IO_TIMEOUT_OCR); + mutex_lock(&instance->reset_mutex); break; case KILL_ADAPTER: megaraid_sas_kill_hba(instance); @@ -6165,6 +6955,32 @@ static int megasas_io_attach(struct megasas_instance *instance) host->max_lun = MEGASAS_MAX_LUN; host->max_cmd_len = 16; + /* Use shared host tagset only for fusion adaptors + * if there are managed interrupts (smp affinity enabled case). + * Single msix_vectors in kdump, so shared host tag is also disabled. + */ + + host->host_tagset = 0; + host->nr_hw_queues = 1; + + if ((instance->adapter_type != MFI_SERIES) && + (instance->msix_vectors > instance->low_latency_index_start) && + host_tagset_enable && + instance->smp_affinity_enable) { + host->host_tagset = 1; + host->nr_hw_queues = instance->msix_vectors - + instance->low_latency_index_start + instance->iopoll_q_count; + if (instance->iopoll_q_count) + host->nr_maps = 3; + } else { + instance->iopoll_q_count = 0; + } + + dev_info(&instance->pdev->dev, + "Max firmware commands: %d shared with default " + "hw_queues = %d poll_queues %d\n", instance->max_fw_cmds, + host->nr_hw_queues - instance->iopoll_q_count, + instance->iopoll_q_count); /* * Notify the mid-layer about the new controller */ @@ -6339,22 +7155,18 @@ static int megasas_alloc_ctrl_mem(struct megasas_instance *instance) switch (instance->adapter_type) { case MFI_SERIES: if (megasas_alloc_mfi_ctrl_mem(instance)) - goto fail; + return -ENOMEM; break; case AERO_SERIES: case VENTURA_SERIES: case THUNDERBOLT_SERIES: case INVADER_SERIES: if (megasas_alloc_fusion_context(instance)) - goto fail; + return -ENOMEM; break; } return 0; - fail: - kfree(instance->reply_map); - instance->reply_map = NULL; - return -ENOMEM; } /* @@ -6385,8 +7197,9 @@ static inline void megasas_free_ctrl_mem(struct megasas_instance *instance) * megasas_alloc_ctrl_dma_buffers - Allocate consistent DMA buffers during * driver load time * - * @instance- Adapter soft instance - * @return- O for SUCCESS + * @instance: Adapter soft instance + * + * @return: O for SUCCESS */ static inline int megasas_alloc_ctrl_dma_buffers(struct megasas_instance *instance) @@ -6413,7 +7226,7 @@ int megasas_alloc_ctrl_dma_buffers(struct megasas_instance *instance) if (!fusion->ioc_init_request) { dev_err(&pdev->dev, - "Failed to allocate PD list buffer\n"); + "Failed to allocate ioc init request\n"); return -ENOMEM; } @@ -6424,6 +7237,18 @@ int megasas_alloc_ctrl_dma_buffers(struct megasas_instance *instance) if (!instance->snapdump_prop) dev_err(&pdev->dev, "Failed to allocate snapdump properties buffer\n"); + + instance->host_device_list_buf = dma_alloc_coherent(&pdev->dev, + HOST_DEVICE_LIST_SZ, + &instance->host_device_list_buf_h, + GFP_KERNEL); + + if (!instance->host_device_list_buf) { + dev_err(&pdev->dev, + "Failed to allocate targetid list buffer\n"); + return -ENOMEM; + } + } instance->pd_list_buf = @@ -6573,6 +7398,13 @@ void megasas_free_ctrl_dma_buffers(struct megasas_instance *instance) sizeof(struct MR_SNAPDUMP_PROPERTIES), instance->snapdump_prop, instance->snapdump_prop_h); + + if (instance->host_device_list_buf) + dma_free_coherent(&pdev->dev, + HOST_DEVICE_LIST_SZ, + instance->host_device_list_buf, + instance->host_device_list_buf_h); + } /* @@ -6596,11 +7428,12 @@ static inline void megasas_init_ctrl_params(struct megasas_instance *instance) INIT_LIST_HEAD(&instance->internal_reset_pending_q); atomic_set(&instance->fw_outstanding, 0); + atomic64_set(&instance->total_io_count, 0); init_waitqueue_head(&instance->int_cmd_wait_q); init_waitqueue_head(&instance->abort_cmd_wait_q); - spin_lock_init(&instance->crashdump_lock); + mutex_init(&instance->crashdump_lock); spin_lock_init(&instance->mfi_pool_lock); spin_lock_init(&instance->hba_lock); spin_lock_init(&instance->stream_lock); @@ -6612,12 +7445,13 @@ static inline void megasas_init_ctrl_params(struct megasas_instance *instance) (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY)) instance->flag_ieee = 1; - megasas_dbg_lvl = 0; instance->flag = 0; instance->unload = 1; instance->last_time = 0; instance->disableOnlineCtrlReset = 1; instance->UnevenSpanSupport = 0; + instance->smp_affinity_enable = smp_affinity_enable ? true : false; + instance->msix_load_balance = false; if (instance->adapter_type != MFI_SERIES) INIT_WORK(&instance->work_init, megasas_fusion_ocr_wq); @@ -6639,6 +7473,12 @@ static int megasas_probe_one(struct pci_dev *pdev, u16 control = 0; switch (pdev->device) { + case PCI_DEVICE_ID_LSI_AERO_10E0: + case PCI_DEVICE_ID_LSI_AERO_10E3: + case PCI_DEVICE_ID_LSI_AERO_10E4: + case PCI_DEVICE_ID_LSI_AERO_10E7: + dev_err(&pdev->dev, "Adapter is in non secure mode\n"); + return 1; case PCI_DEVICE_ID_LSI_AERO_10E1: case PCI_DEVICE_ID_LSI_AERO_10E5: dev_info(&pdev->dev, "Adapter is in configurable secure mode\n"); @@ -6689,7 +7529,7 @@ static int megasas_probe_one(struct pci_dev *pdev, */ instance->pdev = pdev; instance->host = host; - instance->unique_id = pdev->bus->number << 8 | pdev->devfn; + instance->unique_id = pci_dev_id(pdev); instance->init_id = MEGASAS_DEFAULT_INIT_ID; megasas_set_adapter_type(instance); @@ -6746,7 +7586,9 @@ static int megasas_probe_one(struct pci_dev *pdev, /* * Trigger SCSI to scan our drives */ - scsi_scan_host(host); + if (!instance->enable_fw_dev_list || + (instance->host_device_list_buf->count > 0)) + scsi_scan_host(host); /* * Initiate AEN (Asynchronous Event Notification) @@ -6756,6 +7598,8 @@ static int megasas_probe_one(struct pci_dev *pdev, goto fail_start_aen; } + megasas_setup_debugfs(instance); + /* Get current SR-IOV LD/VF affiliation */ if (instance->requestorId) megasas_get_ld_vf_affiliation(instance, 1); @@ -6763,11 +7607,16 @@ static int megasas_probe_one(struct pci_dev *pdev, return 0; fail_start_aen: + instance->unload = 1; + scsi_remove_host(instance->host); fail_io_attach: megasas_mgmt_info.count--; megasas_mgmt_info.max_index--; megasas_mgmt_info.instance[megasas_mgmt_info.max_index] = NULL; + if (instance->requestorId && !instance->skip_heartbeat_timer_del) + timer_delete_sync(&instance->sriov_heartbeat_timer); + instance->instancet->disable_intr(instance); megasas_destroy_irqs(instance); @@ -6775,8 +7624,16 @@ fail_io_attach: megasas_release_fusion(instance); else megasas_release_mfi(instance); + if (instance->msix_vectors) pci_free_irq_vectors(instance->pdev); + instance->msix_vectors = 0; + + if (instance->fw_crash_state != UNAVAILABLE) + megasas_free_host_crash_buffer(instance); + + if (instance->adapter_type != MFI_SERIES) + megasas_fusion_stop_watchdog(instance); fail_init_mfi: scsi_host_put(host); fail_alloc_instance: @@ -6878,25 +7735,27 @@ static void megasas_shutdown_controller(struct megasas_instance *instance, megasas_return_cmd(instance, cmd); } -#ifdef CONFIG_PM /** * megasas_suspend - driver suspend entry point - * @pdev: PCI device structure - * @state: PCI power state to suspend routine + * @dev: Device structure */ -static int -megasas_suspend(struct pci_dev *pdev, pm_message_t state) +static int __maybe_unused +megasas_suspend(struct device *dev) { - struct Scsi_Host *host; struct megasas_instance *instance; - instance = pci_get_drvdata(pdev); - host = instance->host; + instance = dev_get_drvdata(dev); + + if (!instance) + return 0; + instance->unload = 1; + dev_info(dev, "%s is called\n", __func__); + /* Shutdown SR-IOV heartbeat timer */ if (instance->requestorId && !instance->skip_heartbeat_timer_del) - del_timer_sync(&instance->sriov_heartbeat_timer); + timer_delete_sync(&instance->sriov_heartbeat_timer); /* Stop the FW fault detection watchdog */ if (instance->adapter_type != MFI_SERIES) @@ -6922,50 +7781,62 @@ megasas_suspend(struct pci_dev *pdev, pm_message_t state) if (instance->msix_vectors) pci_free_irq_vectors(instance->pdev); - pci_save_state(pdev); - pci_disable_device(pdev); - - pci_set_power_state(pdev, pci_choose_state(pdev, state)); - return 0; } /** * megasas_resume- driver resume entry point - * @pdev: PCI device structure + * @dev: Device structure */ -static int -megasas_resume(struct pci_dev *pdev) +static int __maybe_unused +megasas_resume(struct device *dev) { int rval; struct Scsi_Host *host; struct megasas_instance *instance; - int irq_flags = PCI_IRQ_LEGACY; + u32 status_reg; - instance = pci_get_drvdata(pdev); - host = instance->host; - pci_set_power_state(pdev, PCI_D0); - pci_enable_wake(pdev, PCI_D0, 0); - pci_restore_state(pdev); + instance = dev_get_drvdata(dev); - /* - * PCI prepping: enable device set bus mastering and dma mask - */ - rval = pci_enable_device_mem(pdev); + if (!instance) + return 0; - if (rval) { - dev_err(&pdev->dev, "Enable device failed\n"); - return rval; - } + host = instance->host; - pci_set_master(pdev); + dev_info(dev, "%s is called\n", __func__); /* * We expect the FW state to be READY */ - if (megasas_transition_to_ready(instance, 0)) - goto fail_ready_state; + if (megasas_transition_to_ready(instance, 0)) { + dev_info(&instance->pdev->dev, + "Failed to transition controller to ready from %s!\n", + __func__); + if (instance->adapter_type != MFI_SERIES) { + status_reg = + instance->instancet->read_fw_status_reg(instance); + if (!(status_reg & MFI_RESET_ADAPTER) || + ((megasas_adp_reset_wait_for_ready + (instance, true, 0)) == FAILED)) + goto fail_ready_state; + } else { + atomic_set(&instance->fw_reset_no_pci_access, 1); + instance->instancet->adp_reset + (instance, instance->reg_set); + atomic_set(&instance->fw_reset_no_pci_access, 0); + + /* waiting for about 30 seconds before retry */ + ssleep(30); + + if (megasas_transition_to_ready(instance, 0)) + goto fail_ready_state; + } + + dev_info(&instance->pdev->dev, + "FW restarted successfully from %s!\n", + __func__); + } if (megasas_set_dma_mask(instance)) goto fail_set_dma_mask; @@ -6977,16 +7848,15 @@ megasas_resume(struct pci_dev *pdev) atomic_set(&instance->ldio_outstanding, 0); /* Now re-enable MSI-X */ - if (instance->msix_vectors) { - irq_flags = PCI_IRQ_MSIX; - if (smp_affinity_enable) - irq_flags |= PCI_IRQ_AFFINITY; + if (instance->msix_vectors) + megasas_alloc_irq_vectors(instance); + + if (!instance->msix_vectors) { + rval = pci_alloc_irq_vectors(instance->pdev, 1, 1, + PCI_IRQ_INTX); + if (rval < 0) + goto fail_reenable_msix; } - rval = pci_alloc_irq_vectors(instance->pdev, 1, - instance->msix_vectors ? - instance->msix_vectors : 1, irq_flags); - if (rval < 0) - goto fail_reenable_msix; megasas_setup_reply_map(instance); @@ -7017,6 +7887,9 @@ megasas_resume(struct pci_dev *pdev) megasas_setup_irqs_ioapic(instance)) goto fail_init_mfi; + if (instance->adapter_type != MFI_SERIES) + megasas_setup_irq_poll(instance); + /* Re-launch SR-IOV heartbeat timer */ if (instance->requestorId) { if (!megasas_sriov_start_heartbeat(instance, 0)) @@ -7046,7 +7919,7 @@ megasas_resume(struct pci_dev *pdev) fail_start_watchdog: if (instance->requestorId && !instance->skip_heartbeat_timer_del) - del_timer_sync(&instance->sriov_heartbeat_timer); + timer_delete_sync(&instance->sriov_heartbeat_timer); fail_init_mfi: megasas_free_ctrl_dma_buffers(instance); megasas_free_ctrl_mem(instance); @@ -7056,14 +7929,8 @@ fail_reenable_msix: fail_set_dma_mask: fail_ready_state: - pci_disable_device(pdev); - return -ENODEV; } -#else -#define megasas_suspend NULL -#define megasas_resume NULL -#endif static inline int megasas_wait_for_adapter_operational(struct megasas_instance *instance) @@ -7104,15 +7971,19 @@ static void megasas_detach_one(struct pci_dev *pdev) struct Scsi_Host *host; struct megasas_instance *instance; struct fusion_context *fusion; - u32 pd_seq_map_sz; + size_t pd_seq_map_sz; instance = pci_get_drvdata(pdev); + + if (!instance) + return; + host = instance->host; fusion = instance->ctrl_context; /* Shutdown SR-IOV heartbeat timer */ if (instance->requestorId && !instance->skip_heartbeat_timer_del) - del_timer_sync(&instance->sriov_heartbeat_timer); + timer_delete_sync(&instance->sriov_heartbeat_timer); /* Stop the FW fault detection watchdog */ if (instance->adapter_type != MFI_SERIES) @@ -7172,9 +8043,9 @@ skip_firing_dcmds: if (instance->adapter_type != MFI_SERIES) { megasas_release_fusion(instance); - pd_seq_map_sz = sizeof(struct MR_PD_CFG_SEQ_NUM_SYNC) + - (sizeof(struct MR_PD_CFG_SEQ) * - (MAX_PHYSICAL_DEVICES - 1)); + pd_seq_map_sz = + struct_size_t(struct MR_PD_CFG_SEQ_NUM_SYNC, + seq, MAX_PHYSICAL_DEVICES); for (i = 0; i < 2 ; i++) { if (fusion->ld_map[i]) dma_free_coherent(&instance->pdev->dev, @@ -7220,6 +8091,8 @@ skip_firing_dcmds: megasas_free_ctrl_mem(instance); + megasas_destroy_debugfs(instance); + scsi_host_put(host); pci_disable_device(pdev); @@ -7227,12 +8100,15 @@ skip_firing_dcmds: /** * megasas_shutdown - Shutdown entry point - * @device: Generic device structure + * @pdev: PCI device structure */ static void megasas_shutdown(struct pci_dev *pdev) { struct megasas_instance *instance = pci_get_drvdata(pdev); + if (!instance) + return; + instance->unload = 1; if (megasas_wait_for_adapter_operational(instance)) @@ -7249,8 +8125,10 @@ skip_firing_dcmds: pci_free_irq_vectors(instance->pdev); } -/** +/* * megasas_mgmt_open - char node "open" entry point + * @inode: char node inode + * @filep: char node file */ static int megasas_mgmt_open(struct inode *inode, struct file *filep) { @@ -7263,8 +8141,11 @@ static int megasas_mgmt_open(struct inode *inode, struct file *filep) return 0; } -/** +/* * megasas_mgmt_fasync - Async notifier registration from applications + * @fd: char node file descriptor number + * @filep: char node file + * @mode: notifier on/off * * This function adds the calling process to a driver global queue. When an * event occurs, SIGIO will be sent to all processes in this queue. @@ -7290,9 +8171,11 @@ static int megasas_mgmt_fasync(int fd, struct file *filep, int mode) return rc; } -/** +/* * megasas_mgmt_poll - char node "poll" entry point - * */ + * @filep: char node file + * @wait: Events to poll for + */ static __poll_t megasas_mgmt_poll(struct file *file, poll_table *wait) { __poll_t mask; @@ -7350,7 +8233,8 @@ static int megasas_set_crash_dump_params_ioctl(struct megasas_cmd *cmd) /** * megasas_mgmt_fw_ioctl - Issues management ioctls to FW * @instance: Adapter soft state - * @argp: User's ioctl packet + * @user_ioc: User's ioctl packet + * @ioc: ioctl packet */ static int megasas_mgmt_fw_ioctl(struct megasas_instance *instance, @@ -7365,8 +8249,9 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance, int error = 0, i; void *sense = NULL; dma_addr_t sense_handle; - unsigned long *sense_ptr; + void *sense_ptr; u32 opcode = 0; + int ret = DCMD_SUCCESS; memset(kbuff_arr, 0, sizeof(kbuff_arr)); @@ -7378,7 +8263,9 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance, if ((ioc->frame.hdr.cmd >= MFI_CMD_OP_COUNT) || ((ioc->frame.hdr.cmd == MFI_CMD_NVME) && - !instance->support_nvme_passthru)) { + !instance->support_nvme_passthru) || + ((ioc->frame.hdr.cmd == MFI_CMD_TOOLBOX) && + !instance->support_pci_lane_margining)) { dev_err(&instance->pdev->dev, "Received invalid ioctl command 0x%x\n", ioc->frame.hdr.cmd); @@ -7414,10 +8301,13 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance, opcode = le32_to_cpu(cmd->frame->dcmd.opcode); if (opcode == MR_DCMD_CTRL_SHUTDOWN) { + mutex_lock(&instance->reset_mutex); if (megasas_get_ctrl_info(instance) != DCMD_SUCCESS) { megasas_return_cmd(instance, cmd); + mutex_unlock(&instance->reset_mutex); return -1; } + mutex_unlock(&instance->reset_mutex); } if (opcode == MR_DRIVER_SET_APP_CRASHDUMP_MODE) { @@ -7482,6 +8372,13 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance, } if (ioc->sense_len) { + /* make sure the pointer is part of the frame */ + if (ioc->sense_off > + (sizeof(union megasas_frame) - sizeof(__le64))) { + error = -EINVAL; + goto out; + } + sense = dma_alloc_coherent(&instance->pdev->dev, ioc->sense_len, &sense_handle, GFP_KERNEL); if (!sense) { @@ -7489,12 +8386,9 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance, goto out; } - sense_ptr = - (unsigned long *) ((unsigned long)cmd->frame + ioc->sense_off); - if (instance->consistent_mask_64bit) - *sense_ptr = cpu_to_le64(sense_handle); - else - *sense_ptr = cpu_to_le32(sense_handle); + /* always store 64 bits regardless of addressing */ + sense_ptr = (void *)cmd->frame + ioc->sense_off; + put_unaligned_le64(sense_handle, sense_ptr); } /* @@ -7502,13 +8396,18 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance, * cmd to the SCSI mid-layer */ cmd->sync_cmd = 1; - if (megasas_issue_blocked_cmd(instance, cmd, 0) == DCMD_NOT_FIRED) { + + ret = megasas_issue_blocked_cmd(instance, cmd, 0); + switch (ret) { + case DCMD_INIT: + case DCMD_BUSY: cmd->sync_cmd = 0; dev_err(&instance->pdev->dev, "return -EBUSY from %s %d cmd 0x%x opcode 0x%x cmd->cmd_status_drv 0x%x\n", - __func__, __LINE__, cmd->frame->hdr.cmd, opcode, - cmd->cmd_status_drv); - return -EBUSY; + __func__, __LINE__, cmd->frame->hdr.cmd, opcode, + cmd->cmd_status_drv); + error = -EBUSY; + goto out; } cmd->sync_cmd = 0; @@ -7533,16 +8432,19 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance, * copy out the sense */ if (ioc->sense_len) { + void __user *uptr; /* * sense_ptr points to the location that has the user * sense buffer address */ - sense_ptr = (unsigned long *) ((unsigned long)ioc->frame.raw + - ioc->sense_off); + sense_ptr = (void *)ioc->frame.raw + ioc->sense_off; + if (in_compat_syscall()) + uptr = compat_ptr(get_unaligned((compat_uptr_t *) + sense_ptr)); + else + uptr = get_unaligned((void __user **)sense_ptr); - if (copy_to_user((void __user *)((unsigned long) - get_unaligned((unsigned long *)sense_ptr)), - sense, ioc->sense_len)) { + if (copy_to_user(uptr, sense, ioc->sense_len)) { dev_err(&instance->pdev->dev, "Failed to copy out to user " "sense data\n"); error = -EFAULT; @@ -7585,6 +8487,38 @@ out: return error; } +static struct megasas_iocpacket * +megasas_compat_iocpacket_get_user(void __user *arg) +{ + struct megasas_iocpacket *ioc; + struct compat_megasas_iocpacket __user *cioc = arg; + size_t size; + int err = -EFAULT; + int i; + + ioc = kzalloc(sizeof(*ioc), GFP_KERNEL); + if (!ioc) + return ERR_PTR(-ENOMEM); + size = offsetof(struct megasas_iocpacket, frame) + sizeof(ioc->frame); + if (copy_from_user(ioc, arg, size)) + goto out; + + for (i = 0; i < MAX_IOCTL_SGE; i++) { + compat_uptr_t iov_base; + + if (get_user(iov_base, &cioc->sgl[i].iov_base) || + get_user(ioc->sgl[i].iov_len, &cioc->sgl[i].iov_len)) + goto out; + + ioc->sgl[i].iov_base = compat_ptr(iov_base); + } + + return ioc; +out: + kfree(ioc); + return ERR_PTR(err); +} + static int megasas_mgmt_ioctl_fw(struct file *file, unsigned long arg) { struct megasas_iocpacket __user *user_ioc = @@ -7593,7 +8527,11 @@ static int megasas_mgmt_ioctl_fw(struct file *file, unsigned long arg) struct megasas_instance *instance; int error; - ioc = memdup_user(user_ioc, sizeof(*ioc)); + if (in_compat_syscall()) + ioc = megasas_compat_iocpacket_get_user(user_ioc); + else + ioc = memdup_user(user_ioc, sizeof(struct megasas_iocpacket)); + if (IS_ERR(ioc)) return PTR_ERR(ioc); @@ -7679,6 +8617,9 @@ static int megasas_mgmt_ioctl_aen(struct file *file, unsigned long arg) /** * megasas_mgmt_ioctl - char node ioctl entry point + * @file: char device file pointer + * @cmd: ioctl command + * @arg: ioctl command arguments address */ static long megasas_mgmt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) @@ -7695,78 +8636,13 @@ megasas_mgmt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) } #ifdef CONFIG_COMPAT -static int megasas_mgmt_compat_ioctl_fw(struct file *file, unsigned long arg) -{ - struct compat_megasas_iocpacket __user *cioc = - (struct compat_megasas_iocpacket __user *)arg; - struct megasas_iocpacket __user *ioc = - compat_alloc_user_space(sizeof(struct megasas_iocpacket)); - int i; - int error = 0; - compat_uptr_t ptr; - u32 local_sense_off; - u32 local_sense_len; - u32 user_sense_off; - - if (clear_user(ioc, sizeof(*ioc))) - return -EFAULT; - - if (copy_in_user(&ioc->host_no, &cioc->host_no, sizeof(u16)) || - copy_in_user(&ioc->sgl_off, &cioc->sgl_off, sizeof(u32)) || - copy_in_user(&ioc->sense_off, &cioc->sense_off, sizeof(u32)) || - copy_in_user(&ioc->sense_len, &cioc->sense_len, sizeof(u32)) || - copy_in_user(ioc->frame.raw, cioc->frame.raw, 128) || - copy_in_user(&ioc->sge_count, &cioc->sge_count, sizeof(u32))) - return -EFAULT; - - /* - * The sense_ptr is used in megasas_mgmt_fw_ioctl only when - * sense_len is not null, so prepare the 64bit value under - * the same condition. - */ - if (get_user(local_sense_off, &ioc->sense_off) || - get_user(local_sense_len, &ioc->sense_len) || - get_user(user_sense_off, &cioc->sense_off)) - return -EFAULT; - - if (local_sense_off != user_sense_off) - return -EINVAL; - - if (local_sense_len) { - void __user **sense_ioc_ptr = - (void __user **)((u8 *)((unsigned long)&ioc->frame.raw) + local_sense_off); - compat_uptr_t *sense_cioc_ptr = - (compat_uptr_t *)(((unsigned long)&cioc->frame.raw) + user_sense_off); - if (get_user(ptr, sense_cioc_ptr) || - put_user(compat_ptr(ptr), sense_ioc_ptr)) - return -EFAULT; - } - - for (i = 0; i < MAX_IOCTL_SGE; i++) { - if (get_user(ptr, &cioc->sgl[i].iov_base) || - put_user(compat_ptr(ptr), &ioc->sgl[i].iov_base) || - copy_in_user(&ioc->sgl[i].iov_len, - &cioc->sgl[i].iov_len, sizeof(compat_size_t))) - return -EFAULT; - } - - error = megasas_mgmt_ioctl_fw(file, (unsigned long)ioc); - - if (copy_in_user(&cioc->frame.hdr.cmd_status, - &ioc->frame.hdr.cmd_status, sizeof(u8))) { - printk(KERN_DEBUG "megasas: error copy_in_user cmd_status\n"); - return -EFAULT; - } - return error; -} - static long megasas_mgmt_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { switch (cmd) { case MEGASAS_IOC_FIRMWARE32: - return megasas_mgmt_compat_ioctl_fw(file, arg); + return megasas_mgmt_ioctl_fw(file, arg); case MEGASAS_IOC_GET_AEN: return megasas_mgmt_ioctl_aen(file, arg); } @@ -7790,6 +8666,8 @@ static const struct file_operations megasas_mgmt_fops = { .llseek = noop_llseek, }; +static SIMPLE_DEV_PM_OPS(megasas_pm_ops, megasas_suspend, megasas_resume); + /* * PCI hotplug support registration structure */ @@ -7799,8 +8677,7 @@ static struct pci_driver megasas_pci_driver = { .id_table = megasas_pci_table, .probe = megasas_probe_one, .remove = megasas_detach_one, - .suspend = megasas_suspend, - .resume = megasas_resume, + .driver.pm = &megasas_pm_ops, .shutdown = megasas_shutdown, }; @@ -7859,6 +8736,14 @@ support_nvme_encapsulation_show(struct device_driver *dd, char *buf) static DRIVER_ATTR_RO(support_nvme_encapsulation); +static ssize_t +support_pci_lane_margining_show(struct device_driver *dd, char *buf) +{ + return sprintf(buf, "%u\n", support_pci_lane_margining); +} + +static DRIVER_ATTR_RO(support_pci_lane_margining); + static inline void megasas_remove_scsi_device(struct scsi_device *sdev) { sdev_printk(KERN_INFO, sdev, "SCSI device is removed\n"); @@ -7866,6 +8751,131 @@ static inline void megasas_remove_scsi_device(struct scsi_device *sdev) scsi_device_put(sdev); } +/** + * megasas_update_device_list - Update the PD and LD device list from FW + * after an AEN event notification + * @instance: Adapter soft state + * @event_type: Indicates type of event (PD or LD event) + * + * @return: Success or failure + * + * Issue DCMDs to Firmware to update the internal device list in driver. + * Based on the FW support, driver sends the HOST_DEVICE_LIST or combination + * of PD_LIST/LD_LIST_QUERY DCMDs to get the device list. + */ +static +int megasas_update_device_list(struct megasas_instance *instance, + int event_type) +{ + int dcmd_ret; + + if (instance->enable_fw_dev_list) { + return megasas_host_device_list_query(instance, false); + } else { + if (event_type & SCAN_PD_CHANNEL) { + dcmd_ret = megasas_get_pd_list(instance); + if (dcmd_ret != DCMD_SUCCESS) + return dcmd_ret; + } + + if (event_type & SCAN_VD_CHANNEL) { + if (!instance->requestorId || + megasas_get_ld_vf_affiliation(instance, 0)) { + return megasas_ld_list_query(instance, + MR_LD_QUERY_TYPE_EXPOSED_TO_HOST); + } + } + } + return DCMD_SUCCESS; +} + +/** + * megasas_add_remove_devices - Add/remove devices to SCSI mid-layer + * after an AEN event notification + * @instance: Adapter soft state + * @scan_type: Indicates type of devices (PD/LD) to add + * @return void + */ +static +void megasas_add_remove_devices(struct megasas_instance *instance, + int scan_type) +{ + int i, j; + u16 pd_index = 0; + u16 ld_index = 0; + u16 channel = 0, id = 0; + struct Scsi_Host *host; + struct scsi_device *sdev1; + struct MR_HOST_DEVICE_LIST *targetid_list = NULL; + struct MR_HOST_DEVICE_LIST_ENTRY *targetid_entry = NULL; + + host = instance->host; + + if (instance->enable_fw_dev_list) { + targetid_list = instance->host_device_list_buf; + for (i = 0; i < targetid_list->count; i++) { + targetid_entry = &targetid_list->host_device_list[i]; + if (targetid_entry->flags.u.bits.is_sys_pd) { + channel = le16_to_cpu(targetid_entry->target_id) / + MEGASAS_MAX_DEV_PER_CHANNEL; + id = le16_to_cpu(targetid_entry->target_id) % + MEGASAS_MAX_DEV_PER_CHANNEL; + } else { + channel = MEGASAS_MAX_PD_CHANNELS + + (le16_to_cpu(targetid_entry->target_id) / + MEGASAS_MAX_DEV_PER_CHANNEL); + id = le16_to_cpu(targetid_entry->target_id) % + MEGASAS_MAX_DEV_PER_CHANNEL; + } + sdev1 = scsi_device_lookup(host, channel, id, 0); + if (!sdev1) { + scsi_add_device(host, channel, id, 0); + } else { + scsi_device_put(sdev1); + } + } + } + + if (scan_type & SCAN_PD_CHANNEL) { + for (i = 0; i < MEGASAS_MAX_PD_CHANNELS; i++) { + for (j = 0; j < MEGASAS_MAX_DEV_PER_CHANNEL; j++) { + pd_index = i * MEGASAS_MAX_DEV_PER_CHANNEL + j; + sdev1 = scsi_device_lookup(host, i, j, 0); + if (instance->pd_list[pd_index].driveState == + MR_PD_STATE_SYSTEM) { + if (!sdev1) + scsi_add_device(host, i, j, 0); + else + scsi_device_put(sdev1); + } else { + if (sdev1) + megasas_remove_scsi_device(sdev1); + } + } + } + } + + if (scan_type & SCAN_VD_CHANNEL) { + for (i = 0; i < MEGASAS_MAX_LD_CHANNELS; i++) { + for (j = 0; j < MEGASAS_MAX_DEV_PER_CHANNEL; j++) { + ld_index = (i * MEGASAS_MAX_DEV_PER_CHANNEL) + j; + sdev1 = scsi_device_lookup(host, + MEGASAS_MAX_PD_CHANNELS + i, j, 0); + if (instance->ld_ids[ld_index] != 0xff) { + if (!sdev1) + scsi_add_device(host, MEGASAS_MAX_PD_CHANNELS + i, j, 0); + else + scsi_device_put(sdev1); + } else { + if (sdev1) + megasas_remove_scsi_device(sdev1); + } + } + } + } + +} + static void megasas_aen_polling(struct work_struct *work) { @@ -7873,14 +8883,12 @@ megasas_aen_polling(struct work_struct *work) container_of(work, struct megasas_aen_event, hotplug_work.work); struct megasas_instance *instance = ev->instance; union megasas_evt_class_locale class_locale; - struct Scsi_Host *host; - struct scsi_device *sdev1; - u16 pd_index = 0; - u16 ld_index = 0; - int i, j, doscan = 0; - u32 seq_num, wait_time = MEGASAS_RESET_WAIT_TIME; + int event_type = 0; + u32 seq_num; + u16 ld_target_id; int error; u8 dcmd_ret = DCMD_SUCCESS; + struct scsi_device *sdev1; if (!instance) { printk(KERN_ERR "invalid instance!\n"); @@ -7888,15 +8896,10 @@ megasas_aen_polling(struct work_struct *work) return; } - /* Adjust event workqueue thread wait time for VF mode */ - if (instance->requestorId) - wait_time = MEGASAS_ROUTINE_WAIT_TIME_VF; - /* Don't run the event workqueue thread if OCR is running */ mutex_lock(&instance->reset_mutex); instance->ev = NULL; - host = instance->host; if (instance->evt_detail) { megasas_decode_evt(instance); @@ -7904,40 +8907,34 @@ megasas_aen_polling(struct work_struct *work) case MR_EVT_PD_INSERTED: case MR_EVT_PD_REMOVED: - dcmd_ret = megasas_get_pd_list(instance); - if (dcmd_ret == DCMD_SUCCESS) - doscan = SCAN_PD_CHANNEL; + event_type = SCAN_PD_CHANNEL; break; case MR_EVT_LD_OFFLINE: - case MR_EVT_CFG_CLEARED: case MR_EVT_LD_DELETED: - case MR_EVT_LD_CREATED: - if (!instance->requestorId || - (instance->requestorId && megasas_get_ld_vf_affiliation(instance, 0))) - dcmd_ret = megasas_ld_list_query(instance, MR_LD_QUERY_TYPE_EXPOSED_TO_HOST); - - if (dcmd_ret == DCMD_SUCCESS) - doscan = SCAN_VD_CHANNEL; + ld_target_id = instance->evt_detail->args.ld.target_id; + sdev1 = scsi_device_lookup(instance->host, + MEGASAS_MAX_PD_CHANNELS + + (ld_target_id / MEGASAS_MAX_DEV_PER_CHANNEL), + (ld_target_id % MEGASAS_MAX_DEV_PER_CHANNEL), + 0); + if (sdev1) { + mutex_unlock(&instance->reset_mutex); + megasas_remove_scsi_device(sdev1); + mutex_lock(&instance->reset_mutex); + } + event_type = SCAN_VD_CHANNEL; + break; + case MR_EVT_LD_CREATED: + event_type = SCAN_VD_CHANNEL; break; + case MR_EVT_CFG_CLEARED: case MR_EVT_CTRL_HOST_BUS_SCAN_REQUESTED: case MR_EVT_FOREIGN_CFG_IMPORTED: case MR_EVT_LD_STATE_CHANGE: - dcmd_ret = megasas_get_pd_list(instance); - - if (dcmd_ret != DCMD_SUCCESS) - break; - - if (!instance->requestorId || - (instance->requestorId && megasas_get_ld_vf_affiliation(instance, 0))) - dcmd_ret = megasas_ld_list_query(instance, MR_LD_QUERY_TYPE_EXPOSED_TO_HOST); - - if (dcmd_ret != DCMD_SUCCESS) - break; - - doscan = SCAN_VD_CHANNEL | SCAN_PD_CHANNEL; + event_type = SCAN_PD_CHANNEL | SCAN_VD_CHANNEL; dev_info(&instance->pdev->dev, "scanning for scsi%d...\n", instance->host->host_no); break; @@ -7953,7 +8950,7 @@ megasas_aen_polling(struct work_struct *work) } break; default: - doscan = 0; + event_type = 0; break; } } else { @@ -7963,44 +8960,13 @@ megasas_aen_polling(struct work_struct *work) return; } - mutex_unlock(&instance->reset_mutex); + if (event_type) + dcmd_ret = megasas_update_device_list(instance, event_type); - if (doscan & SCAN_PD_CHANNEL) { - for (i = 0; i < MEGASAS_MAX_PD_CHANNELS; i++) { - for (j = 0; j < MEGASAS_MAX_DEV_PER_CHANNEL; j++) { - pd_index = i*MEGASAS_MAX_DEV_PER_CHANNEL + j; - sdev1 = scsi_device_lookup(host, i, j, 0); - if (instance->pd_list[pd_index].driveState == - MR_PD_STATE_SYSTEM) { - if (!sdev1) - scsi_add_device(host, i, j, 0); - else - scsi_device_put(sdev1); - } else { - if (sdev1) - megasas_remove_scsi_device(sdev1); - } - } - } - } + mutex_unlock(&instance->reset_mutex); - if (doscan & SCAN_VD_CHANNEL) { - for (i = 0; i < MEGASAS_MAX_LD_CHANNELS; i++) { - for (j = 0; j < MEGASAS_MAX_DEV_PER_CHANNEL; j++) { - ld_index = (i * MEGASAS_MAX_DEV_PER_CHANNEL) + j; - sdev1 = scsi_device_lookup(host, MEGASAS_MAX_PD_CHANNELS + i, j, 0); - if (instance->ld_ids[ld_index] != 0xff) { - if (!sdev1) - scsi_add_device(host, MEGASAS_MAX_PD_CHANNELS + i, j, 0); - else - scsi_device_put(sdev1); - } else { - if (sdev1) - megasas_remove_scsi_device(sdev1); - } - } - } - } + if (event_type && dcmd_ret == DCMD_SUCCESS) + megasas_add_remove_devices(instance, event_type); if (dcmd_ret == DCMD_SUCCESS) seq_num = le32_to_cpu(instance->evt_detail->seq_num) + 1; @@ -8043,6 +9009,7 @@ static int __init megasas_init(void) msix_vectors = 1; rdpq_enable = 0; dual_qdepth_disable = 1; + poll_queues = 0; } /* @@ -8050,11 +9017,11 @@ static int __init megasas_init(void) */ pr_info("megasas: %s\n", MEGASAS_VERSION); - spin_lock_init(&poll_aen_lock); - + megasas_dbg_lvl = 0; support_poll_for_event = 2; support_device_change = 1; support_nvme_encapsulation = true; + support_pci_lane_margining = true; memset(&megasas_mgmt_info, 0, sizeof(megasas_mgmt_info)); @@ -8070,6 +9037,8 @@ static int __init megasas_init(void) megasas_mgmt_majorno = rval; + megasas_init_debugfs(); + /* * Register ourselves as PCI hotplug module */ @@ -8080,6 +9049,12 @@ static int __init megasas_init(void) goto err_pcidrv; } + if ((event_log_level < MFI_EVT_CLASS_DEBUG) || + (event_log_level > MFI_EVT_CLASS_DEAD)) { + pr_warn("megaraid_sas: provided event log level is out of range, setting it to default 2(CLASS_CRITICAL), permissible range is: -2 to 4\n"); + event_log_level = MFI_EVT_CLASS_CRITICAL; + } + rval = driver_create_file(&megasas_pci_driver.driver, &driver_attr_version); if (rval) @@ -8109,8 +9084,17 @@ static int __init megasas_init(void) if (rval) goto err_dcf_support_nvme_encapsulation; + rval = driver_create_file(&megasas_pci_driver.driver, + &driver_attr_support_pci_lane_margining); + if (rval) + goto err_dcf_support_pci_lane_margining; + return rval; +err_dcf_support_pci_lane_margining: + driver_remove_file(&megasas_pci_driver.driver, + &driver_attr_support_nvme_encapsulation); + err_dcf_support_nvme_encapsulation: driver_remove_file(&megasas_pci_driver.driver, &driver_attr_support_device_change); @@ -8129,6 +9113,7 @@ err_dcf_rel_date: err_dcf_attr_ver: pci_unregister_driver(&megasas_pci_driver); err_pcidrv: + megasas_exit_debugfs(); unregister_chrdev(megasas_mgmt_majorno, "megaraid_sas_ioctl"); return rval; } @@ -8149,8 +9134,11 @@ static void __exit megasas_exit(void) driver_remove_file(&megasas_pci_driver.driver, &driver_attr_version); driver_remove_file(&megasas_pci_driver.driver, &driver_attr_support_nvme_encapsulation); + driver_remove_file(&megasas_pci_driver.driver, + &driver_attr_support_pci_lane_margining); pci_unregister_driver(&megasas_pci_driver); + megasas_exit_debugfs(); unregister_chrdev(megasas_mgmt_majorno, "megaraid_sas_ioctl"); } |
