From 9d77db3c730780c8ef5ccd4b66403ff5675cfe4e Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Mon, 13 May 2024 10:30:14 +0000 Subject: [PATCH] modify sin led gpio --- kernel/drivers/scsi/megaraid/megaraid_sas_base.c | 2263 ++++++++++++++++++++++++++++++++++++++++++++--------------- 1 files changed, 1,687 insertions(+), 576 deletions(-) diff --git a/kernel/drivers/scsi/megaraid/megaraid_sas_base.c b/kernel/drivers/scsi/megaraid/megaraid_sas_base.c index 8877a21..ec9a19d 100644 --- a/kernel/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/kernel/drivers/scsi/megaraid/megaraid_sas_base.c @@ -1,34 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Linux MegaRAID driver for SAS based RAID controllers * * Copyright (c) 2003-2013 LSI Corporation - * Copyright (c) 2013-2014 Avago Technologies + * 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: Avago Technologies + * Authors: Broadcom Inc. * Sreenivas Bagalkote * Sumant Patro * Bo Yang * Adam Radford - * Kashyap Desai <kashyap.desai@avagotech.com> - * Sumit Saxena <sumit.saxena@avagotech.com> + * Kashyap Desai <kashyap.desai@broadcom.com> + * Sumit Saxena <sumit.saxena@broadcom.com> * - * Send feedback to: megaraidlinux.pdl@avagotech.com - * - * Mail to: Avago Technologies, 350 West Trimble Road, Building 90, - * San Jose, California 95131 + * Send feedback to: megaraidlinux.pdl@broadcom.com */ #include <linux/kernel.h> @@ -50,12 +36,14 @@ #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" @@ -64,52 +52,71 @@ * 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_PARM_DESC(resetwaittime, "Wait time in seconds after I/O timeout " - "before resetting adapter. Default: 180"); +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); -MODULE_PARM_DESC(rdpq_enable, " Allocate reply queue in chunks for large queue depth enable/disable Default: disable(0)"); +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."); + +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'" + ); + +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)"); + +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"); MODULE_LICENSE("GPL"); MODULE_VERSION(MEGASAS_VERSION); -MODULE_AUTHOR("megaraidlinux.pdl@avagotech.com"); -MODULE_DESCRIPTION("Avago MegaRAID SAS Driver"); +MODULE_AUTHOR("megaraidlinux.pdl@broadcom.com"); +MODULE_DESCRIPTION("Broadcom MegaRAID SAS Driver"); int megasas_transition_to_ready(struct megasas_instance *instance, int ocr); static int megasas_get_pd_list(struct megasas_instance *instance); @@ -120,6 +127,8 @@ 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 @@ -165,6 +174,14 @@ {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_TOMCAT)}, {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_VENTURA_4PORT)}, {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_CRUSADER_4PORT)}, + {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_AERO_10E1)}, + {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)}, {} }; @@ -181,15 +198,18 @@ 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 spinlock_t poll_aen_lock; + +extern struct dentry *megasas_debugfs_root; void megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, u8 alt_status); static u32 -megasas_read_fw_status_reg_gen2(struct megasas_register_set __iomem *regs); +megasas_read_fw_status_reg_gen2(struct megasas_instance *instance); static int megasas_adp_reset_gen2(struct megasas_instance *instance, struct megasas_register_set __iomem *reg_set); @@ -219,6 +239,28 @@ static inline void megasas_init_ctrl_params(struct megasas_instance *instance); +u32 megasas_readl(struct megasas_instance *instance, + const volatile void __iomem *addr) +{ + u32 i = 0, ret_val; + /* + * Due to a HW errata in Aero controllers, reads to certain + * 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. + */ + if (instance->adapter_type == AERO_SERIES) { + do { + ret_val = readl(addr); + i++; + } while (ret_val == 0 && i < 3); + return ret_val; + } else { + return readl(addr); + } +} + /** * megasas_set_dma_settings - Populate DMA address, length and flags for DCMDs * @instance: Adapter soft state @@ -244,7 +286,7 @@ } } -void +static void megasas_issue_dcmd(struct megasas_instance *instance, struct megasas_cmd *cmd) { instance->instancet->fire_cmd(instance, @@ -368,23 +410,35 @@ 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) @@ -400,7 +454,7 @@ /** * 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) @@ -416,22 +470,24 @@ /** * 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_register_set __iomem * regs) +megasas_read_fw_status_reg_xscale(struct megasas_instance *instance) { - return readl(&(regs)->outbound_msg_0); + return readl(&instance->reg_set->outbound_msg_0); } /** * megasas_clear_interrupt_xscale - Check & clear interrupt - * @regs: MFI register set + * @instance: Adapter soft state */ static int -megasas_clear_intr_xscale(struct megasas_register_set __iomem * regs) +megasas_clear_intr_xscale(struct megasas_instance *instance) { u32 status; u32 mfiStatus = 0; + struct megasas_register_set __iomem *regs; + regs = instance->reg_set; /* * Check if it is our interrupt @@ -457,9 +513,10 @@ /** * 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, @@ -477,7 +534,8 @@ /** * 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, @@ -518,7 +576,8 @@ /** * 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, @@ -547,19 +606,19 @@ .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) @@ -577,7 +636,7 @@ /** * 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) @@ -593,22 +652,24 @@ /** * 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_register_set __iomem * regs) +megasas_read_fw_status_reg_ppc(struct megasas_instance *instance) { - return readl(&(regs)->outbound_scratch_pad); + return readl(&instance->reg_set->outbound_scratch_pad_0); } /** * megasas_clear_interrupt_ppc - Check & clear interrupt - * @regs: MFI register set + * @instance: Adapter soft state */ static int -megasas_clear_intr_ppc(struct megasas_register_set __iomem * regs) +megasas_clear_intr_ppc(struct megasas_instance *instance) { u32 status, mfiStatus = 0; + struct megasas_register_set __iomem *regs; + regs = instance->reg_set; /* * Check if it is our interrupt @@ -634,9 +695,10 @@ /** * 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, @@ -654,7 +716,8 @@ /** * 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, @@ -684,7 +747,7 @@ /** * 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) @@ -702,7 +765,7 @@ /** * 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) @@ -718,23 +781,25 @@ /** * 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_register_set __iomem *regs) +megasas_read_fw_status_reg_skinny(struct megasas_instance *instance) { - return readl(&(regs)->outbound_scratch_pad); + return readl(&instance->reg_set->outbound_scratch_pad_0); } /** * megasas_clear_interrupt_skinny - Check & clear interrupt - * @regs: MFI register set + * @instance: Adapter soft state */ static int -megasas_clear_intr_skinny(struct megasas_register_set __iomem *regs) +megasas_clear_intr_skinny(struct megasas_instance *instance) { u32 status; u32 mfiStatus = 0; + struct megasas_register_set __iomem *regs; + regs = instance->reg_set; /* * Check if it is our interrupt @@ -748,7 +813,7 @@ /* * Check if it is our interrupt */ - if ((megasas_read_fw_status_reg_skinny(regs) & MFI_STATE_MASK) == + if ((megasas_read_fw_status_reg_skinny(instance) & MFI_STATE_MASK) == MFI_STATE_FAULT) { mfiStatus = MFI_INTR_FLAG_FIRMWARE_STATE_CHANGE; } else @@ -769,9 +834,10 @@ /** * 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, @@ -786,13 +852,13 @@ &(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, @@ -821,14 +887,14 @@ }; -/** -* 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) @@ -847,7 +913,7 @@ /** * 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) @@ -863,23 +929,25 @@ /** * 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_register_set __iomem *regs) +megasas_read_fw_status_reg_gen2(struct megasas_instance *instance) { - return readl(&(regs)->outbound_scratch_pad); + return readl(&instance->reg_set->outbound_scratch_pad_0); } /** * megasas_clear_interrupt_gen2 - Check & clear interrupt - * @regs: MFI register set + * @instance: Adapter soft state */ static int -megasas_clear_intr_gen2(struct megasas_register_set __iomem *regs) +megasas_clear_intr_gen2(struct megasas_instance *instance) { u32 status; u32 mfiStatus = 0; + struct megasas_register_set __iomem *regs; + regs = instance->reg_set; /* * Check if it is our interrupt @@ -904,11 +972,13 @@ 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, @@ -926,7 +996,8 @@ /** * 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, @@ -986,7 +1057,8 @@ /** * 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, @@ -1014,10 +1086,10 @@ .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) @@ -1042,7 +1114,7 @@ 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); @@ -1066,30 +1138,30 @@ 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; } /** @@ -1110,6 +1182,7 @@ struct megasas_cmd *cmd; struct megasas_abort_frame *abort_fr; int ret = 0; + u32 opcode; cmd = megasas_get_cmd(instance); @@ -1131,33 +1204,34 @@ 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; } /** @@ -1330,11 +1404,11 @@ device_id = MEGASAS_DEV_INDEX(scp); pthru = (struct megasas_pthru_frame *)cmd->frame; - if (scp->sc_data_direction == PCI_DMA_TODEVICE) + if (scp->sc_data_direction == DMA_TO_DEVICE) flags = MFI_FRAME_DIR_WRITE; - else if (scp->sc_data_direction == PCI_DMA_FROMDEVICE) + else if (scp->sc_data_direction == DMA_FROM_DEVICE) flags = MFI_FRAME_DIR_READ; - else if (scp->sc_data_direction == PCI_DMA_NONE) + else if (scp->sc_data_direction == DMA_NONE) flags = MFI_FRAME_DIR_NONE; if (instance->flag_ieee == 1) { @@ -1428,9 +1502,9 @@ device_id = MEGASAS_DEV_INDEX(scp); ldio = (struct megasas_io_frame *)cmd->frame; - if (scp->sc_data_direction == PCI_DMA_TODEVICE) + if (scp->sc_data_direction == DMA_TO_DEVICE) flags = MFI_FRAME_DIR_WRITE; - else if (scp->sc_data_direction == PCI_DMA_FROMDEVICE) + else if (scp->sc_data_direction == DMA_FROM_DEVICE) flags = MFI_FRAME_DIR_READ; if (instance->flag_ieee == 1) { @@ -1550,7 +1624,7 @@ /** * 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) @@ -1690,14 +1764,15 @@ /** * 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; @@ -1724,17 +1799,21 @@ } } - 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); 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; + scmd->scsi_done(scmd); + return 0; + } } if (atomic_read(&instance->adprecovery) != MEGASAS_HBA_OPERATIONAL) @@ -1826,6 +1905,10 @@ 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; @@ -1884,6 +1967,51 @@ blk_queue_virt_boundary(sdev->request_queue, mr_nvme_pg_size - 1); } +/* + * 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_fw_assisted_qd(struct scsi_device *sdev, + bool is_target_prop) +{ + u8 interface_type; + u32 device_qd = MEGASAS_DEFAULT_CMD_PER_LUN; + u32 tgt_device_qd; + struct megasas_instance *instance; + struct MR_PRIV_DEVICE *mr_device_priv_data; + + instance = megasas_lookup_instance(sdev->host->host_no); + mr_device_priv_data = sdev->hostdata; + interface_type = mr_device_priv_data->interface_type; + + switch (interface_type) { + case SAS_PD: + device_qd = MEGASAS_SAS_QD; + break; + case SATA_PD: + device_qd = MEGASAS_SATA_QD; + break; + case NVME_PD: + device_qd = MEGASAS_NVME_QD; + break; + } + + if (is_target_prop) { + tgt_device_qd = le32_to_cpu(instance->tgt_prop->device_qdepth); + if (tgt_device_qd) + device_qd = min(instance->host->can_queue, + (int)tgt_device_qd); + } + + 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 - @@ -1900,54 +2028,26 @@ static void megasas_set_static_target_properties(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; instance = megasas_lookup_instance(sdev->host->host_no); - 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; - break; - case SATA_PD: - device_qd = MEGASAS_SATA_QD; - break; - case NVME_PD: - device_qd = MEGASAS_NVME_QD; - break; - } - - 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 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, (max_io_size_kb << 10)); - scsi_change_queue_depth(sdev, device_qd); - + megasas_set_fw_assisted_qd(sdev, is_target_prop); } @@ -1993,7 +2093,7 @@ static int megasas_slave_alloc(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; @@ -2011,6 +2111,9 @@ 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: @@ -2018,6 +2121,14 @@ 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, @@ -2027,6 +2138,23 @@ static void megasas_slave_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; } @@ -2072,6 +2200,12 @@ 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 */ @@ -2079,9 +2213,11 @@ if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) || (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY) || (instance->adapter_type != MFI_SERIES)) { - writel(MFI_STOP_ADP, &instance->reg_set->doorbell); - /* Flush */ - readl(&instance->reg_set->doorbell); + if (!instance->requestorId) { + writel(MFI_STOP_ADP, &instance->reg_set->doorbell); + /* Flush */ + readl(&instance->reg_set->doorbell); + } if (instance->requestorId && instance->peerIsPresent) memset(instance->ld_ids, 0xff, MEGASAS_MAX_LD_IDS); } else { @@ -2191,7 +2327,7 @@ 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) || @@ -2240,9 +2376,9 @@ sizeof(struct MR_LD_VF_AFFILIATION_111)); else { new_affiliation_111 = - pci_zalloc_consistent(instance->pdev, - sizeof(struct MR_LD_VF_AFFILIATION_111), - &new_affiliation_111_h); + dma_alloc_coherent(&instance->pdev->dev, + sizeof(struct MR_LD_VF_AFFILIATION_111), + &new_affiliation_111_h, GFP_KERNEL); if (!new_affiliation_111) { dev_printk(KERN_DEBUG, &instance->pdev->dev, "SR-IOV: Couldn't allocate " "memory for new affiliation for scsi%d\n", @@ -2302,7 +2438,7 @@ } out: if (new_affiliation_111) { - pci_free_consistent(instance->pdev, + dma_free_coherent(&instance->pdev->dev, sizeof(struct MR_LD_VF_AFFILIATION_111), new_affiliation_111, new_affiliation_111_h); @@ -2347,10 +2483,9 @@ sizeof(struct MR_LD_VF_AFFILIATION)); else { new_affiliation = - pci_zalloc_consistent(instance->pdev, - (MAX_LOGICAL_DRIVES + 1) * - sizeof(struct MR_LD_VF_AFFILIATION), - &new_affiliation_h); + dma_alloc_coherent(&instance->pdev->dev, + (MAX_LOGICAL_DRIVES + 1) * sizeof(struct MR_LD_VF_AFFILIATION), + &new_affiliation_h, GFP_KERNEL); if (!new_affiliation) { dev_printk(KERN_DEBUG, &instance->pdev->dev, "SR-IOV: Couldn't allocate " "memory for new affiliation for scsi%d\n", @@ -2470,7 +2605,7 @@ } if (new_affiliation) - pci_free_consistent(instance->pdev, + dma_free_coherent(&instance->pdev->dev, (MAX_LOGICAL_DRIVES + 1) * sizeof(struct MR_LD_VF_AFFILIATION), new_affiliation, new_affiliation_h); @@ -2513,9 +2648,10 @@ if (initial) { instance->hb_host_mem = - pci_zalloc_consistent(instance->pdev, - sizeof(struct MR_CTRL_HB_HOST_MEM), - &instance->hb_host_mem_h); + dma_alloc_coherent(&instance->pdev->dev, + sizeof(struct MR_CTRL_HB_HOST_MEM), + &instance->hb_host_mem_h, + GFP_KERNEL); if (!instance->hb_host_mem) { dev_printk(KERN_DEBUG, &instance->pdev->dev, "SR-IOV: Couldn't allocate" " memory for heartbeat host memory for scsi%d\n", @@ -2646,7 +2782,7 @@ "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); @@ -2682,7 +2818,7 @@ i = 0; outstanding = atomic_read(&instance->fw_outstanding); - fw_state = instance->instancet->read_fw_status_reg(instance->reg_set) & MFI_STATE_MASK; + fw_state = instance->instancet->read_fw_status_reg(instance) & MFI_STATE_MASK; if ((!outstanding && (fw_state == MFI_STATE_OPERATIONAL))) goto no_outstanding; @@ -2692,7 +2828,7 @@ 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; @@ -2711,7 +2847,7 @@ outstanding = atomic_read(&instance->fw_outstanding); - fw_state = instance->instancet->read_fw_status_reg(instance->reg_set) & MFI_STATE_MASK; + fw_state = instance->instancet->read_fw_status_reg(instance) & MFI_STATE_MASK; if ((!outstanding && (fw_state == MFI_STATE_OPERATIONAL))) goto no_outstanding; } @@ -2802,25 +2938,110 @@ } /** - * 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; - 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(__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("\n"); + 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; + union MEGASAS_REQUEST_DESCRIPTOR_UNION *req_desc; + struct megasas_instance *instance; + + cmd = (struct megasas_cmd_fusion *)scmd->SCp.ptr; + 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; + + 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])); + } + 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) { @@ -2830,24 +3051,20 @@ 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); } @@ -2997,56 +3214,51 @@ } 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; } @@ -3058,13 +3270,13 @@ 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); @@ -3076,14 +3288,13 @@ } 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; @@ -3097,9 +3308,9 @@ 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"); @@ -3111,7 +3322,7 @@ } 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); @@ -3122,14 +3333,14 @@ } 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); @@ -3139,7 +3350,7 @@ } 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); @@ -3148,26 +3359,91 @@ 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); +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; -struct device_attribute *megaraid_host_attrs[] = { + 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 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, + &dev_attr_enable_sdev_max_qd, + &dev_attr_dump_system_regs, + &dev_attr_raid_map_id, NULL, }; @@ -3189,9 +3465,8 @@ .eh_timed_out = megasas_reset_timer, .shost_attrs = megaraid_host_attrs, .bios_param = megasas_bios_param, - .use_clustering = ENABLE_CLUSTERING, .change_queue_depth = scsi_change_queue_depth, - .no_write_same = 1, + .max_segment_size = 0xffffffff, }; /** @@ -3207,7 +3482,11 @@ 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); } @@ -3226,8 +3505,24 @@ { 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; + } } } @@ -3281,6 +3576,7 @@ megasas_complete_int_cmd(instance, cmd); break; } + fallthrough; case MFI_CMD_LD_READ: case MFI_CMD_LD_WRITE: @@ -3351,6 +3647,7 @@ case MFI_CMD_SMP: case MFI_CMD_STP: case MFI_CMD_NVME: + case MFI_CMD_TOOLBOX: megasas_complete_int_cmd(instance, cmd); break; @@ -3391,9 +3688,13 @@ 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 || @@ -3500,7 +3801,7 @@ 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); @@ -3538,7 +3839,7 @@ 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 @@ -3546,7 +3847,7 @@ * 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) { @@ -3668,9 +3969,8 @@ return IRQ_HANDLED; } - if ((mfiStatus = instance->instancet->clear_intr( - instance->reg_set) - ) == 0) { + mfiStatus = instance->instancet->clear_intr(instance); + if (mfiStatus == 0) { /* Hardware may not set outbound_intr_status in MSI-X mode */ if (!instance->msix_vectors) return IRQ_NONE; @@ -3680,7 +3980,7 @@ if ((mfiStatus & MFI_INTR_FLAG_FIRMWARE_STATE_CHANGE)) { fw_state = instance->instancet->read_fw_status_reg( - instance->reg_set) & MFI_STATE_MASK; + instance) & MFI_STATE_MASK; if (fw_state != MFI_STATE_FAULT) { dev_notice(&instance->pdev->dev, "fw state:%x\n", @@ -3725,8 +4025,11 @@ 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) { @@ -3748,6 +4051,7 @@ /** * 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 @@ -3760,10 +4064,9 @@ 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->reg_set); + abs_state = instance->instancet->read_fw_status_reg(instance); fw_state = abs_state & MFI_STATE_MASK; if (fw_state != MFI_STATE_READY) @@ -3775,13 +4078,18 @@ 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: /* @@ -3801,7 +4109,6 @@ &instance->reg_set->inbound_doorbell); max_wait = MEGASAS_RESET_WAIT_TIME; - cur_state = MFI_STATE_WAIT_HANDSHAKE; break; case MFI_STATE_BOOT_MESSAGE_PENDING: @@ -3817,7 +4124,6 @@ &instance->reg_set->inbound_doorbell); max_wait = MEGASAS_RESET_WAIT_TIME; - cur_state = MFI_STATE_BOOT_MESSAGE_PENDING; break; case MFI_STATE_OPERATIONAL: @@ -3835,7 +4141,8 @@ if (instance->adapter_type != MFI_SERIES) { for (i = 0; i < (10 * 1000); i += 20) { - if (readl( + if (megasas_readl( + instance, &instance-> reg_set-> doorbell) & 1) @@ -3849,7 +4156,6 @@ &instance->reg_set->inbound_doorbell); max_wait = MEGASAS_RESET_WAIT_TIME; - cur_state = MFI_STATE_OPERATIONAL; break; case MFI_STATE_UNDEFINED: @@ -3857,37 +4163,33 @@ * 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; } @@ -3896,7 +4198,7 @@ */ for (i = 0; i < max_wait * 50; i++) { curr_abs_state = instance->instancet-> - read_fw_status_reg(instance->reg_set); + read_fw_status_reg(instance); if (abs_state == curr_abs_state) { msleep(20); @@ -3910,6 +4212,8 @@ 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; } @@ -3973,21 +4277,10 @@ { 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. @@ -4239,8 +4532,10 @@ 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); @@ -4276,7 +4571,6 @@ 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 " @@ -4285,7 +4579,6 @@ } ci = instance->pd_list_buf; - ci_h = instance->pd_list_buf_h; cmd = megasas_get_cmd(instance); @@ -4358,6 +4651,9 @@ 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))) @@ -4373,6 +4669,11 @@ 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++; } @@ -4476,6 +4777,10 @@ 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; @@ -4485,6 +4790,10 @@ 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); } } @@ -4500,7 +4809,7 @@ /** * 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 @@ -4588,6 +4897,10 @@ 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; @@ -4595,8 +4908,145 @@ 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; +} + +/** + * 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; } @@ -4639,9 +5089,9 @@ } dev_info(&instance->pdev->dev, - "firmware type\t: %s\n", - instance->supportmax256vd ? "Extended VD(240 VD)firmware" : - "Legacy(64 VD) firmware"); + "FW provided supportMaxExtLDs: %d\tmax_lds: %d\n", + instance->ctrl_info_buf->adapterOperations3.supportMaxExtLDs ? 1 : 0, + instance->ctrl_info_buf->max_lds); if (instance->max_raid_mapsize) { ventura_map_sz = instance->max_raid_mapsize * @@ -4664,6 +5114,89 @@ } /* irrespective of FW raid maps, driver raid map is constant */ fusion->drv_map_sz = sizeof(struct MR_DRV_RAID_MAP_ALL); +} + +/* + * dcmd.opcode - MR_DCMD_CTRL_SNAPDUMP_GET_PROPERTIES + * dcmd.hdr.length - number of bytes to read + * dcmd.sge - Ptr to MR_SNAPDUMP_PROPERTIES + * Desc: Fill in snapdump properties + * Status: MFI_STAT_OK- Command successful + */ +void megasas_get_snapdump_properties(struct megasas_instance *instance) +{ + int ret = 0; + struct megasas_cmd *cmd; + struct megasas_dcmd_frame *dcmd; + struct MR_SNAPDUMP_PROPERTIES *ci; + dma_addr_t ci_h = 0; + + ci = instance->snapdump_prop; + ci_h = instance->snapdump_prop_h; + + if (!ci) + return; + + cmd = megasas_get_cmd(instance); + + if (!cmd) { + dev_dbg(&instance->pdev->dev, "Failed to get a free cmd\n"); + return; + } + + dcmd = &cmd->frame->dcmd; + + memset(ci, 0, sizeof(*ci)); + memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); + + 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(sizeof(struct MR_SNAPDUMP_PROPERTIES)); + dcmd->opcode = cpu_to_le32(MR_DCMD_CTRL_SNAPDUMP_GET_PROPERTIES); + + megasas_set_dma_settings(instance, dcmd, ci_h, + sizeof(struct MR_SNAPDUMP_PROPERTIES)); + + 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: + instance->snapdump_wait_time = + min_t(u8, ci->trigger_min_num_sec_before_ocr, + MEGASAS_MAX_SNAP_DUMP_WAIT_TIME); + 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; + } + } + + if (ret != DCMD_TIMEOUT) + megasas_return_cmd(instance, cmd); } /** @@ -4725,9 +5258,11 @@ * CPU endianness format. */ le32_to_cpus((u32 *)&ci->properties.OnOffProperties); + le16_to_cpus((u16 *)&ci->properties.on_off_properties2); 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. @@ -4735,17 +5270,27 @@ * 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; /*Check whether controller is iMR or MR */ instance->is_imr = (ci->memory_size ? 0 : 1); + + instance->snapdump_wait_time = + (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", @@ -4764,6 +5309,10 @@ 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; @@ -4771,8 +5320,10 @@ 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); @@ -4947,16 +5498,13 @@ static u32 megasas_init_adapter_mfi(struct megasas_instance *instance) { - struct megasas_register_set __iomem *reg_set; u32 context_sz; u32 reply_q_sz; - - reg_set = instance->reg_set; /* * Get various operational parameters from status register */ - instance->max_fw_cmds = instance->instancet->read_fw_status_reg(reg_set) & 0x00FFFF; + instance->max_fw_cmds = instance->instancet->read_fw_status_reg(instance) & 0x00FFFF; /* * Reduce the max supported cmds by 1. This is to ensure that the * reply_q_sz (1 more than the max cmd that driver may send) @@ -4964,7 +5512,7 @@ */ instance->max_fw_cmds = instance->max_fw_cmds-1; instance->max_mfi_cmds = instance->max_fw_cmds; - instance->max_num_sge = (instance->instancet->read_fw_status_reg(reg_set) & 0xFF0000) >> + instance->max_num_sge = (instance->instancet->read_fw_status_reg(instance) & 0xFF0000) >> 0x10; /* * For MFI skinny adapters, MEGASAS_SKINNY_INT_CMDS commands @@ -5000,9 +5548,8 @@ context_sz = sizeof(u32); reply_q_sz = context_sz * (instance->max_fw_cmds + 1); - instance->reply_queue = pci_alloc_consistent(instance->pdev, - reply_q_sz, - &instance->reply_queue_h); + instance->reply_queue = dma_alloc_coherent(&instance->pdev->dev, + reply_q_sz, &instance->reply_queue_h, GFP_KERNEL); if (!instance->reply_queue) { dev_printk(KERN_DEBUG, &instance->pdev->dev, "Out of DMA mem for reply queue\n"); @@ -5021,7 +5568,7 @@ instance->fw_support_ieee = 0; instance->fw_support_ieee = - (instance->instancet->read_fw_status_reg(reg_set) & + (instance->instancet->read_fw_status_reg(instance) & 0x04000000); dev_notice(&instance->pdev->dev, "megasas_init_mfi: fw_support_ieee=%d", @@ -5034,13 +5581,32 @@ fail_fw_init: - pci_free_consistent(instance->pdev, reply_q_sz, + dma_free_coherent(&instance->pdev->dev, reply_q_sz, instance->reply_queue, instance->reply_queue_h); fail_reply_queue: megasas_free_cmds(instance); 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); + } } /* @@ -5059,14 +5625,18 @@ 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; } @@ -5091,16 +5661,23 @@ 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_set_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); @@ -5109,6 +5686,7 @@ } } } + return 0; } @@ -5121,9 +5699,22 @@ 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_set_affinity_hint( + pci_irq_vector(instance->pdev, i), NULL); free_irq(pci_irq_vector(instance->pdev, i), &instance->irq_context[i]); } @@ -5135,7 +5726,6 @@ /** * megasas_setup_jbod_map - setup jbod map for FP seq_number. * @instance: Adapter soft state - * @is_probe: Driver probe check * * Return 0 on success. */ @@ -5149,10 +5739,12 @@ pd_seq_map_sz = sizeof(struct MR_PD_CFG_SEQ_NUM_SYNC) + (sizeof(struct MR_PD_CFG_SEQ) * (MAX_PHYSICAL_DEVICES - 1)); + 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; @@ -5191,9 +5783,11 @@ 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; - for (queue = 0; queue < instance->msix_vectors; queue++) { + low_latency_index_start = instance->low_latency_index_start; + + for (queue = low_latency_index_start; queue < instance->msix_vectors; queue++) { mask = pci_irq_get_affinity(instance->pdev, queue); if (!mask) goto fallback; @@ -5204,8 +5798,131 @@ 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) +{ + memset(instance->pd_list, 0, + (MEGASAS_MAX_PD * sizeof(struct megasas_pd_list))); + memset(instance->ld_ids, 0xff, MEGASAS_MAX_LD_IDS); + + 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_hint - Set affinity hint for high IOPS queues + * @instance: Adapter soft state + * return: void + */ +static inline void +megasas_set_high_iops_queue_affinity_hint(struct megasas_instance *instance) +{ + int i; + int local_numa_node; + + if (instance->perf_mode == MR_BALANCED_PERF_MODE) { + local_numa_node = dev_to_node(&instance->pdev->dev); + + for (i = 0; i < instance->low_latency_index_start; i++) + irq_set_affinity_hint(pci_irq_vector(instance->pdev, i), + cpumask_of_node(local_numa_node)); + } +} + +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; + else + descp = NULL; + + i = pci_alloc_irq_vectors_affinity(instance->pdev, + instance->low_latency_index_start, + instance->msix_vectors, 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; + + i = __megasas_alloc_irq_vectors(instance); + + if ((instance->perf_mode == MR_BALANCED_PERF_MODE) && + (i != instance->msix_vectors)) { + 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 = num_online_cpus() + instance->low_latency_index_start; + + instance->msix_vectors = min(num_msix_req, + instance->msix_vectors); + + i = __megasas_alloc_irq_vectors(instance); + + } + + dev_info(&instance->pdev->dev, + "requested/available msix %d/%d\n", instance->msix_vectors, i); + + if (i > 0) + instance->msix_vectors = i; + else + instance->msix_vectors = 0; + + if (instance->smp_affinity_enable) + megasas_set_high_iops_queue_affinity_hint(instance); } /** @@ -5219,15 +5936,17 @@ { u32 max_sectors_1; u32 max_sectors_2, tmp_sectors, msix_enable; - u32 scratch_pad_2, scratch_pad_3, scratch_pad_4, status_reg; + u32 scratch_pad_1, scratch_pad_2, scratch_pad_3, status_reg; resource_size_t base_addr; - struct megasas_register_set __iomem *reg_set; + 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; @@ -5241,14 +5960,17 @@ } 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; } - reg_set = instance->reg_set; + 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; @@ -5276,29 +5998,35 @@ } 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->reg_set); - do_adp_reset = status_reg & MFI_RESET_ADAPTER; - } - - if (do_adp_reset) { + instance); + 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); @@ -5314,31 +6042,43 @@ fusion = instance->ctrl_context; - if (instance->adapter_type == VENTURA_SERIES) { - scratch_pad_3 = - readl(&instance->reg_set->outbound_scratch_pad_3); - instance->max_raid_mapsize = ((scratch_pad_3 >> + if (instance->adapter_type >= VENTURA_SERIES) { + scratch_pad_2 = + megasas_readl(instance, + &instance->reg_set->outbound_scratch_pad_2); + instance->max_raid_mapsize = ((scratch_pad_2 >> MR_MAX_RAID_MAP_SIZE_OFFSET_SHIFT) & 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(reg_set) & + msix_enable = (instance->instancet->read_fw_status_reg(instance) & 0x4000000) >> 0x1a; if (msix_enable && !msix_disable) { - int irq_flags = PCI_IRQ_MSIX; - scratch_pad_2 = readl - (&instance->reg_set->outbound_scratch_pad_2); + scratch_pad_1 = megasas_readl + (instance, &instance->reg_set->outbound_scratch_pad_1); /* Check max MSI-X vectors */ if (fusion) { if (instance->adapter_type == THUNDERBOLT_SERIES) { /* Thunderbolt Series*/ - instance->msix_vectors = (scratch_pad_2 + instance->msix_vectors = (scratch_pad_1 & MR_MAX_REPLY_QUEUES_OFFSET) + 1; - fw_msix_count = instance->msix_vectors; } else { - instance->msix_vectors = ((scratch_pad_2 + instance->msix_vectors = ((scratch_pad_1 & MR_MAX_REPLY_QUEUES_EXT_OFFSET) >> MR_MAX_REPLY_QUEUES_EXT_OFFSET_SHIFT) + 1; @@ -5355,6 +6095,7 @@ if (instance->msix_vectors > 8) instance->msix_combined = true; break; + case AERO_SERIES: case VENTURA_SERIES: if (instance->msix_vectors > 16) instance->msix_combined = true; @@ -5362,9 +6103,15 @@ } if (rdpq_enable) - instance->is_rdpq = (scratch_pad_2 & MR_RDPQ_MODE_OFFSET) ? + 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 @@ -5377,22 +6124,91 @@ + (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 && + (num_online_cpus() >= 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 = num_online_cpus() + 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. @@ -5417,8 +6233,6 @@ 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()); dev_info(&instance->pdev->dev, @@ -5437,13 +6251,14 @@ if (instance->instancet->init_adapter(instance)) goto fail_init_adapter; - if (instance->adapter_type == VENTURA_SERIES) { - scratch_pad_4 = - readl(&instance->reg_set->outbound_scratch_pad_4); - if ((scratch_pad_4 & MR_NVME_PAGE_SIZE_MASK) >= + if (instance->adapter_type >= VENTURA_SERIES) { + scratch_pad_3 = + megasas_readl(instance, + &instance->reg_set->outbound_scratch_pad_3); + if ((scratch_pad_3 & MR_NVME_PAGE_SIZE_MASK) >= MR_DEFAULT_NVME_PAGE_SHIFT) instance->nvme_page_size = - (1 << (scratch_pad_4 & MR_NVME_PAGE_SIZE_MASK)); + (1 << (scratch_pad_3 & MR_NVME_PAGE_SIZE_MASK)); dev_info(&instance->pdev->dev, "NVME page size\t: (%d)\n", instance->nvme_page_size); @@ -5454,26 +6269,24 @@ 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) { + if (instance->adapter_type >= VENTURA_SERIES) { fusion->stream_detect_by_ld = kcalloc(MAX_LOGICAL_DRIVES_EXT, sizeof(struct LD_STREAM_DETECT *), @@ -5500,10 +6313,6 @@ = MR_STREAM_BITMAP; } } - - 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 @@ -5566,13 +6375,18 @@ else { if (instance->crash_dump_buf) - pci_free_consistent(instance->pdev, + dma_free_coherent(&instance->pdev->dev, CRASH_DMA_BUF_SIZE, instance->crash_dump_buf, instance->crash_dump_h); instance->crash_dump_buf = NULL; } + if (instance->snapdump_wait_time) { + megasas_get_snapdump_properties(instance); + dev_info(&instance->pdev->dev, "Snap dump wait time\t: %d\n", + instance->snapdump_wait_time); + } dev_info(&instance->pdev->dev, "pci id\t\t: (0x%04x)/(0x%04x)/(0x%04x)/(0x%04x)\n", @@ -5584,9 +6398,8 @@ 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; @@ -5610,14 +6423,28 @@ /* Launch SR-IOV heartbeat timer */ if (instance->requestorId) { - if (!megasas_sriov_start_heartbeat(instance, 1)) + if (!megasas_sriov_start_heartbeat(instance, 1)) { megasas_start_timer(instance); - else + } else { instance->skip_heartbeat_timer_del = 1; + goto fail_get_ld_pd_list; + } } + + /* + * Create and start watchdog thread which will monitor + * controller state every 1 sec and trigger OCR when + * it enters fault state + */ + if (instance->adapter_type != MFI_SERIES) + if (megasas_fusion_start_watchdog(instance) != SUCCESS) + goto fail_start_watchdog; return 0; +fail_start_watchdog: + if (instance->requestorId && !instance->skip_heartbeat_timer_del) + del_timer_sync(&instance->sriov_heartbeat_timer); fail_get_ld_pd_list: instance->instancet->disable_intr(instance); megasas_destroy_irqs(instance); @@ -5648,7 +6475,7 @@ u32 reply_q_sz = sizeof(u32) *(instance->max_mfi_cmds + 1); if (instance->reply_queue) - pci_free_consistent(instance->pdev, reply_q_sz, + dma_free_coherent(&instance->pdev->dev, reply_q_sz, instance->reply_queue, instance->reply_queue_h); megasas_free_cmds(instance); @@ -5687,10 +6514,9 @@ } dcmd = &cmd->frame->dcmd; - el_info = pci_zalloc_consistent(instance->pdev, - sizeof(struct megasas_evt_log_info), - &el_info_h); - + el_info = dma_alloc_coherent(&instance->pdev->dev, + sizeof(struct megasas_evt_log_info), + &el_info_h, GFP_KERNEL); if (!el_info) { megasas_return_cmd(instance, cmd); return -ENOMEM; @@ -5727,8 +6553,9 @@ eli->boot_seq_num = el_info->boot_seq_num; dcmd_failed: - pci_free_consistent(instance->pdev, sizeof(struct megasas_evt_log_info), - el_info, el_info_h); + dma_free_coherent(&instance->pdev->dev, + sizeof(struct megasas_evt_log_info), + el_info, el_info_h); megasas_return_cmd(instance, cmd); @@ -5739,7 +6566,7 @@ * 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 @@ -5936,8 +6763,10 @@ 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); @@ -6072,7 +6901,7 @@ { u64 consistent_mask; struct pci_dev *pdev; - u32 scratch_pad_2; + u32 scratch_pad_1; pdev = instance->pdev; consistent_mask = (instance->adapter_type >= VENTURA_SERIES) ? @@ -6090,10 +6919,10 @@ * If 32 bit DMA mask fails, then try for 64 bit mask * for FW capable of handling 64 bit DMA. */ - scratch_pad_2 = readl - (&instance->reg_set->outbound_scratch_pad_2); + scratch_pad_1 = megasas_readl + (instance, &instance->reg_set->outbound_scratch_pad_1); - if (!(scratch_pad_2 & MR_CAN_HANDLE_64_BIT_DMA_OFFSET)) + if (!(scratch_pad_1 & MR_CAN_HANDLE_64_BIT_DMA_OFFSET)) goto fail_set_dma_mask; else if (dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(63))) @@ -6108,7 +6937,7 @@ instance->consistent_mask_64bit = true; dev_info(&pdev->dev, "%s bit DMA mask and %s bit consistent mask\n", - ((*pdev->dev.dma_mask == DMA_BIT_MASK(64)) ? "63" : "32"), + ((*pdev->dev.dma_mask == DMA_BIT_MASK(63)) ? "63" : "32"), (instance->consistent_mask_64bit ? "63" : "32")); return 0; @@ -6122,12 +6951,14 @@ /* * megasas_set_adapter_type - Set adapter type. * Supported controllers can be divided in - * 4 categories- enum MR_ADAPTER_TYPE { - * MFI_SERIES = 1, - * THUNDERBOLT_SERIES = 2, - * INVADER_SERIES = 3, - * VENTURA_SERIES = 4, - * }; + * different categories- + * enum MR_ADAPTER_TYPE { + * MFI_SERIES = 1, + * THUNDERBOLT_SERIES = 2, + * INVADER_SERIES = 3, + * VENTURA_SERIES = 4, + * AERO_SERIES = 5, + * }; * @instance: Adapter soft state * return: void */ @@ -6138,6 +6969,12 @@ instance->adapter_type = MFI_SERIES; } else { switch (instance->pdev->device) { + case PCI_DEVICE_ID_LSI_AERO_10E1: + case PCI_DEVICE_ID_LSI_AERO_10E2: + case PCI_DEVICE_ID_LSI_AERO_10E5: + case PCI_DEVICE_ID_LSI_AERO_10E6: + instance->adapter_type = AERO_SERIES; + break; case PCI_DEVICE_ID_LSI_VENTURA: case PCI_DEVICE_ID_LSI_CRUSADER: case PCI_DEVICE_ID_LSI_HARPOON: @@ -6167,10 +7004,10 @@ static inline int megasas_alloc_mfi_ctrl_mem(struct megasas_instance *instance) { - instance->producer = pci_alloc_consistent(instance->pdev, sizeof(u32), - &instance->producer_h); - instance->consumer = pci_alloc_consistent(instance->pdev, sizeof(u32), - &instance->consumer_h); + instance->producer = dma_alloc_coherent(&instance->pdev->dev, + sizeof(u32), &instance->producer_h, GFP_KERNEL); + instance->consumer = dma_alloc_coherent(&instance->pdev->dev, + sizeof(u32), &instance->consumer_h, GFP_KERNEL); if (!instance->producer || !instance->consumer) { dev_err(&instance->pdev->dev, @@ -6205,6 +7042,7 @@ if (megasas_alloc_mfi_ctrl_mem(instance)) goto fail; break; + case AERO_SERIES: case VENTURA_SERIES: case THUNDERBOLT_SERIES: case INVADER_SERIES: @@ -6232,11 +7070,11 @@ kfree(instance->reply_map); if (instance->adapter_type == MFI_SERIES) { if (instance->producer) - pci_free_consistent(instance->pdev, sizeof(u32), + dma_free_coherent(&instance->pdev->dev, sizeof(u32), instance->producer, instance->producer_h); if (instance->consumer) - pci_free_consistent(instance->pdev, sizeof(u32), + dma_free_coherent(&instance->pdev->dev, sizeof(u32), instance->consumer, instance->consumer_h); } else { @@ -6248,8 +7086,9 @@ * 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) @@ -6257,10 +7096,9 @@ struct pci_dev *pdev = instance->pdev; struct fusion_context *fusion = instance->ctrl_context; - instance->evt_detail = - pci_alloc_consistent(pdev, - sizeof(struct megasas_evt_detail), - &instance->evt_detail_h); + instance->evt_detail = dma_alloc_coherent(&pdev->dev, + sizeof(struct megasas_evt_detail), + &instance->evt_detail_h, GFP_KERNEL); if (!instance->evt_detail) { dev_err(&instance->pdev->dev, @@ -6280,12 +7118,32 @@ "Failed to allocate PD list buffer\n"); return -ENOMEM; } + + instance->snapdump_prop = dma_alloc_coherent(&pdev->dev, + sizeof(struct MR_SNAPDUMP_PROPERTIES), + &instance->snapdump_prop_h, GFP_KERNEL); + + 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 = - pci_alloc_consistent(pdev, + dma_alloc_coherent(&pdev->dev, MEGASAS_MAX_PD * sizeof(struct MR_PD_LIST), - &instance->pd_list_buf_h); + &instance->pd_list_buf_h, GFP_KERNEL); if (!instance->pd_list_buf) { dev_err(&pdev->dev, "Failed to allocate PD list buffer\n"); @@ -6293,9 +7151,9 @@ } instance->ctrl_info_buf = - pci_alloc_consistent(pdev, + dma_alloc_coherent(&pdev->dev, sizeof(struct megasas_ctrl_info), - &instance->ctrl_info_buf_h); + &instance->ctrl_info_buf_h, GFP_KERNEL); if (!instance->ctrl_info_buf) { dev_err(&pdev->dev, @@ -6304,9 +7162,9 @@ } instance->ld_list_buf = - pci_alloc_consistent(pdev, + dma_alloc_coherent(&pdev->dev, sizeof(struct MR_LD_LIST), - &instance->ld_list_buf_h); + &instance->ld_list_buf_h, GFP_KERNEL); if (!instance->ld_list_buf) { dev_err(&pdev->dev, "Failed to allocate LD list buffer\n"); @@ -6314,9 +7172,9 @@ } instance->ld_targetid_list_buf = - pci_alloc_consistent(pdev, - sizeof(struct MR_LD_TARGETID_LIST), - &instance->ld_targetid_list_buf_h); + dma_alloc_coherent(&pdev->dev, + sizeof(struct MR_LD_TARGETID_LIST), + &instance->ld_targetid_list_buf_h, GFP_KERNEL); if (!instance->ld_targetid_list_buf) { dev_err(&pdev->dev, @@ -6326,21 +7184,20 @@ if (!reset_devices) { instance->system_info_buf = - pci_alloc_consistent(pdev, - sizeof(struct MR_DRV_SYSTEM_INFO), - &instance->system_info_h); + dma_alloc_coherent(&pdev->dev, + sizeof(struct MR_DRV_SYSTEM_INFO), + &instance->system_info_h, GFP_KERNEL); instance->pd_info = - pci_alloc_consistent(pdev, - sizeof(struct MR_PD_INFO), - &instance->pd_info_h); + dma_alloc_coherent(&pdev->dev, + sizeof(struct MR_PD_INFO), + &instance->pd_info_h, GFP_KERNEL); instance->tgt_prop = - pci_alloc_consistent(pdev, - sizeof(struct MR_TARGET_PROPERTIES), - &instance->tgt_prop_h); + dma_alloc_coherent(&pdev->dev, + sizeof(struct MR_TARGET_PROPERTIES), + &instance->tgt_prop_h, GFP_KERNEL); instance->crash_dump_buf = - pci_alloc_consistent(pdev, - CRASH_DMA_BUF_SIZE, - &instance->crash_dump_h); + dma_alloc_coherent(&pdev->dev, CRASH_DMA_BUF_SIZE, + &instance->crash_dump_h, GFP_KERNEL); if (!instance->system_info_buf) dev_err(&instance->pdev->dev, @@ -6376,7 +7233,7 @@ struct fusion_context *fusion = instance->ctrl_context; if (instance->evt_detail) - pci_free_consistent(pdev, sizeof(struct megasas_evt_detail), + dma_free_coherent(&pdev->dev, sizeof(struct megasas_evt_detail), instance->evt_detail, instance->evt_detail_h); @@ -6387,43 +7244,56 @@ fusion->ioc_init_request_phys); if (instance->pd_list_buf) - pci_free_consistent(pdev, + dma_free_coherent(&pdev->dev, MEGASAS_MAX_PD * sizeof(struct MR_PD_LIST), instance->pd_list_buf, instance->pd_list_buf_h); if (instance->ld_list_buf) - pci_free_consistent(pdev, sizeof(struct MR_LD_LIST), + dma_free_coherent(&pdev->dev, sizeof(struct MR_LD_LIST), instance->ld_list_buf, instance->ld_list_buf_h); if (instance->ld_targetid_list_buf) - pci_free_consistent(pdev, sizeof(struct MR_LD_TARGETID_LIST), + dma_free_coherent(&pdev->dev, sizeof(struct MR_LD_TARGETID_LIST), instance->ld_targetid_list_buf, instance->ld_targetid_list_buf_h); if (instance->ctrl_info_buf) - pci_free_consistent(pdev, sizeof(struct megasas_ctrl_info), + dma_free_coherent(&pdev->dev, sizeof(struct megasas_ctrl_info), instance->ctrl_info_buf, instance->ctrl_info_buf_h); if (instance->system_info_buf) - pci_free_consistent(pdev, sizeof(struct MR_DRV_SYSTEM_INFO), + dma_free_coherent(&pdev->dev, sizeof(struct MR_DRV_SYSTEM_INFO), instance->system_info_buf, instance->system_info_h); if (instance->pd_info) - pci_free_consistent(pdev, sizeof(struct MR_PD_INFO), + dma_free_coherent(&pdev->dev, sizeof(struct MR_PD_INFO), instance->pd_info, instance->pd_info_h); if (instance->tgt_prop) - pci_free_consistent(pdev, sizeof(struct MR_TARGET_PROPERTIES), + dma_free_coherent(&pdev->dev, sizeof(struct MR_TARGET_PROPERTIES), instance->tgt_prop, instance->tgt_prop_h); if (instance->crash_dump_buf) - pci_free_consistent(pdev, CRASH_DMA_BUF_SIZE, + dma_free_coherent(&pdev->dev, CRASH_DMA_BUF_SIZE, instance->crash_dump_buf, instance->crash_dump_h); + + if (instance->snapdump_prop) + dma_free_coherent(&pdev->dev, + 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); + } /* @@ -6447,11 +7317,12 @@ 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); @@ -6469,13 +7340,13 @@ 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) { + if (instance->adapter_type != MFI_SERIES) INIT_WORK(&instance->work_init, megasas_fusion_ocr_wq); - INIT_WORK(&instance->crash_init, megasas_fusion_crash_dump_wq); - } else { + else INIT_WORK(&instance->work_init, process_fw_state_change_wq); - } } /** @@ -6490,6 +7361,19 @@ struct Scsi_Host *host; struct megasas_instance *instance; 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"); + break; + } /* Reset MSI-X in the kdump kernel */ if (reset_devices) { @@ -6549,17 +7433,20 @@ if (instance->requestorId) { if (instance->PlasmaFW111) { instance->vf_affiliation_111 = - pci_alloc_consistent(pdev, sizeof(struct MR_LD_VF_AFFILIATION_111), - &instance->vf_affiliation_111_h); + dma_alloc_coherent(&pdev->dev, + sizeof(struct MR_LD_VF_AFFILIATION_111), + &instance->vf_affiliation_111_h, + GFP_KERNEL); if (!instance->vf_affiliation_111) dev_warn(&pdev->dev, "Can't allocate " "memory for VF affiliation buffer\n"); } else { instance->vf_affiliation = - pci_alloc_consistent(pdev, - (MAX_LOGICAL_DRIVES + 1) * - sizeof(struct MR_LD_VF_AFFILIATION), - &instance->vf_affiliation_h); + dma_alloc_coherent(&pdev->dev, + (MAX_LOGICAL_DRIVES + 1) * + sizeof(struct MR_LD_VF_AFFILIATION), + &instance->vf_affiliation_h, + GFP_KERNEL); if (!instance->vf_affiliation) dev_warn(&pdev->dev, "Can't allocate " "memory for VF affiliation buffer\n"); @@ -6589,7 +7476,9 @@ /* * 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) @@ -6599,6 +7488,8 @@ 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); @@ -6606,10 +7497,15 @@ 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) + del_timer_sync(&instance->sriov_heartbeat_timer); instance->instancet->disable_intr(instance); megasas_destroy_irqs(instance); @@ -6618,8 +7514,16 @@ 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: @@ -6730,16 +7634,24 @@ static int megasas_suspend(struct pci_dev *pdev, pm_message_t state) { - struct Scsi_Host *host; struct megasas_instance *instance; instance = pci_get_drvdata(pdev); - host = instance->host; + + if (!instance) + return 0; + instance->unload = 1; + + dev_info(&pdev->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); + + /* Stop the FW fault detection watchdog */ + if (instance->adapter_type != MFI_SERIES) + megasas_fusion_stop_watchdog(instance); megasas_flush_cache(instance); megasas_shutdown_controller(instance, MR_DCMD_HIBERNATE_SHUTDOWN); @@ -6779,14 +7691,19 @@ int rval; struct Scsi_Host *host; struct megasas_instance *instance; - int irq_flags = PCI_IRQ_LEGACY; + u32 status_reg; instance = pci_get_drvdata(pdev); + + if (!instance) + return 0; + host = instance->host; pci_set_power_state(pdev, PCI_D0); pci_enable_wake(pdev, PCI_D0, 0); pci_restore_state(pdev); + dev_info(&pdev->dev, "%s is called\n", __func__); /* * PCI prepping: enable device set bus mastering and dma mask */ @@ -6802,9 +7719,35 @@ /* * 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; @@ -6816,16 +7759,15 @@ 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_LEGACY); + 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); @@ -6856,6 +7798,9 @@ 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)) @@ -6876,8 +7821,16 @@ if (megasas_start_aen(instance)) dev_err(&instance->pdev->dev, "Start AEN failed\n"); + /* Re-launch FW fault watchdog */ + if (instance->adapter_type != MFI_SERIES) + if (megasas_fusion_start_watchdog(instance) != SUCCESS) + goto fail_start_watchdog; + return 0; +fail_start_watchdog: + if (instance->requestorId && !instance->skip_heartbeat_timer_del) + del_timer_sync(&instance->sriov_heartbeat_timer); fail_init_mfi: megasas_free_ctrl_dma_buffers(instance); megasas_free_ctrl_mem(instance); @@ -6938,12 +7891,20 @@ u32 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); + + /* Stop the FW fault detection watchdog */ + if (instance->adapter_type != MFI_SERIES) + megasas_fusion_stop_watchdog(instance); if (instance->fw_crash_state != UNAVAILABLE) megasas_free_host_crash_buffer(instance); @@ -6989,7 +7950,7 @@ if (instance->msix_vectors) pci_free_irq_vectors(instance->pdev); - if (instance->adapter_type == VENTURA_SERIES) { + if (instance->adapter_type >= VENTURA_SERIES) { for (i = 0; i < MAX_LOGICAL_DRIVES_EXT; ++i) kfree(fusion->stream_detect_by_ld[i]); kfree(fusion->stream_detect_by_ld); @@ -7027,25 +7988,27 @@ } if (instance->vf_affiliation) - pci_free_consistent(pdev, (MAX_LOGICAL_DRIVES + 1) * + dma_free_coherent(&pdev->dev, (MAX_LOGICAL_DRIVES + 1) * sizeof(struct MR_LD_VF_AFFILIATION), instance->vf_affiliation, instance->vf_affiliation_h); if (instance->vf_affiliation_111) - pci_free_consistent(pdev, + dma_free_coherent(&pdev->dev, sizeof(struct MR_LD_VF_AFFILIATION_111), instance->vf_affiliation_111, instance->vf_affiliation_111_h); if (instance->hb_host_mem) - pci_free_consistent(pdev, sizeof(struct MR_CTRL_HB_HOST_MEM), + dma_free_coherent(&pdev->dev, sizeof(struct MR_CTRL_HB_HOST_MEM), instance->hb_host_mem, instance->hb_host_mem_h); megasas_free_ctrl_dma_buffers(instance); megasas_free_ctrl_mem(instance); + + megasas_destroy_debugfs(instance); scsi_host_put(host); @@ -7054,11 +8017,14 @@ /** * megasas_shutdown - Shutdown entry point - * @device: Generic device structure + * @pdev: Generic device structure */ static void megasas_shutdown(struct pci_dev *pdev) { struct megasas_instance *instance = pci_get_drvdata(pdev); + + if (!instance) + return; instance->unload = 1; @@ -7076,8 +8042,10 @@ 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) { @@ -7090,8 +8058,11 @@ 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. @@ -7117,9 +8088,11 @@ 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; @@ -7177,7 +8150,8 @@ /** * 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, @@ -7194,6 +8168,7 @@ dma_addr_t sense_handle; void *sense_ptr; u32 opcode = 0; + int ret = DCMD_SUCCESS; memset(kbuff_arr, 0, sizeof(kbuff_arr)); @@ -7205,7 +8180,9 @@ 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); @@ -7241,10 +8218,13 @@ 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) { @@ -7287,7 +8267,7 @@ /* * We don't change the dma_coherent_mask, so - * pci_alloc_consistent only returns 32bit addresses + * dma_alloc_coherent only returns 32bit addresses */ if (instance->consistent_mask_64bit) { kern_sge64[i].phys_addr = cpu_to_le64(buf_handle); @@ -7333,13 +8313,18 @@ * 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; @@ -7510,6 +8495,9 @@ /** * 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) @@ -7690,6 +8678,14 @@ 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"); @@ -7697,102 +8693,103 @@ scsi_device_put(sdev); } -static void -megasas_aen_polling(struct work_struct *work) +/** + * 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) { - struct megasas_aen_event *ev = - 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 error; - u8 dcmd_ret = DCMD_SUCCESS; + int dcmd_ret = DCMD_SUCCESS; - if (!instance) { - printk(KERN_ERR "invalid instance!\n"); - kfree(ev); - 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); - - switch (le32_to_cpu(instance->evt_detail->code)) { - - 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; - 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; - - break; - - 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; - dev_info(&instance->pdev->dev, "scanning for scsi%d...\n", - instance->host->host_no); - break; - - case MR_EVT_CTRL_PROP_CHANGED: - dcmd_ret = megasas_get_ctrl_info(instance); - break; - default: - doscan = 0; - break; - } + if (instance->enable_fw_dev_list) { + dcmd_ret = megasas_host_device_list_query(instance, false); + if (dcmd_ret != DCMD_SUCCESS) + goto out; } else { - dev_err(&instance->pdev->dev, "invalid evt_detail!\n"); - mutex_unlock(&instance->reset_mutex); - kfree(ev); - return; + if (event_type & SCAN_PD_CHANNEL) { + dcmd_ret = megasas_get_pd_list(instance); + + if (dcmd_ret != DCMD_SUCCESS) + goto out; + } + + if (event_type & SCAN_VD_CHANNEL) { + 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) + goto out; + } + } } - mutex_unlock(&instance->reset_mutex); +out: + return dcmd_ret; +} - if (doscan & SCAN_PD_CHANNEL) { +/** + * 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; + 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) { @@ -7808,11 +8805,12 @@ } } - if (doscan & SCAN_VD_CHANNEL) { + 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); + 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); @@ -7825,6 +8823,97 @@ } } } + +} + +static void +megasas_aen_polling(struct work_struct *work) +{ + struct megasas_aen_event *ev = + container_of(work, struct megasas_aen_event, hotplug_work.work); + struct megasas_instance *instance = ev->instance; + union megasas_evt_class_locale class_locale; + 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"); + kfree(ev); + return; + } + + /* Don't run the event workqueue thread if OCR is running */ + mutex_lock(&instance->reset_mutex); + + instance->ev = NULL; + if (instance->evt_detail) { + megasas_decode_evt(instance); + + switch (le32_to_cpu(instance->evt_detail->code)) { + + case MR_EVT_PD_INSERTED: + case MR_EVT_PD_REMOVED: + event_type = SCAN_PD_CHANNEL; + break; + + case MR_EVT_LD_OFFLINE: + case MR_EVT_LD_DELETED: + 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) + megasas_remove_scsi_device(sdev1); + + 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: + event_type = SCAN_PD_CHANNEL | SCAN_VD_CHANNEL; + dev_info(&instance->pdev->dev, "scanning for scsi%d...\n", + instance->host->host_no); + break; + + case MR_EVT_CTRL_PROP_CHANGED: + dcmd_ret = megasas_get_ctrl_info(instance); + if (dcmd_ret == DCMD_SUCCESS && + instance->snapdump_wait_time) { + megasas_get_snapdump_properties(instance); + dev_info(&instance->pdev->dev, + "Snap dump wait time\t: %d\n", + instance->snapdump_wait_time); + } + break; + default: + event_type = 0; + break; + } + } else { + dev_err(&instance->pdev->dev, "invalid evt_detail!\n"); + mutex_unlock(&instance->reset_mutex); + kfree(ev); + return; + } + + if (event_type) + dcmd_ret = megasas_update_device_list(instance, event_type); + + mutex_unlock(&instance->reset_mutex); + + 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; @@ -7879,6 +8968,7 @@ 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)); @@ -7894,6 +8984,8 @@ megasas_mgmt_majorno = rval; + megasas_init_debugfs(); + /* * Register ourselves as PCI hotplug module */ @@ -7902,6 +8994,12 @@ if (rval) { printk(KERN_DEBUG "megasas: PCI hotplug registration failed \n"); 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, @@ -7933,7 +9031,16 @@ 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, @@ -7953,6 +9060,7 @@ 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; } @@ -7973,8 +9081,11 @@ 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"); } -- Gitblit v1.6.2