diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h index a6e788c02..b7f697882 100644 --- a/drivers/scsi/megaraid/megaraid_sas.h +++ b/drivers/scsi/megaraid/megaraid_sas.h @@ -1,10 +1,22 @@ -/* 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-2016 Avago Technologies - * Copyright (c) 2016-2018 Broadcom Inc. + * Copyright (c) 2003-2018 LSI Corporation. + * Copyright (c) 2003-2018 Avago Technologies. + * Copyright (c) 2003-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 . * * FILE: megaraid_sas.h * @@ -18,11 +30,37 @@ #ifndef LSI_MEGARAID_SAS_H #define LSI_MEGARAID_SAS_H +#if ((defined(RHEL_MAJOR) && (RHEL_MAJOR == 7) && (RHEL_MINOR >= 3)) || \ + (defined(CONFIG_SUSE_KERNEL) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4,4,21))) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4,6,0))) +#include +#define ENABLE_IRQ_POLL +#endif + +#if ((defined(RHEL_MAJOR) && (RHEL_MAJOR == 8) && (RHEL_MINOR >= 2)) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(5,0,0))) +#define USE_MANAGED_IRQ_API +#endif + +#if ((defined(RHEL_MAJOR) && (RHEL_MAJOR == 8) && (RHEL_MINOR >= 4)) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(5,10,0))) +#include +#define HOST_TAGSET_SUPPORT +#endif + +#if defined(HOST_TAGSET_SUPPORT) && !defined(USE_MANAGED_IRQ_API) +#error "Please use Managed IRQs APIs for shared host tagset to work" +#endif + /* * MegaRAID SAS Driver meta data */ -#define MEGASAS_VERSION "07.710.50.00-rc1" -#define MEGASAS_RELDATE "June 28, 2019" +#define MEGASAS_VERSION "07.721.02.00" +#define MEGASAS_RELDATE "Jan 07, 2022" +#define MEGASAS_EXT_VERSION "Jan 07, 17:00:00 PDT 2022" + +#define MEGASAS_MSIX_NAME_LEN 32 + /* * Device IDs @@ -48,14 +86,14 @@ #define PCI_DEVICE_ID_LSI_TOMCAT 0x0017 #define PCI_DEVICE_ID_LSI_VENTURA_4PORT 0x001B #define PCI_DEVICE_ID_LSI_CRUSADER_4PORT 0x001C -#define PCI_DEVICE_ID_LSI_AERO_10E1 0x10e1 -#define PCI_DEVICE_ID_LSI_AERO_10E2 0x10e2 -#define PCI_DEVICE_ID_LSI_AERO_10E5 0x10e5 -#define PCI_DEVICE_ID_LSI_AERO_10E6 0x10e6 -#define PCI_DEVICE_ID_LSI_AERO_10E0 0x10e0 -#define PCI_DEVICE_ID_LSI_AERO_10E3 0x10e3 -#define PCI_DEVICE_ID_LSI_AERO_10E4 0x10e4 -#define PCI_DEVICE_ID_LSI_AERO_10E7 0x10e7 +#define PCI_DEVICE_ID_LSI_AERO_10E1 0x10e1 +#define PCI_DEVICE_ID_LSI_AERO_10E2 0x10e2 +#define PCI_DEVICE_ID_LSI_AERO_10E5 0x10e5 +#define PCI_DEVICE_ID_LSI_AERO_10E6 0x10e6 +#define PCI_DEVICE_ID_LSI_AERO_10E0 0x10e0 +#define PCI_DEVICE_ID_LSI_AERO_10E3 0x10e3 +#define PCI_DEVICE_ID_LSI_AERO_10E4 0x10e4 +#define PCI_DEVICE_ID_LSI_AERO_10E7 0x10e7 /* * Intel HBA SSDIDs @@ -177,6 +215,10 @@ #define DRV_DCMD_POLLED_MODE 0x1 #define DRV_DCMD_SKIP_REFIRE 0x2 +#define MR_HIGH_IOPS_QUEUE_COUNT 8 +#define MR_DEVICE_HIGH_IOPS_DEPTH 8 +#define MR_HIGH_IOPS_BATCH_COUNT 16 + /* * Definition for cmd_status */ @@ -193,8 +235,8 @@ enum MFI_CMD_OP { MFI_CMD_PD_SCSI_IO = 0x4, MFI_CMD_DCMD = 0x5, MFI_CMD_ABORT = 0x6, - MFI_CMD_SMP = 0x7, - MFI_CMD_STP = 0x8, + MFI_CMD_SMP = 0x7, + MFI_CMD_STP = 0x8, MFI_CMD_NVME = 0x9, MFI_CMD_TOOLBOX = 0xa, MFI_CMD_OP_COUNT, @@ -509,7 +551,7 @@ union MR_PROGRESS { */ struct MR_PD_PROGRESS { struct { -#ifndef MFI_BIG_ENDIAN +#ifndef __BIG_ENDIAN_BITFIELD u32 rbld:1; u32 patrol:1; u32 clear:1; @@ -535,7 +577,7 @@ struct MR_PD_PROGRESS { }; struct { -#ifndef MFI_BIG_ENDIAN +#ifndef __BIG_ENDIAN_BITFIELD u32 rbld:1; u32 patrol:1; u32 clear:1; @@ -1453,12 +1495,11 @@ struct megasas_ctrl_info { u32 size; u32 pad1; - u8 reserved6[64]; - + struct { #if defined(__BIG_ENDIAN_BITFIELD) - u32 reserved:19; + u32 reserved:19; u32 support_pci_lane_margining: 1; u32 support_psoc_update:1; u32 support_force_personality_change:1; @@ -1492,7 +1533,7 @@ struct megasas_ctrl_info { u8 reserved7[3]; - u8 TaskAbortTO; /* Timeout value in seconds used by Abort Task TM */ + u8 TaskAbortTO; /* Timeout value in seconds used by Abort Task TM Request. */ u8 MaxResetTO; /* Max Supported Reset timeout in seconds. */ u8 reserved8[3]; } __packed; @@ -1515,6 +1556,7 @@ struct megasas_ctrl_info { #define MEGASAS_MAX_LD_IDS (MEGASAS_MAX_LD_CHANNELS * \ MEGASAS_MAX_DEV_PER_CHANNEL) +#define MEGASAS_MAX_NAME 32 #define MEGASAS_MAX_SECTORS (2*1024) #define MEGASAS_MAX_SECTORS_IEEE (2*128) #define MEGASAS_DBG_LVL 1 @@ -1679,7 +1721,7 @@ struct megasas_register_set { u32 reserved_3[3]; /*00A4h*/ - u32 outbound_scratch_pad_0; /*00B0h*/ + u32 outbound_scratch_pad_0; /*00B0h*/ u32 outbound_scratch_pad_1; /*00B4h*/ u32 outbound_scratch_pad_2; /*00B8h*/ u32 outbound_scratch_pad_3; /*00BCh*/ @@ -1814,10 +1856,11 @@ struct megasas_init_frame { __le32 queue_info_new_phys_addr_hi; /*1Ch */ __le32 queue_info_old_phys_addr_lo; /*20h */ __le32 queue_info_old_phys_addr_hi; /*24h */ - __le32 reserved_4[2]; /*28h */ + __le32 driver_ver_lo; /*28h */ + __le32 driver_ver_hi; /*2Ch */ __le32 system_info_lo; /*30h */ __le32 system_info_hi; /*34h */ - __le32 reserved_5[2]; /*38h */ + __le32 reserved_3[2]; /*38h */ } __attribute__ ((packed)); @@ -2020,11 +2063,16 @@ union megasas_frame { */ struct MR_PRIV_DEVICE { bool is_tm_capable; + bool is_epd; bool tm_busy; + bool device_removed_by_fw; atomic_t r1_ldio_hint; u8 interface_type; u8 task_abort_tmo; u8 target_reset_tmo; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)) + atomic_t sdev_priv_busy; +#endif }; struct megasas_cmd; @@ -2203,12 +2251,16 @@ struct megasas_aen_event { }; struct megasas_irq_context { + char name[MEGASAS_MSIX_NAME_LEN]; struct megasas_instance *instance; u32 MSIxIndex; +#if defined(ENABLE_IRQ_POLL) u32 os_irq; struct irq_poll irqpoll; bool irq_poll_scheduled; bool irq_line_enable; +#endif + atomic_t in_use; }; struct MR_DRV_SYSTEM_INFO { @@ -2230,39 +2282,33 @@ enum MR_PD_TYPE { /* JBOD Queue depth definitions */ #define MEGASAS_SATA_QD 32 -#define MEGASAS_SAS_QD 64 +#define MEGASAS_SAS_QD 256 #define MEGASAS_DEFAULT_PD_QD 64 -#define MEGASAS_NVME_QD 32 +#define MEGASAS_NVME_QD 64 #define MR_DEFAULT_NVME_PAGE_SIZE 4096 #define MR_DEFAULT_NVME_PAGE_SHIFT 12 #define MR_DEFAULT_NVME_MDTS_KB 128 #define MR_NVME_PAGE_SIZE_MASK 0x000000FF -/*Aero performance parameters*/ -#define MR_HIGH_IOPS_QUEUE_COUNT 8 -#define MR_DEVICE_HIGH_IOPS_DEPTH 8 -#define MR_HIGH_IOPS_BATCH_COUNT 16 - -enum MR_PERF_MODE { - MR_BALANCED_PERF_MODE = 0, - MR_IOPS_PERF_MODE = 1, - MR_LATENCY_PERF_MODE = 2, +enum MEGASAS_LD_TARGET_ID_STATUS { + LD_TARGET_ID_INITIAL, + LD_TARGET_ID_ACTIVE, + LD_TARGET_ID_DELETED, }; -#define MEGASAS_PERF_MODE_2STR(mode) \ - ((mode) == MR_BALANCED_PERF_MODE ? "Balanced" : \ - (mode) == MR_IOPS_PERF_MODE ? "IOPS" : \ - (mode) == MR_LATENCY_PERF_MODE ? "Latency" : \ - "Unknown") +#define MEGASAS_TARGET_ID(sdev) \ + (((sdev->channel % 2) * MEGASAS_MAX_DEV_PER_CHANNEL) + \ + sdev->id) struct megasas_instance { - unsigned int *reply_map; __le32 *producer; dma_addr_t producer_h; __le32 *consumer; dma_addr_t consumer_h; + __le32 *verbuf; + dma_addr_t verbuf_h; struct MR_DRV_SYSTEM_INFO *system_info_buf; dma_addr_t system_info_h; struct MR_LD_VF_AFFILIATION *vf_affiliation; @@ -2320,6 +2366,9 @@ struct megasas_instance { struct megasas_pd_list pd_list[MEGASAS_MAX_PD]; struct megasas_pd_list local_pd_list[MEGASAS_MAX_PD]; u8 ld_ids[MEGASAS_MAX_LD_IDS]; + u8 ld_tgtid_status[MEGASAS_MAX_LD_IDS]; + u8 ld_ids_prev[MEGASAS_MAX_LD_IDS]; + u8 ld_ids_from_raidmap[MEGASAS_MAX_LD_IDS]; s8 init_id; u16 max_num_sge; @@ -2398,6 +2447,8 @@ struct megasas_instance { /* Ptr to hba specific information */ void *ctrl_context; unsigned int msix_vectors; + u8 *reply_map; + struct msix_entry msixentry[MEGASAS_MAX_MSIX_QUEUES]; struct megasas_irq_context irq_context[MEGASAS_MAX_MSIX_QUEUES]; u64 map_id; u64 pd_seq_map_id; @@ -2434,8 +2485,8 @@ struct megasas_instance { u8 max_reset_tmo; u8 snapdump_wait_time; #ifdef CONFIG_DEBUG_FS - struct dentry *debugfs_root; - struct dentry *raidmap_dump; + struct dentry *debugfs_root; + struct dentry *raidmap_dump; #endif u8 enable_fw_dev_list; bool atomic_desc_support; @@ -2443,6 +2494,8 @@ struct megasas_instance { bool support_pci_lane_margining; u8 low_latency_index_start; int perf_mode; + bool use_blk_mq; + int iopoll_q_count; }; struct MR_LD_VF_MAP { @@ -2539,6 +2592,9 @@ struct megasas_instance_template { #define MEGASAS_IS_LOGICAL(sdev) \ ((sdev->channel < MEGASAS_MAX_PD_CHANNELS) ? 0 : 1) +#define MEGASAS_IS_LUN_VALID(sdev) \ + ((sdev->lun == 0) ? 1 : 0) + #define MEGASAS_DEV_INDEX(scp) \ (((scp->device->channel % 2) * MEGASAS_MAX_DEV_PER_CHANNEL) + \ scp->device->id) @@ -2637,12 +2693,32 @@ enum MEGASAS_OCR_CAUSE { }; enum DCMD_RETURN_STATUS { - DCMD_SUCCESS = 0, - DCMD_TIMEOUT = 1, - DCMD_FAILED = 2, - DCMD_NOT_FIRED = 3, + DCMD_SUCCESS = 0x00, + DCMD_TIMEOUT = 0x01, + DCMD_FAILED = 0x02, + DCMD_BUSY = 0x03, + DCMD_INIT = 0xff, }; +enum MR_PERF_MODE { + MR_BALANCED_PERF_MODE = 0, + MR_IOPS_PERF_MODE = 1, + MR_LATENCY_PERF_MODE = 2, +}; + +#define MEGASAS_PERF_MODE_2STR(mode) \ + ((mode) == MR_BALANCED_PERF_MODE ? "Balanced" : \ + (mode) == MR_IOPS_PERF_MODE ? "IOPs" : \ + (mode) == MR_LATENCY_PERF_MODE ? "Latency": \ + "Unknown") + +#define MEGASAS_PCIE_SPEED2STR(speed) \ + ((speed) == MEGASAS_PCIE_SPEED_16_0GT ? "16 GT/s" : \ + (speed) == MEGASAS_PCIE_SPEED_8_0GT ? "8 GT/s" : \ + (speed) == MEGASAS_PCIE_SPEED_5_0GT ? "5 GT/s" : \ + (speed) == MEGASAS_PCIE_SPEED_2_5GT ? "2.5 GT/s" : \ + "Unknown speed") + u8 MR_BuildRaidContext(struct megasas_instance *instance, struct IO_REQUEST_INFO *io_info, @@ -2700,9 +2776,14 @@ void megasas_fusion_stop_watchdog(struct megasas_instance *instance); void megasas_set_dma_settings(struct megasas_instance *instance, struct megasas_dcmd_frame *dcmd, dma_addr_t dma_addr, u32 dma_len); +unsigned int megasas_get_irq(struct megasas_instance *instance, int msix_index); +#if defined(ENABLE_IRQ_POLL) +extern int megasas_irqpoll(struct irq_poll *irqpoll, int budget); +#endif +void megasas_dump_fusion_io(struct scsi_cmnd *scmd); int megasas_adp_reset_wait_for_ready(struct megasas_instance *instance, bool do_adp_reset, int ocr_context); -int megasas_irqpoll(struct irq_poll *irqpoll, int budget); -void megasas_dump_fusion_io(struct scsi_cmnd *scmd); +int megasas_blk_mq_poll(struct Scsi_Host *shost, unsigned int queue_num); + #endif /*LSI_MEGARAID_SAS_H */ diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index b9c1f722f..ee2b31c01 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -1,10 +1,22 @@ -// 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-2016 Avago Technologies - * Copyright (c) 2016-2018 Broadcom Inc. + * Copyright (c) 2003-2018 LSI Corporation. + * Copyright (c) 2003-2018 Avago Technologies. + * Copyright (c) 2003-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 . * * Authors: Broadcom Inc. * Sreenivas Bagalkote @@ -17,6 +29,7 @@ * Send feedback to: megaraidlinux.pdl@broadcom.com */ +#include #include #include #include @@ -36,7 +49,6 @@ #include #include #include -#include #include #include @@ -44,6 +56,7 @@ #include #include #include + #include "megaraid_sas_fusion.h" #include "megaraid_sas.h" @@ -93,6 +106,9 @@ 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 crashdump_enable = 1; +module_param(crashdump_enable, int, 0444); +MODULE_PARM_DESC(crashdump_enable, "Firmware Crash dump feature enable/disbale Default: enable(1)"); 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" @@ -109,10 +125,43 @@ 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)"); +int disable_ext_io; +module_param(disable_ext_io, int, 0444); +MODULE_PARM_DESC(disable_ext_io, "Disable 1M IO support Default: 0(1M IO enabled)"); + 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"); +int poll_queues; +module_param(poll_queues, int, 0444); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,12,0)) +MODULE_PARM_DESC(poll_queues, "Number of queues to be use for io_uring poll mode.\n\t\t" + "This parameter is effective only if host_tagset_enable=1 &\n\t\t" + "It is not applicable for MFI_SERIES. &\n\t\t" + "Driver will work in latency mode. &\n\t\t" + "High iops queues are not allocated &\n\t\t" + ); +#else +MODULE_PARM_DESC(poll_queues, "Not supported for this kernel."); +#endif + +#if defined(HOST_TAGSET_SUPPORT) + +#if ((defined(RHEL_MAJOR) && (RHEL_MAJOR == 8) && (RHEL_MINOR > 5)) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(5,16,0))) +int host_tagset_enable = 1; +module_param(host_tagset_enable, int, 0444); +MODULE_PARM_DESC(host_tagset_enable, "Shared host tagset enable/disable Default: enable(1)"); +#else +int host_tagset_enable = 0; +module_param(host_tagset_enable, int, 0444); +MODULE_PARM_DESC(host_tagset_enable, "Shared host tagset enable/disable Default: disable(0)\n\t\t" + "Enabling Shared host tagset is experimental."); +#endif + +#endif + MODULE_LICENSE("GPL"); MODULE_VERSION(MEGASAS_VERSION); MODULE_AUTHOR("megaraidlinux.pdl@broadcom.com"); @@ -200,8 +249,8 @@ static bool support_pci_lane_margining; /* define lock for aen poll */ spinlock_t poll_aen_lock; - extern struct dentry *megasas_debugfs_root; +extern int megasas_blk_mq_poll(struct Scsi_Host *shost, unsigned int queue_num); extern void megasas_init_debugfs(void); extern void megasas_exit_debugfs(void); extern void megasas_setup_debugfs(struct megasas_instance *instance); @@ -210,6 +259,7 @@ extern void megasas_destroy_debugfs(struct megasas_instance *instance); void megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, u8 alt_status); +void megasas_free_irq_vectors(struct megasas_instance *instance); static u32 megasas_read_fw_status_reg_gen2(struct megasas_instance *instance); static int @@ -257,6 +307,7 @@ u32 megasas_readl(struct megasas_instance *instance, ret_val = readl(addr); i++; } while (ret_val == 0 && i < 3); + return ret_val; } else { return readl(addr); @@ -413,8 +464,9 @@ megasas_decode_evt(struct megasas_instance *instance) class_locale.word = le32_to_cpu(evt_detail->cl.word); 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_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; } @@ -425,6 +477,13 @@ megasas_decode_evt(struct megasas_instance *instance) (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); + } /** @@ -842,6 +901,10 @@ megasas_fire_cmd_skinny(struct megasas_instance *instance, &(regs)->inbound_high_queue_port); writel((lower_32_bits(frame_phys_addr) | (frame_count<<1))|1, &(regs)->inbound_low_queue_port); +#if !((defined(RHEL_MAJOR) && (RHEL_MAJOR == 8) && (RHEL_MINOR >= 2)) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(5,2,0))) + mmiowb(); +#endif spin_unlock_irqrestore(&instance->hba_lock, flags); } @@ -1099,7 +1162,7 @@ megasas_issue_polled(struct megasas_instance *instance, struct megasas_cmd *cmd) if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR) { dev_err(&instance->pdev->dev, "Failed from %s %d\n", __func__, __LINE__); - return DCMD_NOT_FIRED; + return DCMD_INIT; } instance->instancet->issue_dcmd(instance, cmd); @@ -1123,19 +1186,19 @@ megasas_issue_blocked_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, int timeout) { int ret = 0; - cmd->cmd_status_drv = MFI_STAT_INVALID_STATUS; + cmd->cmd_status_drv = DCMD_INIT; if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR) { dev_err(&instance->pdev->dev, "Failed from %s %d\n", __func__, __LINE__); - return DCMD_NOT_FIRED; + return DCMD_INIT; } instance->instancet->issue_dcmd(instance, cmd); if (timeout) { ret = wait_event_timeout(instance->int_cmd_wait_q, - cmd->cmd_status_drv != MFI_STAT_INVALID_STATUS, timeout * HZ); + cmd->cmd_status_drv != DCMD_INIT, timeout * HZ); if (!ret) { dev_err(&instance->pdev->dev, "DCMD(opcode: 0x%x) is timed out, func:%s\n", @@ -1144,10 +1207,9 @@ megasas_issue_blocked_cmd(struct megasas_instance *instance, } } 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; } /** @@ -1190,35 +1252,33 @@ megasas_issue_blocked_abort_cmd(struct megasas_instance *instance, cpu_to_le32(upper_32_bits(cmd_to_abort->frame_phys_addr)); cmd->sync_cmd = 1; - cmd->cmd_status_drv = MFI_STAT_INVALID_STATUS; + cmd->cmd_status_drv = DCMD_INIT; if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR) { dev_err(&instance->pdev->dev, "Failed from %s %d\n", __func__, __LINE__); - return DCMD_NOT_FIRED; + return DCMD_INIT; } instance->instancet->issue_dcmd(instance, cmd); if (timeout) { ret = wait_event_timeout(instance->abort_cmd_wait_q, - cmd->cmd_status_drv != MFI_STAT_INVALID_STATUS, timeout * HZ); + cmd->cmd_status_drv != DCMD_INIT, timeout * HZ); if (!ret) { 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__); + 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; } /** @@ -1759,6 +1819,7 @@ 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; @@ -1785,17 +1846,22 @@ megasas_queue_command(struct Scsi_Host *shost, struct scsi_cmnd *scmd) } } - 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) @@ -1887,6 +1953,8 @@ void megasas_set_dynamic_target_properties(struct scsi_device *sdev, mr_device_priv_data->is_tm_capable = raid->capability.tmCapable; + mr_device_priv_data->is_epd = + raid->flags.isEPD; } else if (instance->use_seqnum_jbod_fp) { pd_index = (sdev->channel * MEGASAS_MAX_DEV_PER_CHANNEL) + sdev->id; @@ -1898,21 +1966,59 @@ void megasas_set_dynamic_target_properties(struct scsi_device *sdev, if (is_target_prop && instance->tgt_prop->reset_tmo) { /* - * If FW provides a target reset timeout value, driver will use - * it. If not set, fallback to default values. + * FW provides a non-zero reset_tmo for NVMe EPD only. + * For rest of the cases, set the timeout values to default. */ mr_device_priv_data->target_reset_tmo = min_t(u8, instance->max_reset_tmo, instance->tgt_prop->reset_tmo); mr_device_priv_data->task_abort_tmo = instance->task_abort_tmo; } else { - mr_device_priv_data->target_reset_tmo = - MEGASAS_DEFAULT_TM_TIMEOUT; - mr_device_priv_data->task_abort_tmo = - MEGASAS_DEFAULT_TM_TIMEOUT; + mr_device_priv_data->target_reset_tmo = MEGASAS_DEFAULT_TM_TIMEOUT; + mr_device_priv_data->task_abort_tmo = MEGASAS_DEFAULT_TM_TIMEOUT; } } +inline void megasas_update_device_queue_depth(struct scsi_device *sdev, + u32 queue_depth) +{ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0)) + scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), queue_depth); +#else + scsi_change_queue_depth(sdev, queue_depth); +#endif +} + +/** + * megasas_blk_limits_max_hw_sectors - set hard and soft limit of max sectors for request + * @limits: the queue limits + * @max_hw_sectors: max hardware sectors in the usual 512b unit + */ +void megasas_blk_limits_max_hw_sectors(struct queue_limits *limits, unsigned int max_hw_sectors) +{ + if ((max_hw_sectors << 9) < PAGE_SIZE) { + max_hw_sectors = 1 << (PAGE_SHIFT - 9); + printk(KERN_INFO "%s: set to minimum %d\n", + __func__, max_hw_sectors); + } + + limits->max_hw_sectors = max_hw_sectors; + limits->max_sectors = min_t(unsigned int, max_hw_sectors, + 1024); +} + +/** + * megasas_queue_flag_set - atomically set a queue flag + * + * @flag: flag to be set + * @q: request queue + */ +static inline void +megasas_queue_flag_set(unsigned int flag, struct request_queue *q) +{ + set_bit(flag, &q->queue_flags); +} + /* * megasas_set_nvme_device_properties - * set nomerges=2 @@ -1939,12 +2045,11 @@ megasas_set_nvme_device_properties(struct scsi_device *sdev, u32 max_io_size) mr_nvme_pg_size = max_t(u32, instance->nvme_page_size, MR_DEFAULT_NVME_PAGE_SIZE); - blk_queue_max_hw_sectors(sdev->request_queue, (max_io_size / 512)); + megasas_blk_limits_max_hw_sectors(&sdev->request_queue->limits, + (max_io_size / 512)); - blk_queue_flag_set(QUEUE_FLAG_NOMERGES, sdev->request_queue); blk_queue_virt_boundary(sdev->request_queue, mr_nvme_pg_size - 1); } - /* * megasas_set_fw_assisted_qd - * set device queue depth to can_queue @@ -1964,6 +2069,7 @@ static void megasas_set_fw_assisted_qd(struct scsi_device *sdev, 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) { @@ -1980,15 +2086,16 @@ static void megasas_set_fw_assisted_qd(struct scsi_device *sdev, if (is_target_prop) { tgt_device_qd = le32_to_cpu(instance->tgt_prop->device_qdepth); - if (tgt_device_qd && - (tgt_device_qd <= instance->host->can_queue)) - device_qd = tgt_device_qd; + 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) + if (instance->enable_sdev_max_qd && + (MEGASAS_IS_LOGICAL(sdev) || (interface_type != UNKNOWN_DRIVE))) { device_qd = instance->host->can_queue; + } - scsi_change_queue_depth(sdev, device_qd); + megasas_update_device_queue_depth(sdev, device_qd); } /* @@ -2035,6 +2142,7 @@ static int megasas_slave_configure(struct scsi_device *sdev) struct megasas_instance *instance; int ret_target_prop = DCMD_FAILED; bool is_target_prop = false; + struct MR_PRIV_DEVICE *mr_device_priv_data; instance = megasas_lookup_instance(sdev->host->host_no); if (instance->pd_list_not_supported) { @@ -2064,6 +2172,10 @@ static int megasas_slave_configure(struct scsi_device *sdev) /* This sdev property may change post OCR */ megasas_set_dynamic_target_properties(sdev, is_target_prop); + mr_device_priv_data = sdev->hostdata; + if (!mr_device_priv_data->is_epd && MEGASAS_IS_LOGICAL(sdev)) + sdev->no_write_same = 1; + mutex_unlock(&instance->reset_mutex); return 0; @@ -2071,7 +2183,7 @@ static int megasas_slave_configure(struct scsi_device *sdev) 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; @@ -2089,6 +2201,9 @@ static int megasas_slave_alloc(struct scsi_device *sdev) goto scan_target; } return -ENXIO; + } else if (!MEGASAS_IS_LUN_VALID(sdev)) { + sdev_printk(KERN_INFO, sdev, "%s: invalid LUN\n", __func__); + return -ENXIO; } scan_target: @@ -2096,19 +2211,65 @@ scan_target: GFP_KERNEL); if (!mr_device_priv_data) return -ENOMEM; - sdev->hostdata = mr_device_priv_data; + 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, instance->r1_ldio_hint_default); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0)) + sdev->tagged_supported = 1; + scsi_activate_tcq(sdev, sdev->queue_depth); +#endif + return 0; } 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; } +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0)) +static int megasas_change_queue_depth(struct scsi_device *sdev, + int queue_depth, int reason) +{ + if (reason != SCSI_QDEPTH_DEFAULT) + return -EOPNOTSUPP; + + if (queue_depth > sdev->host->can_queue) + queue_depth = sdev->host->can_queue; + scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), + queue_depth); + + return queue_depth; +} +#endif + /* * megasas_complete_outstanding_ioctls - Complete outstanding ioctls after a * kill adapter @@ -2150,6 +2311,16 @@ static void megasas_complete_outstanding_ioctls(struct megasas_instance *instanc void megaraid_sas_kill_hba(struct megasas_instance *instance) { + dev_warn(&instance->pdev->dev, + "Kill HBA from %s %d\n", __func__, __LINE__); + + /* If adapter is already declared dead, do not issue kill HBA again */ + 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 */ @@ -2249,7 +2420,11 @@ static void megasas_complete_cmd_dpc(unsigned long instance_addr) megasas_check_and_restore_queue_depth(instance); } +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)) +static void megasas_sriov_heartbeat_handler(unsigned long instance_addr); +#else static void megasas_sriov_heartbeat_handler(struct timer_list *t); +#endif /** * megasas_start_timer - Initializes sriov heartbeat timer object @@ -2259,8 +2434,13 @@ static void megasas_sriov_heartbeat_handler(struct timer_list *t); void megasas_start_timer(struct megasas_instance *instance) { struct timer_list *timer = &instance->sriov_heartbeat_timer; - +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)) + init_timer(timer); + timer->data = (unsigned long)instance; + timer->function = megasas_sriov_heartbeat_handler; +#else timer_setup(timer, megasas_sriov_heartbeat_handler, 0); +#endif timer->expires = jiffies + MEGASAS_SRIOV_HEARTBEAT_INTERVAL_VF; add_timer(timer); } @@ -2330,6 +2510,8 @@ static int megasas_get_ld_vf_affiliation_111(struct megasas_instance *instance, megasas_return_cmd(instance, cmd); return -ENOMEM; } + memset(new_affiliation_111, 0, + sizeof(struct MR_LD_VF_AFFILIATION_111)); } memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); @@ -2437,6 +2619,8 @@ static int megasas_get_ld_vf_affiliation_12(struct megasas_instance *instance, megasas_return_cmd(instance, cmd); return -ENOMEM; } + memset(new_affiliation, 0, (MAX_LOGICAL_DRIVES + 1) * + sizeof(struct MR_LD_VF_AFFILIATION)); } memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); @@ -2603,6 +2787,8 @@ int megasas_sriov_start_heartbeat(struct megasas_instance *instance, retval = -ENOMEM; goto out; } + memset(instance->hb_host_mem, 0, + sizeof(struct MR_CTRL_HB_HOST_MEM)); } memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); @@ -2645,11 +2831,17 @@ out: } /* Handler for SR-IOV heartbeat */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)) +static void megasas_sriov_heartbeat_handler(unsigned long instance_addr) +{ + struct megasas_instance *instance = + (struct megasas_instance *)instance_addr; +#else static void megasas_sriov_heartbeat_handler(struct timer_list *t) { struct megasas_instance *instance = from_timer(instance, t, sriov_heartbeat_timer); - +#endif if (instance->hb_host_mem->HB.fwCounter != instance->hb_host_mem->HB.driverCounter) { instance->hb_host_mem->HB.driverCounter = @@ -2726,7 +2918,7 @@ static int megasas_wait_for_outstanding(struct megasas_instance *instance) "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); @@ -2864,7 +3056,11 @@ blk_eh_timer_return megasas_reset_timer(struct scsi_cmnd *scmd) if (time_after(jiffies, scmd->jiffies_at_alloc + (scmd_timeout * 2) * HZ)) { - return BLK_EH_DONE; + /* BLK_EH_NOT_HANDLED and BLK_EH_DONE are macros provided by + * different kernels for return value, as both macros are defined as 0 + * so returning 0 to avoid kernel version based checks. + */ + return 0; } instance = (struct megasas_instance *)scmd->device->host->hostdata; @@ -2878,16 +3074,18 @@ blk_eh_timer_return megasas_reset_timer(struct scsi_cmnd *scmd) spin_unlock_irqrestore(instance->host->host_lock, flags); } + return BLK_EH_RESET_TIMER; } /** - * 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. + * 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. */ inline void megasas_dump(void *buf, int sz, int format) @@ -2906,28 +3104,28 @@ megasas_dump(void *buf, int sz, int format) printk(KERN_CONT "\n"); } -/** - * megasas_dump_reg_set - This function will print hexdump of register set - * @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. +/* + * megasas_dump_reg_set - This function will print hexdump of register set + * @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. */ inline void megasas_dump_reg_set(void __iomem *reg_set) { - unsigned int i, sz = 256; - u32 __iomem *reg = (u32 __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])); + 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 + * of SCSI IO + * scmd- SCSI command pointer of SCSI IO */ void megasas_dump_fusion_io(struct scsi_cmnd *scmd) @@ -2935,40 +3133,40 @@ 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); + "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); + 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); + 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); + 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. + * 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) @@ -2985,7 +3183,6 @@ megasas_dump_sys_regs(void __iomem *reg_set, char *buf) } return bytes_wrote; } - /** * megasas_reset_bus_host - Bus & host reset handler entry point */ @@ -2998,11 +3195,11 @@ static int megasas_reset_bus_host(struct scsi_cmnd *scmd) scmd_printk(KERN_INFO, scmd, "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", + "SCSI host state: %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 @@ -3106,6 +3303,48 @@ megasas_bios_param(struct scsi_device *sdev, struct block_device *bdev, return 0; } +#if defined(HOST_TAGSET_SUPPORT) +static int megasas_map_queues(struct Scsi_Host *shost) +{ + struct megasas_instance *instance; + int qoff, offset; + struct blk_mq_queue_map *map; + + instance = (struct megasas_instance *)shost->hostdata; + + if (shost->nr_hw_queues == 1) + return 0; + + offset = instance->low_latency_index_start; + qoff = 0; + + /* Setup Default hctx */ + map = &shost->tag_set.map[HCTX_TYPE_DEFAULT]; + map->nr_queues = instance->msix_vectors - offset; + map->queue_offset = 0; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5,12,0)) + return blk_mq_pci_map_queues(map, instance->pdev, offset); +#else + blk_mq_pci_map_queues(map, instance->pdev, offset); + qoff += map->nr_queues; + + /* Setup Poll hctx */ + map = &shost->tag_set.map[HCTX_TYPE_POLL]; + map->nr_queues = instance->iopoll_q_count; + if (map->nr_queues) { + /* + * The poll queue(s) doesn't have an IRQ (and hence IRQ + * affinity), so use the regular blk-mq cpu mapping + */ + map->queue_offset = qoff; + blk_mq_map_queues(map); + } + return 0; +#endif +} + +#endif + static void megasas_aen_polling(struct work_struct *work); /** @@ -3160,7 +3399,7 @@ megasas_service_aen(struct megasas_instance *instance, struct megasas_cmd *cmd) } static ssize_t -fw_crash_buffer_store(struct device *cdev, +megasas_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); @@ -3179,7 +3418,7 @@ fw_crash_buffer_store(struct device *cdev, } static ssize_t -fw_crash_buffer_show(struct device *cdev, +megasas_fw_crash_buffer_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); @@ -3203,6 +3442,7 @@ fw_crash_buffer_show(struct device *cdev, return -EINVAL; } + if (buff_offset > (instance->fw_crash_buffer_size * dmachunk)) { dev_err(&instance->pdev->dev, "Firmware crash dump offset is out of range\n"); @@ -3224,7 +3464,7 @@ fw_crash_buffer_show(struct device *cdev, } static ssize_t -fw_crash_buffer_size_show(struct device *cdev, +megasas_fw_crash_buffer_size_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); @@ -3236,7 +3476,7 @@ fw_crash_buffer_size_show(struct device *cdev, } static ssize_t -fw_crash_state_store(struct device *cdev, +megasas_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); @@ -3271,7 +3511,7 @@ fw_crash_state_store(struct device *cdev, } static ssize_t -fw_crash_state_show(struct device *cdev, +megasas_fw_crash_state_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); @@ -3282,14 +3522,14 @@ fw_crash_state_show(struct device *cdev, } static ssize_t -page_size_show(struct device *cdev, +megasas_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 -ldio_outstanding_show(struct device *cdev, struct device_attribute *attr, +megasas_ldio_outstanding_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); @@ -3299,7 +3539,7 @@ ldio_outstanding_show(struct device *cdev, struct device_attribute *attr, } static ssize_t -fw_cmds_outstanding_show(struct device *cdev, +megasas_fw_cmds_outstanding_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); @@ -3309,18 +3549,47 @@ fw_cmds_outstanding_show(struct device *cdev, } static ssize_t -enable_sdev_max_qd_show(struct device *cdev, - struct device_attribute *attr, char *buf) +megasas_ldio_hint_count_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; + + sscanf(buf, "%u", &val); + + if (val < 4 || val > 16) + instance->r1_ldio_hint_default = MR_R1_LDIO_PIGGYBACK_DEFAULT; + else + instance->r1_ldio_hint_default = val; + + return strlen(buf); +} + +static ssize_t +megasas_ldio_hint_count_show(struct device *cdev, struct device_attribute *attr, + char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); struct megasas_instance *instance = (struct megasas_instance *)shost->hostdata; - return snprintf(buf, PAGE_SIZE, "%d\n", instance->enable_sdev_max_qd); + return snprintf(buf, PAGE_SIZE, "%ld\n", (unsigned long) instance->r1_ldio_hint_default); } static ssize_t -enable_sdev_max_qd_store(struct device *cdev, - struct device_attribute *attr, const char *buf, size_t count) +megasas_enable_sdev_max_qd_show(struct device *cdev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct megasas_instance *instance = (struct megasas_instance *)shost->hostdata; + + return snprintf(buf, PAGE_SIZE, "%ld\n", + (unsigned long) instance->enable_sdev_max_qd); +} + +static ssize_t +megasas_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; @@ -3328,7 +3597,7 @@ enable_sdev_max_qd_store(struct device *cdev, 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; @@ -3339,7 +3608,7 @@ enable_sdev_max_qd_store(struct device *cdev, 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; @@ -3351,37 +3620,47 @@ enable_sdev_max_qd_store(struct device *cdev, } static ssize_t -dump_system_regs_show(struct device *cdev, - struct device_attribute *attr, char *buf) +megasas_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; - + 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) +megasas_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; + struct megasas_instance *instance = (struct megasas_instance *)shost->hostdata; + + return snprintf(buf, PAGE_SIZE, "%ld\n", (unsigned long)instance->map_id); - 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 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 DEVICE_ATTR(ldio_hint_count, S_IRUGO | S_IWUSR, + megasas_ldio_hint_count_show, megasas_ldio_hint_count_store); +static DEVICE_ATTR(enable_sdev_max_qd, S_IRUGO | S_IWUSR, + megasas_enable_sdev_max_qd_show, megasas_enable_sdev_max_qd_store); +static DEVICE_ATTR(dump_system_regs, S_IRUGO, + megasas_dump_system_regs_show, NULL); +static DEVICE_ATTR(raid_map_id, S_IRUGO, + megasas_raid_map_id_show, NULL); + static struct device_attribute *megaraid_host_attrs[] = { &dev_attr_fw_crash_buffer_size, @@ -3390,6 +3669,7 @@ static struct device_attribute *megaraid_host_attrs[] = { &dev_attr_page_size, &dev_attr_ldio_outstanding, &dev_attr_fw_cmds_outstanding, + &dev_attr_ldio_hint_count, &dev_attr_enable_sdev_max_qd, &dev_attr_dump_system_regs, &dev_attr_raid_map_id, @@ -3402,7 +3682,7 @@ static struct device_attribute *megaraid_host_attrs[] = { static struct scsi_host_template megasas_template = { .module = THIS_MODULE, - .name = "Avago SAS based MegaRAID driver", + .name = "Broadcom SAS based MegaRAID driver", .proc_name = "megaraid_sas", .slave_configure = megasas_slave_configure, .slave_alloc = megasas_slave_alloc, @@ -3414,9 +3694,20 @@ static struct scsi_host_template megasas_template = { .eh_timed_out = megasas_reset_timer, .shost_attrs = megaraid_host_attrs, .bios_param = megasas_bios_param, +#if defined(HOST_TAGSET_SUPPORT) + .map_queues = megasas_map_queues, +#endif +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,12,0)) + .mq_poll = megasas_blk_mq_poll, +#endif +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0)) + .use_clustering = ENABLE_CLUSTERING, +#endif +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0)) + .change_queue_depth = megasas_change_queue_depth, +#else .change_queue_depth = scsi_change_queue_depth, - .max_segment_size = 0xffffffff, - .no_write_same = 1, +#endif }; /** @@ -3432,7 +3723,12 @@ static void megasas_complete_int_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd) { - cmd->cmd_status_drv = cmd->frame->io.cmd_status; + + if (cmd->cmd_status_drv == DCMD_INIT) + cmd->cmd_status_drv = + (cmd->frame->io.cmd_status == MFI_STAT_OK) ? + DCMD_SUCCESS : DCMD_FAILED; + wake_up(&instance->int_cmd_wait_q); } @@ -3451,11 +3747,27 @@ megasas_complete_abort(struct megasas_instance *instance, { if (cmd->sync_cmd) { cmd->sync_cmd = 0; - cmd->cmd_status_drv = 0; + cmd->cmd_status_drv = DCMD_SUCCESS; wake_up(&instance->abort_cmd_wait_q); } } +static void +megasas_set_ld_removed_by_fw(struct megasas_instance *instance) +{ + uint i; + + for (i = 0; (i < MEGASAS_MAX_LD_IDS); i++) { + if ((instance->ld_ids_prev[i] != 0xff) && + (instance->ld_ids_from_raidmap[i] == 0xff)) { + if (megasas_dbg_lvl & LD_PD_DEBUG) + dev_info(&instance->pdev->dev, + "LD target ID %d removed from RAID map\n", i); + instance->ld_tgtid_status[i] = LD_TARGET_ID_DELETED; + } + } +} + /** * megasas_complete_cmd - Completes a command * @instance: Adapter soft state @@ -3506,8 +3818,7 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, megasas_complete_int_cmd(instance, cmd); break; } - /* fall through */ - + /* Fall through */ case MFI_CMD_LD_READ: case MFI_CMD_LD_WRITE: @@ -3618,9 +3929,22 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, fusion->fast_path_io = 0; } + /* Below case is for Invader onwards controller. + * Driver will know VDs removed by firmware much earlier than + * waiting for events. It will allow driver to avoid sending IOs + * to the VDs which are removed. + * + * Without below check, there can be a small window + * possible where driver send IOs to the removed VD but FW might have + * reused same target id for other VD. + */ + 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 || @@ -3727,7 +4051,7 @@ megasas_issue_pending_cmds_again(struct megasas_instance *instance) dev_notice(&instance->pdev->dev, "%p synchronous cmd" "on the internal reset queue," "issue it again.\n", cmd); - cmd->cmd_status_drv = MFI_STAT_INVALID_STATUS; + cmd->cmd_status_drv = DCMD_INIT; instance->instancet->fire_cmd(instance, cmd->frame_phys_addr, 0, instance->reg_set); @@ -3894,9 +4218,7 @@ megasas_deplete_reply_queue(struct megasas_instance *instance, instance->reg_set)) == 1) { return IRQ_HANDLED; } - - mfiStatus = instance->instancet->clear_intr(instance); - if (mfiStatus == 0) { + if ((mfiStatus = instance->instancet->clear_intr(instance)) == 0 ) { /* Hardware may not set outbound_intr_status in MSI-X mode */ if (!instance->msix_vectors) return IRQ_NONE; @@ -4001,9 +4323,10 @@ megasas_transition_to_ready(struct megasas_instance *instance, int ocr) case MFI_STATE_FAULT: 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__); + "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; break; @@ -4252,7 +4575,7 @@ static int megasas_create_frame_pool(struct megasas_instance *instance) cmd = instance->cmd_list[i]; - cmd->frame = dma_pool_zalloc(instance->frame_dma_pool, + cmd->frame = dma_pool_alloc(instance->frame_dma_pool, GFP_KERNEL, &cmd->frame_phys_addr); cmd->sense = dma_pool_alloc(instance->sense_dma_pool, @@ -4267,7 +4590,7 @@ static int megasas_create_frame_pool(struct megasas_instance *instance) megasas_teardown_frame_pool(instance); return -ENOMEM; } - + memset(cmd->frame, 0, instance->mfi_frame_size); cmd->frame->io.context = cpu_to_le32(cmd->index); cmd->frame->io.pad_0 = 0; if ((instance->adapter_type == MFI_SERIES) && reset_devices) @@ -4339,7 +4662,6 @@ int megasas_alloc_cmds(struct megasas_instance *instance) return -ENOMEM; } - memset(instance->cmd_list, 0, sizeof(struct megasas_cmd *) *max_cmd); for (i = 0; i < max_cmd; i++) { instance->cmd_list[i] = kmalloc(sizeof(struct megasas_cmd), @@ -4575,7 +4897,7 @@ megasas_get_pd_list(struct megasas_instance *instance) 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)); + __func__, le32_to_cpu(ci->count)); if ((le32_to_cpu(ci->count) > (MEGASAS_MAX_PD_CHANNELS * MEGASAS_MAX_DEV_PER_CHANNEL))) @@ -4593,9 +4915,9 @@ megasas_get_pd_list(struct megasas_instance *instance) 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%d: targetID: 0x%03x deviceType:0x%x\n", + pd_index, le16_to_cpu(pd_addr->deviceId), + pd_addr->scsiDevType); pd_addr++; } @@ -4701,7 +5023,7 @@ megasas_get_ld_list(struct megasas_instance *instance) case DCMD_SUCCESS: if (megasas_dbg_lvl & LD_PD_DEBUG) dev_info(&instance->pdev->dev, "%s, LD count: 0x%x\n", - __func__, ld_count); + __func__, ld_count); if (ld_count > instance->fw_supported_vd_count) break; @@ -4714,8 +5036,8 @@ megasas_get_ld_list(struct megasas_instance *instance) 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); + "LD%d: targetID: 0x%03x\n", + ld_index, ids); } } @@ -4821,8 +5143,8 @@ megasas_ld_list_query(struct megasas_instance *instance, u8 query_type) if (megasas_dbg_lvl & LD_PD_DEBUG) dev_info(&instance->pdev->dev, "%s, LD count: 0x%x\n", - __func__, tgtid_count); - + __func__, tgtid_count); + if ((tgtid_count > (instance->fw_supported_vd_count))) break; @@ -4832,7 +5154,7 @@ megasas_ld_list_query(struct megasas_instance *instance, u8 query_type) 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]); + ld_index, ci->targetId[ld_index]); } break; @@ -5038,13 +5360,14 @@ static void megasas_update_ext_vd_details(struct megasas_instance *instance) 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 + * Desc: Fill in snapdump properties + * Status: MFI_STAT_OK- Command successful */ + void megasas_get_snapdump_properties(struct megasas_instance *instance) { int ret = 0; @@ -5062,12 +5385,13 @@ void megasas_get_snapdump_properties(struct megasas_instance *instance) cmd = megasas_get_cmd(instance); if (!cmd) { - dev_dbg(&instance->pdev->dev, "Failed to get a free cmd\n"); + dev_err(&instance->pdev->dev, "Failed to get a free cmd" + "Fail from %s %d\n", __func__, __LINE__); return; } dcmd = &cmd->frame->dcmd; - + memset(ci, 0, sizeof(*ci)); memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); @@ -5115,12 +5439,14 @@ void megasas_get_snapdump_properties(struct megasas_instance *instance) __func__, __LINE__); break; } + } if (ret != DCMD_TIMEOUT) megasas_return_cmd(instance, cmd); } + /** * megasas_get_controller_info - Returns FW's controller structure * @instance: Adapter soft state @@ -5209,7 +5535,7 @@ megasas_get_ctrl_info(struct megasas_instance *instance) 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; @@ -5231,10 +5557,10 @@ megasas_get_ctrl_info(struct megasas_instance *instance) dev_info(&instance->pdev->dev, "FW provided TM TaskAbort/Reset timeout\t: %d secs/%d secs\n", instance->task_abort_tmo, instance->max_reset_tmo); - dev_info(&instance->pdev->dev, "JBOD sequence map support\t: %s\n", - instance->support_seqnum_jbod_fp ? "Yes" : "No"); dev_info(&instance->pdev->dev, "PCI Lane Margining support\t: %s\n", instance->support_pci_lane_margining ? "Yes" : "No"); + dev_info(&instance->pdev->dev, "JBOD sequence map support\t: %s\n", + instance->support_seqnum_jbod_fp ? "Yes" : "No"); break; @@ -5387,6 +5713,18 @@ megasas_issue_init_mfi(struct megasas_instance *instance) init_frame->cmd = MFI_CMD_INIT; init_frame->cmd_status = MFI_STAT_INVALID_STATUS; + + if (instance->verbuf) { + snprintf((char *)instance->verbuf, strlen(MEGASAS_VERSION) + 2, + "%s", MEGASAS_VERSION); + + if (reset_devices) + strcat((char *)instance->verbuf, "-kdump"); + + init_frame->driver_ver_lo = cpu_to_le32(instance->verbuf_h); + init_frame->driver_ver_hi = 0; + } + init_frame->queue_info_new_phys_addr_lo = cpu_to_le32(lower_32_bits(initq_info_h)); init_frame->queue_info_new_phys_addr_hi = @@ -5512,26 +5850,71 @@ fail_alloc_cmds: return 1; } +/** + * megasas_get_irq - Return IRQ vector associated with MSI-x index + * @instance: Adapter soft state + * @msix_index: MSI-x index + * + * Return IRQ vector associated with passed MSI-x index. + */ +inline unsigned int +megasas_get_irq(struct megasas_instance *instance, int msix_index) +{ + + unsigned int irq; +#ifndef USE_MANAGED_IRQ_API + if (instance->msix_vectors) + irq = instance->msixentry[msix_index].vector; + else + irq = instance->pdev->irq; +#else + irq = pci_irq_vector(instance->pdev, msix_index); +#endif + return irq; + +} + +/** + * megasas_irq_set_affinity_hint - Set IRQ affinity hint for relevant kernels + * @msix_vector: MSI-x vector to which IRQ affinity hint + * is provided + * @mask: CPU mask + * return: true: irq_set_affinity_hint successful + * false: irq_set_affinity_hint failed + */ +static inline bool +megasas_irq_set_affinity_hint(struct megasas_instance *instance, + unsigned int msix_vector, + const struct cpumask *mask) +{ + if (instance->smp_affinity_enable) { + if (irq_set_affinity_hint(msix_vector, mask)) + return false; + } + return true; +} + +#if defined(ENABLE_IRQ_POLL) 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->os_irq = megasas_get_irq(instance, i); irq_ctx->irq_poll_scheduled = false; irq_poll_init(&irq_ctx->irqpoll, instance->threshold_reply_count, megasas_irqpoll); } } +#endif -/* +/** * megasas_setup_irqs_ioapic - register legacy interrupts. * @instance: Adapter soft state * @@ -5547,12 +5930,15 @@ megasas_setup_irqs_ioapic(struct megasas_instance *instance) pdev = instance->pdev; instance->irq_context[0].instance = instance; instance->irq_context[0].MSIxIndex = 0; - if (request_irq(pci_irq_vector(pdev, 0), - instance->instancet->service_isr, IRQF_SHARED, - "megasas", &instance->irq_context[0])) { + atomic_set(&instance->irq_context[0].in_use, 0); + snprintf(instance->irq_context->name, MEGASAS_MSIX_NAME_LEN, "%s%u", + "megasas", instance->host->host_no); + if (request_irq(megasas_get_irq(instance, 0), + instance->instancet->service_isr, IRQF_SHARED, + instance->irq_context->name, &instance->irq_context[0])) { dev_err(&instance->pdev->dev, - "Failed to register IRQ from %s %d\n", - __func__, __LINE__); + "Failed to register IRQ from %s %d\n", + __func__, __LINE__); return -1; } instance->perf_mode = MR_LATENCY_PERF_MODE; @@ -5560,6 +5946,65 @@ megasas_setup_irqs_ioapic(struct megasas_instance *instance) return 0; } +/** + * megasas_setup_reply_map: Create mapping table of CPU-reply queue map + * @instance: Adapter soft state + * return: void + */ +static void megasas_setup_reply_map(struct megasas_instance *instance) +{ + unsigned int queue, cpu, low_latency_index_start; + const struct cpumask *mask; + + low_latency_index_start = instance->low_latency_index_start; +#ifndef USE_MANAGED_IRQ_API + /* Set affinity for all low latency queues*/ + cpu = cpumask_first(cpu_online_mask); + for (queue = low_latency_index_start; queue < instance->msix_vectors; queue++) { + mask = get_cpu_mask(cpu); + if (!megasas_irq_set_affinity_hint(instance, + megasas_get_irq(instance, queue), + mask)) + dev_err(&instance->pdev->dev, + "Failed to set affinity hint for cpu %d\n", + cpu); + + cpu = cpumask_next(cpu, cpu_online_mask); + } + + /* Setup CPU to reply queues mapping for all low latency queues */ + queue = low_latency_index_start; + for_each_online_cpu(cpu) { + instance->reply_map[cpu] = queue; + if (queue == (instance->msix_vectors - 1)) + queue = low_latency_index_start; + else + queue++; + } + +#else + for (queue = low_latency_index_start; queue < instance->msix_vectors; queue++) { + mask = pci_irq_get_affinity(instance->pdev, queue); + if (!mask) + goto fallback; + + for_each_cpu(cpu, mask) + instance->reply_map[cpu] = queue; + } + return; + +fallback: + 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++; + } +#endif +} + /** * megasas_setup_irqs_msix - register MSI-x interrupts. * @instance: Adapter soft state @@ -5576,38 +6021,38 @@ megasas_setup_irqs_msix(struct megasas_instance *instance, u8 is_probe) struct pci_dev *pdev; pdev = instance->pdev; - + /* Try MSI-x */ for (i = 0; i < instance->msix_vectors; i++) { instance->irq_context[i].instance = instance; instance->irq_context[i].MSIxIndex = i; - if (request_irq(pci_irq_vector(pdev, i), - instance->instancet->service_isr, 0, "megasas", + atomic_set(&instance->irq_context[i].in_use, 0); + snprintf(instance->irq_context[i].name, MEGASAS_MSIX_NAME_LEN, "%s%u-msix%d", + "megasas", instance->host->host_no, i); + if (request_irq(megasas_get_irq(instance, i), + 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++) { - if (j < instance->low_latency_index_start) - irq_set_affinity_hint( - pci_irq_vector(pdev, j), NULL); - free_irq(pci_irq_vector(pdev, j), + for (j = 0; j < i; j++) + free_irq(megasas_get_irq(instance, 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); + megasas_free_irq_vectors(instance); return megasas_setup_irqs_ioapic(instance); } else { return -1; } } } + + megasas_setup_reply_map(instance); return 0; } - /* * megasas_destroy_irqs- unregister interrupts. * @instance: Adapter soft state @@ -5617,6 +6062,7 @@ static void megasas_destroy_irqs(struct megasas_instance *instance) { int i; +#if defined(ENABLE_IRQ_POLL) int count; struct megasas_irq_context *irq_ctx; @@ -5627,18 +6073,20 @@ megasas_destroy_irqs(struct megasas_instance *instance) { irq_poll_disable(&irq_ctx->irqpoll); } } +#endif - if (instance->msix_vectors) + 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), + if (i >= instance->low_latency_index_start) + megasas_irq_set_affinity_hint(instance, + megasas_get_irq(instance, i), + NULL); + free_irq(megasas_get_irq(instance, i), &instance->irq_context[i]); } - else - free_irq(pci_irq_vector(instance->pdev, 0), - &instance->irq_context[0]); + } else { + free_irq(megasas_get_irq(instance, 0), &instance->irq_context[0]); + } } /** @@ -5699,34 +6147,148 @@ skip_alloc: instance->use_seqnum_jbod_fp = false; } -static void megasas_setup_reply_map(struct megasas_instance *instance) +#if defined(USE_MANAGED_IRQ_API) +/** + * 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) { - const struct cpumask *mask; - unsigned int queue, cpu, low_latency_index_start; + int i; + int local_numa_node; - low_latency_index_start = instance->low_latency_index_start; + if (instance->perf_mode == MR_BALANCED_PERF_MODE) { + local_numa_node = dev_to_node(&instance->pdev->dev); - for (queue = low_latency_index_start; queue < instance->msix_vectors; queue++) { - mask = pci_irq_get_affinity(instance->pdev, queue); - if (!mask) - goto fallback; - - for_each_cpu(cpu, mask) - instance->reply_map[cpu] = queue; - } - return; - -fallback: - 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++; + for (i = 0; i < instance->low_latency_index_start; i++) + megasas_irq_set_affinity_hint(instance, + megasas_get_irq(instance, 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 | PCI_IRQ_ALL_TYPES; + else + descp = NULL; + + /* Do not allocate msix vectors for poll_queues. + * msix_vectors is always within a range of FW supported reply queue. + */ + i = pci_alloc_irq_vectors_affinity(instance->pdev, + instance->low_latency_index_start, + instance->msix_vectors - instance->iopoll_q_count, irq_flags, descp); + + return i; +} +#endif + +/** + * 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; +#if defined(USE_MANAGED_IRQ_API) + unsigned int num_msix_req; + instance->iopoll_q_count = 0; + + if ((instance->adapter_type != MFI_SERIES) && + poll_queues) { + + instance->perf_mode = MR_LATENCY_PERF_MODE; + instance->low_latency_index_start = 1; + + /* reserve for default and non-mananged pre-vector. */ + if (instance->msix_vectors > (poll_queues + 2)) + instance->iopoll_q_count = poll_queues; + else + instance->iopoll_q_count = 0; + + num_msix_req = num_online_cpus() + instance->low_latency_index_start; + instance->msix_vectors = min(num_msix_req, + instance->msix_vectors); + + } + + i = __megasas_alloc_irq_vectors(instance); + + if (((instance->perf_mode == MR_BALANCED_PERF_MODE) + || instance->iopoll_q_count) && + (i != (instance->msix_vectors - instance->iopoll_q_count))) { + if (instance->msix_vectors) + pci_free_irq_vectors(instance->pdev); + /* Disable Balanced IOPS mode and try realloc vectors */ + instance->perf_mode = MR_LATENCY_PERF_MODE; + instance->low_latency_index_start = 1; + num_msix_req = num_online_cpus() + instance->low_latency_index_start; + + instance->msix_vectors = min(num_msix_req, + instance->msix_vectors); + + instance->iopoll_q_count = 0; + i = __megasas_alloc_irq_vectors(instance); + + } + + if (instance->smp_affinity_enable) + megasas_set_high_iops_queue_affinity_hint(instance); + +#else + for (i = 0; i < instance->msix_vectors; i++) + instance->msixentry[i].entry = i; + + i = pci_enable_msix_range(instance->pdev, instance->msixentry, + 1, instance->msix_vectors); + + if (i != instance->msix_vectors) { + instance->perf_mode = MR_LATENCY_PERF_MODE; + instance->low_latency_index_start = 1; + } + +#endif + dev_info(&instance->pdev->dev, + "requested/available msix: %d/%d\n", + instance->msix_vectors - instance->iopoll_q_count, + i); + + if (i > 0) + instance->msix_vectors = i; + else + instance->msix_vectors = 0; + +} + +/** + * megasas_free_irq_vectors - Freeup IRQ vectors/disable MSI-x vectors + * @instance: Adapter soft state + */ +void megasas_free_irq_vectors(struct megasas_instance *instance) +{ + if (instance->msix_vectors) +#ifndef USE_MANAGED_IRQ_API + pci_disable_msix(instance->pdev); +#else + pci_free_irq_vectors(instance->pdev); +#endif +} + + + /** * megasas_get_device_list - Get the PD and LD device list from FW. * @instance: Adapter soft state @@ -5744,8 +6306,10 @@ int megasas_get_device_list(struct megasas_instance *instance) memset(instance->ld_ids, 0xff, MEGASAS_MAX_LD_IDS); if (instance->enable_fw_dev_list) { - if (megasas_host_device_list_query(instance, true)) + if (megasas_host_device_list_query(instance, true)) { + dev_err(&instance->pdev->dev, "failed to get host device list\n"); return FAILED; + } } else { if (megasas_get_pd_list(instance) < 0) { dev_err(&instance->pdev->dev, "failed to get PD list\n"); @@ -5762,87 +6326,6 @@ int megasas_get_device_list(struct megasas_instance *instance) 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); -} /** * megasas_init_fw - Initializes the FW @@ -5854,7 +6337,7 @@ megasas_alloc_irq_vectors(struct megasas_instance *instance) static int megasas_init_fw(struct megasas_instance *instance) { u32 max_sectors_1; - u32 max_sectors_2, tmp_sectors, msix_enable; + u32 max_sectors_2, tmp_sectors, msix_enable, num_msix_req; u32 scratch_pad_1, scratch_pad_2, scratch_pad_3, status_reg; resource_size_t base_addr; void *base_addr_phys; @@ -5864,7 +6347,6 @@ static int megasas_init_fw(struct megasas_instance *instance) struct IOV_111 *iovPtr; struct fusion_context *fusion; bool intr_coalescing; - unsigned int num_msix_req; u16 lnksta, speed; fusion = instance->ctrl_context; @@ -5879,18 +6361,18 @@ static int megasas_init_fw(struct megasas_instance *instance) } base_addr = pci_resource_start(instance->pdev, instance->bar); - instance->reg_set = ioremap_nocache(base_addr, 8192); + instance->reg_set = ioremap(base_addr, 8192); + + base_addr_phys = &base_addr; + dev_printk(KERN_DEBUG, &instance->pdev->dev, + "BAR:0x%lx BAR's base_addr(phys):0x%pa mapped virt_addr:0x%p\n", + instance->bar, base_addr_phys, instance->reg_set); if (!instance->reg_set) { dev_printk(KERN_DEBUG, &instance->pdev->dev, "Failed to map IO mem\n"); goto fail_ioremap; } - base_addr_phys = &base_addr; - dev_printk(KERN_DEBUG, &instance->pdev->dev, - "BAR:0x%lx BAR's base_addr(phys):%pa mapped virt_addr:0x%p\n", - instance->bar, base_addr_phys, instance->reg_set); - if (instance->adapter_type != MFI_SERIES) instance->instancet = &megasas_instance_template_fusion; else { @@ -5922,7 +6404,7 @@ static int megasas_init_fw(struct megasas_instance *instance) __func__); if (instance->adapter_type != MFI_SERIES) { status_reg = instance->instancet->read_fw_status_reg( - instance); + instance); if (status_reg & MFI_RESET_ADAPTER) { if (megasas_adp_reset_wait_for_ready (instance, true, 0) == FAILED) @@ -5968,6 +6450,7 @@ static int megasas_init_fw(struct megasas_instance *instance) 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; @@ -5987,7 +6470,6 @@ static int megasas_init_fw(struct megasas_instance *instance) msix_enable = (instance->instancet->read_fw_status_reg(instance) & 0x4000000) >> 0x1a; if (msix_enable && !msix_disable) { - scratch_pad_1 = megasas_readl (instance, &instance->reg_set->outbound_scratch_pad_1); /* Check max MSI-X vectors */ @@ -6002,13 +6484,11 @@ static int megasas_init_fw(struct megasas_instance *instance) >> MR_MAX_REPLY_QUEUES_EXT_OFFSET_SHIFT) + 1; /* - * For Invader series, > 8 MSI-x vectors - * supported by FW/HW implies combined - * reply queue mode is enabled. - * For Ventura series, > 16 MSI-x vectors - * supported by FW/HW implies combined - * reply queue mode is enabled. - */ + * For Invader series, > 8 MSI-x vectors supported by FW/HW implies + * combined reply queue mode is enabled. + * For Ventura series, > 16 MSI-x vectors supported by FW/HW implies + * combined reply queue mode is enabled. + */ switch (instance->adapter_type) { case INVADER_SERIES: if (instance->msix_vectors > 8) @@ -6024,13 +6504,6 @@ static int megasas_init_fw(struct megasas_instance *instance) if (rdpq_enable) instance->is_rdpq = (scratch_pad_1 & MR_RDPQ_MODE_OFFSET) ? 1 : 0; - - 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 @@ -6045,14 +6518,19 @@ static int megasas_init_fw(struct megasas_instance *instance) } dev_info(&instance->pdev->dev, - "firmware supports msix\t: (%d)", - instance->msix_vectors); + "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; + intr_coalescing = (scratch_pad_1 & MR_INTR_COALESCING_SUPPORT_OFFSET) ? + true : false; /* * For Aero (if some conditions are met), driver will configure a @@ -6073,8 +6551,6 @@ static int megasas_init_fw(struct megasas_instance *instance) * 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)) @@ -6082,7 +6558,7 @@ static int megasas_init_fw(struct megasas_instance *instance) else 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; @@ -6095,8 +6571,8 @@ static int megasas_init_fw(struct megasas_instance *instance) 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. @@ -6106,14 +6582,14 @@ static int megasas_init_fw(struct megasas_instance *instance) (perf_mode <= MR_LATENCY_PERF_MODE)) instance->perf_mode = perf_mode; /* - * If intr coalescing is not supported by controller FW, then IOPS + * 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 (!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; @@ -6125,6 +6601,15 @@ static int megasas_init_fw(struct megasas_instance *instance) instance->msix_vectors = min(num_msix_req, instance->msix_vectors); + /* + * Disable SMP affinity if number of logical CPUs exceeds number of + * MSI-x vectors configured + */ + if (!instance->msix_combined) { + instance->msix_load_balance = true; + instance->smp_affinity_enable = false; + } + megasas_alloc_irq_vectors(instance); if (!instance->msix_vectors) instance->msix_load_balance = false; @@ -6143,13 +6628,13 @@ static int megasas_init_fw(struct megasas_instance *instance) MPI2_REPLY_POST_HOST_INDEX_OFFSET); } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) if (!instance->msix_vectors) { i = pci_alloc_irq_vectors(instance->pdev, 1, 1, PCI_IRQ_LEGACY); if (i < 0) goto fail_init_adapter; } - - megasas_setup_reply_map(instance); +#endif dev_info(&instance->pdev->dev, "current msix/online cpus\t: (%d/%d)\n", @@ -6188,9 +6673,10 @@ static int megasas_init_fw(struct megasas_instance *instance) megasas_setup_irqs_ioapic(instance)) goto fail_init_adapter; +#if defined(ENABLE_IRQ_POLL) if (instance->adapter_type != MFI_SERIES) megasas_setup_irq_poll(instance); - +#endif instance->instancet->enable_intr(instance); dev_info(&instance->pdev->dev, "INIT adapter done\n"); @@ -6368,8 +6854,7 @@ fail_get_ld_pd_list: instance->instancet->disable_intr(instance); megasas_destroy_irqs(instance); fail_init_adapter: - if (instance->msix_vectors) - pci_free_irq_vectors(instance->pdev); + megasas_free_irq_vectors(instance); instance->msix_vectors = 0; fail_alloc_dma_buf: megasas_free_ctrl_dma_buffers(instance); @@ -6440,7 +6925,7 @@ megasas_get_seq_num(struct megasas_instance *instance, megasas_return_cmd(instance, cmd); return -ENOMEM; } - + memset(el_info, 0, sizeof(*el_info)); memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); dcmd->cmd = MFI_CMD_DCMD; @@ -6677,6 +7162,7 @@ megasas_get_target_prop(struct megasas_instance *instance, else ret = megasas_issue_polled(instance, cmd); + switch (ret) { case DCMD_TIMEOUT: switch (dcmd_timeout_ocr_possible(instance)) { @@ -6738,6 +7224,32 @@ static int megasas_start_aen(struct megasas_instance *instance) class_locale.word); } +static ssize_t +sysfs_max_sectors_read(struct file *filep, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + + struct Scsi_Host *host = class_to_shost(dev); + + struct megasas_instance *instance = + (struct megasas_instance *)host->hostdata; + + count = sprintf(buf,"%u\n", instance->max_sectors_per_req); + + return count+1; +} + +static struct bin_attribute sysfs_max_sectors_attr = { + .attr = { + .name = "max_sectors", + .mode = S_IRUSR|S_IRGRP|S_IROTH, + }, + .size = 7, + .read = sysfs_max_sectors_read, +}; + /** * megasas_io_attach - Attaches this driver to SCSI mid-layer * @instance: Adapter soft state @@ -6745,10 +7257,14 @@ static int megasas_start_aen(struct megasas_instance *instance) static int megasas_io_attach(struct megasas_instance *instance) { struct Scsi_Host *host = instance->host; + u32 error; /* * Export parameters required by SCSI mid-layer */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)) + host->irq = instance->pdev->irq; +#endif host->unique_id = instance->unique_id; host->can_queue = instance->max_scsi_cmds; host->this_id = instance->init_id; @@ -6785,6 +7301,46 @@ static int megasas_io_attach(struct megasas_instance *instance) host->max_lun = MEGASAS_MAX_LUN; host->max_cmd_len = 16; +#if defined(HOST_TAGSET_SUPPORT) + /* Use shared host tagset only for fusion adaptors + * if there are managed interrupts (smp affinity enabled case). + * Single msix_vectors in kdump, so shared host tag is also disabled. + */ + + host->host_tagset = 0; + host->nr_hw_queues = 1; + + if ((instance->adapter_type != MFI_SERIES) && + (instance->msix_vectors > instance->low_latency_index_start) && + host_tagset_enable && + instance->smp_affinity_enable) { + host->host_tagset = 1; + host->nr_hw_queues = instance->msix_vectors - + instance->low_latency_index_start + instance->iopoll_q_count; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,12,0)) + if (instance->iopoll_q_count) + host->nr_maps = 3; +#endif + } else { + instance->iopoll_q_count = 0; + } + + dev_info(&instance->pdev->dev, + "Max firmware commands: %d shared with default " + "HW queues: %d poll_queues: %d\n", instance->max_fw_cmds, + host->nr_hw_queues - instance->iopoll_q_count, + instance->iopoll_q_count); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,4,0)) + error = scsi_init_shared_tag_map(host, host->can_queue); + if (error) { + dev_err(&instance->pdev->dev, + "Failed to shared tag from %s %d\n", + __func__, __LINE__); + return -ENODEV; + } +#endif /* * Notify the mid-layer about the new controller */ @@ -6795,9 +7351,38 @@ static int megasas_io_attach(struct megasas_instance *instance) return -ENODEV; } + /* + * Create sysfs entries for module paramaters + */ + error = sysfs_create_bin_file(&instance->host->shost_dev.kobj, + &sysfs_max_sectors_attr); + + if (error) { + dev_err(&instance->pdev->dev, + "megasas: Error in creating the sysfs entry" + " max_sectors.\n"); + scsi_remove_host(host); + return error; + } + return 0; } +/** + * megasas_dma_set_mask_and_coherent- Set streaming and coherent DMA mask + * dev- struct representing device inside struct pci_dev + * mask- mask to be set + * return- 0 for success + * non-zero for failure + */ +static inline int megasas_dma_set_mask_and_coherent(struct device *dev, u64 mask) +{ + int rc = dma_set_mask(dev, mask); + if (rc == 0) + dma_set_coherent_mask(dev, mask); + return rc; +} + /** * megasas_set_dma_mask - Set DMA mask for supported controllers * @@ -6814,6 +7399,11 @@ static int megasas_io_attach(struct megasas_instance *instance) * * For older controllers(Thunderbolt and MFI based adapters)- * driver/FW will operate in 32 bit consistent DMA addresses. + * + * Although Invader FW supporting full 64-bit DMA, Ventura and Aero controllers + * supports 64-bit DMA addressing, due to HW design, address 0xFFFFFFFF-FFFFFFFF + * with all 64-bits set will result in PL fault - rollover error. + * Use 63-bit mask instead. */ static int megasas_set_dma_mask(struct megasas_instance *instance) @@ -6824,16 +7414,16 @@ megasas_set_dma_mask(struct megasas_instance *instance) pdev = instance->pdev; consistent_mask = (instance->adapter_type >= VENTURA_SERIES) ? - DMA_BIT_MASK(63) : DMA_BIT_MASK(32); + DMA_BIT_MASK(63) : DMA_BIT_MASK(32); if (IS_DMA64) { if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(63)) && - dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32))) + megasas_dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32))) goto fail_set_dma_mask; if ((*pdev->dev.dma_mask == DMA_BIT_MASK(63)) && - (dma_set_coherent_mask(&pdev->dev, consistent_mask) && - dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)))) { + (dma_set_coherent_mask(&pdev->dev, consistent_mask) && + megasas_dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)))) { /* * If 32 bit DMA mask fails, then try for 64 bit mask * for FW capable of handling 64 bit DMA. @@ -6843,11 +7433,11 @@ megasas_set_dma_mask(struct megasas_instance *instance) 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))) + else if (megasas_dma_set_mask_and_coherent(&pdev->dev, + DMA_BIT_MASK(63))) goto fail_set_dma_mask; } - } else if (dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32))) + } else if (megasas_dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32))) goto fail_set_dma_mask; if (pdev->dev.coherent_dma_mask == DMA_BIT_MASK(32)) @@ -6869,15 +7459,15 @@ fail_set_dma_mask: /* * megasas_set_adapter_type - Set adapter type. - * Supported controllers can be divided in - * different categories- - * enum MR_ADAPTER_TYPE { - * MFI_SERIES = 1, - * THUNDERBOLT_SERIES = 2, - * INVADER_SERIES = 3, - * VENTURA_SERIES = 4, - * AERO_SERIES = 5, - * }; + * Supported controllers can be divided in + * 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 */ @@ -6970,6 +7560,7 @@ static int megasas_alloc_ctrl_mem(struct megasas_instance *instance) break; } + return 0; fail: kfree(instance->reply_map); @@ -7045,6 +7636,7 @@ int megasas_alloc_ctrl_dma_buffers(struct megasas_instance *instance) 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, @@ -7067,7 +7659,7 @@ int megasas_alloc_ctrl_dma_buffers(struct megasas_instance *instance) dev_err(&pdev->dev, "Failed to allocate PD list buffer\n"); return -ENOMEM; } - + instance->ctrl_info_buf = dma_alloc_coherent(&pdev->dev, sizeof(struct megasas_ctrl_info), @@ -7100,6 +7692,13 @@ int megasas_alloc_ctrl_dma_buffers(struct megasas_instance *instance) return -ENOMEM; } + instance->verbuf = dma_alloc_coherent(&pdev->dev, + MEGASAS_MAX_NAME * sizeof(u32), + &instance->verbuf_h, GFP_KERNEL); + + if (!instance->verbuf) + dev_err(&instance->pdev->dev, "Failed to allocate version buffer\n"); + if (!reset_devices) { instance->system_info_buf = dma_alloc_coherent(&pdev->dev, @@ -7113,9 +7712,6 @@ int megasas_alloc_ctrl_dma_buffers(struct megasas_instance *instance) dma_alloc_coherent(&pdev->dev, sizeof(struct MR_TARGET_PROPERTIES), &instance->tgt_prop_h, GFP_KERNEL); - instance->crash_dump_buf = - 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, @@ -7129,6 +7725,15 @@ int megasas_alloc_ctrl_dma_buffers(struct megasas_instance *instance) dev_err(&instance->pdev->dev, "Failed to allocate tgt_prop buffer\n"); + + } + + if (crashdump_enable) { + instance->crash_dump_buf = + dma_alloc_coherent(&pdev->dev, + CRASH_DMA_BUF_SIZE, + &instance->crash_dump_h, GFP_KERNEL); + if (!instance->crash_dump_buf) dev_err(&instance->pdev->dev, "Failed to allocate crash dump buffer\n"); @@ -7182,6 +7787,10 @@ void megasas_free_ctrl_dma_buffers(struct megasas_instance *instance) instance->ctrl_info_buf, instance->ctrl_info_buf_h); + if (instance->verbuf) + dma_free_coherent(&pdev->dev, MEGASAS_MAX_NAME * sizeof(u32), + instance->verbuf, instance->verbuf_h); + if (instance->system_info_buf) dma_free_coherent(&pdev->dev, sizeof(struct MR_DRV_SYSTEM_INFO), instance->system_info_buf, @@ -7267,32 +7876,58 @@ static inline void megasas_init_ctrl_params(struct megasas_instance *instance) INIT_WORK(&instance->work_init, process_fw_state_change_wq); } +static inline void +megasas_get_blk_mq(struct megasas_instance *instance) +{ +#if ((defined(RHEL_MAJOR) && (RHEL_MAJOR == 8)) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0))) + instance->use_blk_mq = true; +#elif ((defined(RHEL_MAJOR) && (RHEL_MAJOR == 7) && (RHEL_MINOR >=2)) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)) && \ + (LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0))) + struct Scsi_Host *host = instance->host; + if (shost_use_blk_mq(host)) + instance->use_blk_mq = true; +#endif + return; +} + /** * megasas_probe_one - PCI hotplug entry point * @pdev: PCI device structure * @id: PCI ids of supported hotplugged adapter */ -static int megasas_probe_one(struct pci_dev *pdev, - const struct pci_device_id *id) +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,6,10)) +static int __devinit +megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) +#else +static int +megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) +#endif { int rval, pos; struct Scsi_Host *host; struct megasas_instance *instance; u16 control = 0; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)) + u32 dev_cap = 0; +#endif switch (pdev->device) { case PCI_DEVICE_ID_LSI_AERO_10E0: - case PCI_DEVICE_ID_LSI_AERO_10E3: case PCI_DEVICE_ID_LSI_AERO_10E4: + dev_err(&pdev->dev, "Adapter is in invalid mode\n"); + return 1; + case PCI_DEVICE_ID_LSI_AERO_10E3: case PCI_DEVICE_ID_LSI_AERO_10E7: - dev_err(&pdev->dev, "Adapter is in non secure mode\n"); + dev_err(&pdev->dev, "Adapter is in tampered 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) { pos = pci_find_capability(pdev, PCI_CAP_ID_MSIX); @@ -7342,6 +7977,19 @@ static int megasas_probe_one(struct pci_dev *pdev, megasas_set_adapter_type(instance); + /* + * Enable PCIe Extended tags for < 4.11 kernels + * > 4.11 kernels PCI Subsystem takes care of + * enabling PCIe tags + */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)) + pcie_capability_read_dword(pdev, PCI_EXP_DEVCAP, &dev_cap); + if (dev_cap & PCI_EXP_DEVCAP_EXT_TAG){ + dev_info(&pdev->dev, "enabling Extended Tags\n"); + pcie_capability_set_word(pdev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_EXT_TAG); + } +#endif + /* * Initialize MFI Firmware */ @@ -7389,6 +8037,8 @@ static int megasas_probe_one(struct pci_dev *pdev, */ if (megasas_io_attach(instance)) goto fail_io_attach; + + megasas_get_blk_mq(instance); instance->unload = 0; /* @@ -7415,11 +8065,16 @@ static int megasas_probe_one(struct pci_dev *pdev, return 0; fail_start_aen: + instance->unload = 1; + scsi_remove_host(instance->host); fail_io_attach: megasas_mgmt_info.count--; megasas_mgmt_info.max_index--; megasas_mgmt_info.instance[megasas_mgmt_info.max_index] = NULL; + if (instance->requestorId && !instance->skip_heartbeat_timer_del) + del_timer_sync(&instance->sriov_heartbeat_timer); + instance->instancet->disable_intr(instance); megasas_destroy_irqs(instance); @@ -7427,10 +8082,18 @@ fail_io_attach: megasas_release_fusion(instance); else megasas_release_mfi(instance); - if (instance->msix_vectors) - pci_free_irq_vectors(instance->pdev); + megasas_free_irq_vectors(instance); + 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: pci_disable_device(pdev); @@ -7542,7 +8205,7 @@ megasas_suspend(struct pci_dev *pdev, pm_message_t state) struct megasas_instance *instance; instance = pci_get_drvdata(pdev); - + if (!instance) return 0; @@ -7558,6 +8221,7 @@ megasas_suspend(struct pci_dev *pdev, pm_message_t state) if (instance->adapter_type != MFI_SERIES) megasas_fusion_stop_watchdog(instance); + megasas_flush_cache(instance); megasas_shutdown_controller(instance, MR_DCMD_HIBERNATE_SHUTDOWN); @@ -7574,9 +8238,7 @@ megasas_suspend(struct pci_dev *pdev, pm_message_t state) instance->instancet->disable_intr(instance); megasas_destroy_irqs(instance); - - if (instance->msix_vectors) - pci_free_irq_vectors(instance->pdev); + megasas_free_irq_vectors(instance); pci_save_state(pdev); pci_disable_device(pdev); @@ -7596,10 +8258,10 @@ megasas_resume(struct pci_dev *pdev) 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; @@ -7624,8 +8286,37 @@ megasas_resume(struct pci_dev *pdev) /* * 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) { + 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); + + /*waitting for about 30 second 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; @@ -7638,18 +8329,30 @@ megasas_resume(struct pci_dev *pdev) atomic_set(&instance->ldio_outstanding, 0); /* Now re-enable MSI-X */ - if (instance->msix_vectors) { - irq_flags = PCI_IRQ_MSIX; - if (instance->smp_affinity_enable) - irq_flags |= PCI_IRQ_AFFINITY; - } - rval = pci_alloc_irq_vectors(instance->pdev, 1, - instance->msix_vectors ? - instance->msix_vectors : 1, irq_flags); +#if defined(USE_MANAGED_IRQ_API) + if (instance->msix_vectors) + megasas_alloc_irq_vectors(instance); + else + rval = pci_alloc_irq_vectors(instance->pdev, 1, 1, PCI_IRQ_LEGACY); + if (rval < 0) goto fail_reenable_msix; +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) && \ + (LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0)) + if (instance->msix_vectors) + rval = pci_enable_msix_exact(instance->pdev, instance->msixentry, + instance->msix_vectors); + else + rval = pci_alloc_irq_vectors(instance->pdev, 1, 1, PCI_IRQ_LEGACY); + + if (rval < 0) + goto fail_reenable_msix; - megasas_setup_reply_map(instance); +#else + if (instance->msix_vectors && + pci_enable_msix(instance->pdev, instance->msixentry, instance->msix_vectors)) + goto fail_reenable_msix; +#endif if (instance->adapter_type != MFI_SERIES) { megasas_reset_reply_desc(instance); @@ -7678,9 +8381,10 @@ megasas_resume(struct pci_dev *pdev) megasas_setup_irqs_ioapic(instance)) goto fail_init_mfi; +#if defined(ENABLE_IRQ_POLL) if (instance->adapter_type != MFI_SERIES) megasas_setup_irq_poll(instance); - +#endif /* Re-launch SR-IOV heartbeat timer */ if (instance->requestorId) { if (!megasas_sriov_start_heartbeat(instance, 0)) @@ -7762,7 +8466,12 @@ megasas_wait_for_adapter_operational(struct megasas_instance *instance) * megasas_detach_one - PCI hot"un"plug entry point * @pdev: PCI device structure */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,6,10)) +static void __devexit megasas_detach_one(struct pci_dev *pdev) +#else static void megasas_detach_one(struct pci_dev *pdev) +#endif + { int i; struct Scsi_Host *host; @@ -7771,7 +8480,7 @@ static void megasas_detach_one(struct pci_dev *pdev) u32 pd_seq_map_sz; instance = pci_get_drvdata(pdev); - + if (!instance) return; @@ -7826,9 +8535,7 @@ skip_firing_dcmds: instance->instancet->disable_intr(instance); megasas_destroy_irqs(instance); - - if (instance->msix_vectors) - pci_free_irq_vectors(instance->pdev); + megasas_free_irq_vectors(instance); if (instance->adapter_type >= VENTURA_SERIES) { for (i = 0; i < MAX_LOGICAL_DRIVES_EXT; ++i) @@ -7917,9 +8624,7 @@ static void megasas_shutdown(struct pci_dev *pdev) skip_firing_dcmds: instance->instancet->disable_intr(instance); megasas_destroy_irqs(instance); - - if (instance->msix_vectors) - pci_free_irq_vectors(instance->pdev); + megasas_free_irq_vectors(instance); } /** @@ -7936,6 +8641,17 @@ static int megasas_mgmt_open(struct inode *inode, struct file *filep) return 0; } +/** + * megasas_mgmt_release - char node "release" entry point + */ +static int megasas_mgmt_release(struct inode *inode, struct file *filep) +{ + filep->private_data = NULL; + fasync_helper(-1, filep, 0, &megasas_async_queue); + + return 0; +} + /** * megasas_mgmt_fasync - Async notifier registration from applications * @@ -7966,15 +8682,15 @@ static int megasas_mgmt_fasync(int fd, struct file *filep, int mode) /** * megasas_mgmt_poll - char node "poll" entry point * */ -static __poll_t megasas_mgmt_poll(struct file *file, poll_table *wait) +static unsigned int megasas_mgmt_poll(struct file *file, poll_table *wait) { - __poll_t mask; + unsigned int mask; unsigned long flags; poll_wait(file, &megasas_poll_wait, wait); spin_lock_irqsave(&poll_aen_lock, flags); if (megasas_poll_wait_aen) - mask = (EPOLLIN | EPOLLRDNORM); + mask = (POLLIN | POLLRDNORM); else mask = 0; megasas_poll_wait_aen = 0; @@ -8038,8 +8754,9 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance, int error = 0, i; void *sense = NULL; dma_addr_t sense_handle; - void *sense_ptr; + unsigned long *sense_ptr; u32 opcode = 0; + int ret = DCMD_SUCCESS; memset(kbuff_arr, 0, sizeof(kbuff_arr)); @@ -8160,13 +8877,6 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance, } if (ioc->sense_len) { - /* make sure the pointer is part of the frame */ - if (ioc->sense_off > - (sizeof(union megasas_frame) - sizeof(__le64))) { - error = -EINVAL; - goto out; - } - sense = dma_alloc_coherent(&instance->pdev->dev, ioc->sense_len, &sense_handle, GFP_KERNEL); if (!sense) { @@ -8174,9 +8884,12 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance, goto out; } - /* always store 64 bits regardless of addressing */ - sense_ptr = (void *)cmd->frame + ioc->sense_off; - put_unaligned_le64(sense_handle, sense_ptr); + sense_ptr = + (unsigned long *) ((unsigned long)cmd->frame + ioc->sense_off); + if (instance->consistent_mask_64bit) + *sense_ptr = cpu_to_le64(sense_handle); + else + *sense_ptr = cpu_to_le32(sense_handle); } /* @@ -8184,13 +8897,17 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance, * cmd to the SCSI mid-layer */ cmd->sync_cmd = 1; - if (megasas_issue_blocked_cmd(instance, cmd, 0) == DCMD_NOT_FIRED) { + + ret = megasas_issue_blocked_cmd(instance, cmd, 0); + switch (ret) { + case DCMD_INIT: + case DCMD_BUSY: cmd->sync_cmd = 0; dev_err(&instance->pdev->dev, "return -EBUSY from %s %d cmd 0x%x opcode 0x%x cmd->cmd_status_drv 0x%x\n", - __func__, __LINE__, cmd->frame->hdr.cmd, opcode, - cmd->cmd_status_drv); - return -EBUSY; + __func__, __LINE__, cmd->frame->hdr.cmd, opcode, cmd->cmd_status_drv); + error = -EBUSY; + goto out; } cmd->sync_cmd = 0; @@ -8241,6 +8958,11 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance, error = -EFAULT; } + if (copy_to_user(&user_ioc->frame.hdr.scsi_status, + &cmd->frame->hdr.scsi_status, sizeof(u8))) { + dev_printk(KERN_DEBUG, &instance->pdev->dev, "Error copying out scsi_status\n"); + error = -EFAULT; + } out: if (sense) { dma_free_coherent(&instance->pdev->dev, ioc->sense_len, @@ -8439,6 +9161,13 @@ static int megasas_mgmt_compat_ioctl_fw(struct file *file, unsigned long arg) printk(KERN_DEBUG "megasas: error copy_in_user cmd_status\n"); return -EFAULT; } + + if (copy_in_user(&cioc->frame.hdr.scsi_status, + &ioc->frame.hdr.scsi_status, sizeof(u8))) { + printk(KERN_DEBUG "megasas: error copy_in_user scsi_status\n"); + return -EFAULT; + } + return error; } @@ -8463,6 +9192,7 @@ megasas_mgmt_compat_ioctl(struct file *file, unsigned int cmd, static const struct file_operations megasas_mgmt_fops = { .owner = THIS_MODULE, .open = megasas_mgmt_open, + .release = megasas_mgmt_release, .fasync = megasas_mgmt_fasync, .unlocked_ioctl = megasas_mgmt_ioctl, .poll = megasas_mgmt_poll, @@ -8480,7 +9210,11 @@ static struct pci_driver megasas_pci_driver = { .name = "megaraid_sas", .id_table = megasas_pci_table, .probe = megasas_probe_one, +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,6,10)) + .remove = __devexit_p(megasas_detach_one), +#else .remove = megasas_detach_one, +#endif .suspend = megasas_suspend, .resume = megasas_resume, .shutdown = megasas_shutdown, @@ -8494,26 +9228,22 @@ static ssize_t version_show(struct device_driver *dd, char *buf) return snprintf(buf, strlen(MEGASAS_VERSION) + 2, "%s\n", MEGASAS_VERSION); } -static DRIVER_ATTR_RO(version); static ssize_t release_date_show(struct device_driver *dd, char *buf) { return snprintf(buf, strlen(MEGASAS_RELDATE) + 2, "%s\n", MEGASAS_RELDATE); } -static DRIVER_ATTR_RO(release_date); static ssize_t support_poll_for_event_show(struct device_driver *dd, char *buf) { return sprintf(buf, "%u\n", support_poll_for_event); } -static DRIVER_ATTR_RO(support_poll_for_event); static ssize_t support_device_change_show(struct device_driver *dd, char *buf) { return sprintf(buf, "%u\n", support_device_change); } -static DRIVER_ATTR_RO(support_device_change); static ssize_t dbg_lvl_show(struct device_driver *dd, char *buf) { @@ -8531,7 +9261,6 @@ static ssize_t dbg_lvl_store(struct device_driver *dd, const char *buf, } return retval; } -static DRIVER_ATTR_RW(dbg_lvl); static ssize_t support_nvme_encapsulation_show(struct device_driver *dd, char *buf) @@ -8539,15 +9268,34 @@ support_nvme_encapsulation_show(struct device_driver *dd, char *buf) return sprintf(buf, "%u\n", support_nvme_encapsulation); } -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); } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)) +static DRIVER_ATTR_RO(version); +static DRIVER_ATTR_RO(release_date); +static DRIVER_ATTR_RO(support_poll_for_event); +static DRIVER_ATTR_RO(support_device_change); +static DRIVER_ATTR_RW(dbg_lvl); +static DRIVER_ATTR_RO(support_nvme_encapsulation); static DRIVER_ATTR_RO(support_pci_lane_margining); +#else +static DRIVER_ATTR(version, S_IRUGO, version_show, NULL); +static DRIVER_ATTR(release_date, S_IRUGO, release_date_show, NULL); +static DRIVER_ATTR(support_poll_for_event, S_IRUGO, + support_poll_for_event_show, NULL); +static DRIVER_ATTR(support_device_change, S_IRUGO, + support_device_change_show, NULL); +static DRIVER_ATTR(dbg_lvl, S_IRUGO|S_IWUSR, dbg_lvl_show, dbg_lvl_store); +static DRIVER_ATTR(support_nvme_encapsulation, S_IRUGO, + support_nvme_encapsulation_show, NULL); +static DRIVER_ATTR(support_pci_lane_margining, S_IRUGO, + support_pci_lane_margining_show, NULL); + +#endif static inline void megasas_remove_scsi_device(struct scsi_device *sdev) { @@ -8698,8 +9446,10 @@ megasas_aen_polling(struct work_struct *work) 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"); @@ -8722,12 +9472,22 @@ megasas_aen_polling(struct work_struct *work) break; case MR_EVT_LD_OFFLINE: - case MR_EVT_CFG_CLEARED: 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: @@ -8738,8 +9498,7 @@ megasas_aen_polling(struct work_struct *work) case MR_EVT_CTRL_PROP_CHANGED: dcmd_ret = megasas_get_ctrl_info(instance); - if (dcmd_ret == DCMD_SUCCESS && - instance->snapdump_wait_time) { + 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", @@ -8805,9 +9564,19 @@ static int __init megasas_init(void) if (reset_devices) { msix_vectors = 1; rdpq_enable = 0; + crashdump_enable = 0; dual_qdepth_disable = 1; + poll_queues = 0; } +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5,12,0)) + poll_queues = 0; +#endif + +#if defined(HOST_TAGSET_SUPPORT) + if (!host_tagset_enable) + poll_queues = 0; +#endif /* * Announce driver version and other information */ @@ -8835,7 +9604,7 @@ static int __init megasas_init(void) megasas_mgmt_majorno = rval; megasas_init_debugfs(); - + /* * Register ourselves as PCI hotplug module */ @@ -8847,8 +9616,9 @@ static int __init megasas_init(void) } 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_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; } @@ -8885,7 +9655,6 @@ static int __init megasas_init(void) &driver_attr_support_pci_lane_margining); if (rval) goto err_dcf_support_pci_lane_margining; - return rval; err_dcf_support_pci_lane_margining: diff --git a/drivers/scsi/megaraid/megaraid_sas_debugfs.c b/drivers/scsi/megaraid/megaraid_sas_debugfs.c index c69760775..b62c374a6 100644 --- a/drivers/scsi/megaraid/megaraid_sas_debugfs.c +++ b/drivers/scsi/megaraid/megaraid_sas_debugfs.c @@ -25,28 +25,31 @@ * * Send feedback to: megaraidlinux.pdl@broadcom.com */ +#include #include #include #include #include #include -#include +#include #include #include #include + #include "megaraid_sas_fusion.h" #include "megaraid_sas.h" #ifdef CONFIG_DEBUG_FS #include -struct dentry *megasas_debugfs_root; +struct dentry *megasas_debugfs_root = NULL; static ssize_t megasas_debugfs_read(struct file *filp, char __user *ubuf, size_t cnt, - loff_t *ppos) + loff_t *ppos) + { struct megasas_debugfs_buffer *debug = filp->private_data; @@ -72,7 +75,7 @@ megasas_debugfs_raidmap_open(struct inode *inode, struct file *file) debug->buf = (void *)fusion->ld_drv_map[(instance->map_id & 1)]; debug->len = fusion->drv_map_sz; file->private_data = debug; - + return 0; } @@ -111,7 +114,8 @@ void megasas_init_debugfs(void) */ void megasas_exit_debugfs(void) { - debugfs_remove_recursive(megasas_debugfs_root); + if (megasas_debugfs_root) + debugfs_remove_recursive(megasas_debugfs_root); } /* @@ -127,8 +131,7 @@ megasas_setup_debugfs(struct megasas_instance *instance) fusion = instance->ctrl_context; if (fusion) { - snprintf(name, sizeof(name), - "scsi_host%d", instance->host->host_no); + snprintf(name, sizeof(name), "scsi_host%d", instance->host->host_no); if (!instance->debugfs_root) { instance->debugfs_root = debugfs_create_dir(name, megasas_debugfs_root); @@ -142,8 +145,8 @@ megasas_setup_debugfs(struct megasas_instance *instance) snprintf(name, sizeof(name), "raidmap_dump"); instance->raidmap_dump = debugfs_create_file(name, S_IRUGO, - instance->debugfs_root, instance, - &megasas_debugfs_raidmap_fops); + instance->debugfs_root, + instance, &megasas_debugfs_raidmap_fops); if (!instance->raidmap_dump) { dev_err(&instance->pdev->dev, "Cannot create raidmap debugfs file\n"); @@ -160,7 +163,8 @@ megasas_setup_debugfs(struct megasas_instance *instance) */ void megasas_destroy_debugfs(struct megasas_instance *instance) { - debugfs_remove_recursive(instance->debugfs_root); + if (instance->debugfs_root) + debugfs_remove_recursive(instance->debugfs_root); } #else @@ -177,3 +181,4 @@ void megasas_destroy_debugfs(struct megasas_instance *instance) { } #endif /*CONFIG_DEBUG_FS*/ + diff --git a/drivers/scsi/megaraid/megaraid_sas_fp.c b/drivers/scsi/megaraid/megaraid_sas_fp.c index 50b8c1b12..7b344aba9 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fp.c +++ b/drivers/scsi/megaraid/megaraid_sas_fp.c @@ -1,10 +1,22 @@ -// SPDX-License-Identifier: GPL-2.0-or-later /* * Linux MegaRAID driver for SAS based RAID controllers * - * Copyright (c) 2009-2013 LSI Corporation - * Copyright (c) 2013-2016 Avago Technologies - * Copyright (c) 2016-2018 Broadcom Inc. + * Copyright (c) 2009-2018 LSI Corporation. + * Copyright (c) 2009-2018 Avago Technologies. + * Copyright (c) 2009-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 . * * FILE: megaraid_sas_fp.c * @@ -18,6 +30,7 @@ * Send feedback to: megaraidlinux.pdl@broadcom.com */ +#include #include #include #include @@ -33,7 +46,6 @@ #include #include #include -#include #include #include @@ -59,7 +71,7 @@ MODULE_PARM_DESC(lb_pending_cmds, "Change raid-1 load balancing outstanding " #define SPAN_INVALID 0xff /* Prototypes */ -static void mr_update_span_set(struct MR_DRV_RAID_MAP_ALL *map, +static u8 mr_update_span_set(struct MR_DRV_RAID_MAP_ALL *map, PLD_SPAN_INFO ldSpanInfo); static u8 mr_spanset_get_phy_params(struct megasas_instance *instance, u32 ld, u64 stripRow, u16 stripRef, struct IO_REQUEST_INFO *io_info, @@ -74,6 +86,7 @@ u32 mega_mod64(u64 dividend, u32 divisor) if (!divisor) printk(KERN_ERR "megasas : DIVISOR is zero, in div fn\n"); + d = dividend; remainder = do_div(d, divisor); return remainder; @@ -343,13 +356,17 @@ u8 MR_ValidateMapInfo(struct megasas_instance *instance, u64 map_id) } if (instance->UnevenSpanSupport) - mr_update_span_set(drv_map, ldSpanInfo); + if (mr_update_span_set(drv_map, ldSpanInfo)) + return 0; if (lbInfo) mr_update_load_balance_params(drv_map, lbInfo); num_lds = le16_to_cpu(drv_map->raidMap.ldCount); + memcpy(instance->ld_ids_prev, instance->ld_ids_from_raidmap, + sizeof(instance->ld_ids_from_raidmap)); + memset(instance->ld_ids_from_raidmap, 0xff, MEGASAS_MAX_LD_IDS); /*Convert Raid capability values to CPU arch */ for (i = 0; (num_lds > 0) && (i < MAX_LOGICAL_DRIVES_EXT); i++) { ld = MR_TargetIdToLdGet(i, drv_map); @@ -360,7 +377,7 @@ u8 MR_ValidateMapInfo(struct megasas_instance *instance, u64 map_id) raid = MR_LdRaidGet(ld, drv_map); le32_to_cpus((u32 *)&raid->capability); - + instance->ld_ids_from_raidmap[i] = i; num_lds--; } @@ -494,7 +511,7 @@ static u64 get_row_from_strip(struct megasas_instance *instance, for (info = 0; info < MAX_QUAD_DEPTH; info++) { span_set = &(ldSpanInfo[ld].span_set[info]); - if (span_set->span_row_data_width == 0) + if ((span_set->span_row_data_width == 0) || (span_set->diff == 0)) break; if (strip > span_set->data_strip_end) continue; @@ -562,6 +579,8 @@ static u64 get_strip_from_row(struct megasas_instance *instance, block_span_info.noElements) >= info+1) { quad = &map->raidMap.ldSpanMap[ld]. spanBlock[span].block_span_info.quad[info]; + if (le32_to_cpu(quad->diff) == 0) + return -1; if (le64_to_cpu(quad->logStart) <= row && row <= le64_to_cpu(quad->logEnd) && mega_mod64((row - le64_to_cpu(quad->logStart)), @@ -708,6 +727,8 @@ static u8 mr_spanset_get_phy_params(struct megasas_instance *instance, u32 ld, row = io_info->start_row; span = io_info->start_span; + if (SPAN_ROW_SIZE(map, ld, span) == 0) + return false; if (raid->level == 6) { logArm = get_arm_from_strip(instance, ld, stripRow, map); @@ -901,7 +922,7 @@ u8 MR_GetPhyParams(struct megasas_instance *instance, u32 ld, u64 stripRow, * This routine calculates the logical arm, data Arm, row number and parity arm * for R56 CTIO write operation. */ -static void mr_get_phy_params_r56_rmw(struct megasas_instance *instance, +static u8 mr_get_phy_params_r56_rmw(struct megasas_instance *instance, u32 ld, u64 stripNo, struct IO_REQUEST_INFO *io_info, struct RAID_CONTEXT_G35 *pRAID_Context, @@ -918,6 +939,8 @@ static void mr_get_phy_params_r56_rmw(struct megasas_instance *instance, rowNum = mega_div64_32(stripNo, dataArms); /* parity disk arm, first arm is 0 */ + if (arms == 0) + return 1; rightmostParityArm = (arms - 1) - mega_mod64(rowNum, arms); /* logical arm within row */ @@ -930,7 +953,7 @@ static void mr_get_phy_params_r56_rmw(struct megasas_instance *instance, } else { span = (u8)MR_GetSpanBlock(ld, rowNum, pdBlock, map); if (span == SPAN_INVALID) - return; + return 1; } if (raid->level == 6) { @@ -939,7 +962,7 @@ static void mr_get_phy_params_r56_rmw(struct megasas_instance *instance, if (PParityArm < 0) PParityArm += arms; - + /* rightmostParityArm is P-Parity for RAID 5 and Q-Parity for RAID */ pRAID_Context->flow_specific.r56_arm_map = rightmostParityArm; pRAID_Context->flow_specific.r56_arm_map |= @@ -957,9 +980,10 @@ static void mr_get_phy_params_r56_rmw(struct megasas_instance *instance, pRAID_Context->raid_flags = (MR_RAID_FLAGS_IO_SUB_TYPE_R56_DIV_OFFLOAD << MR_RAID_CTX_RAID_FLAGS_IO_SUB_TYPE_SHIFT); - return; + return 0; } + /* ****************************************************************************** * @@ -1023,10 +1047,11 @@ MR_BuildRaidContext(struct megasas_instance *instance, } } + io_info->data_arms = raid->rowDataSize; + stripSize = 1 << raid->stripeShift; stripe_mask = stripSize-1; - io_info->data_arms = raid->rowDataSize; /* * calculate starting row and stripe, and number of strips and rows @@ -1170,9 +1195,10 @@ MR_BuildRaidContext(struct megasas_instance *instance, /* Aero R5/6 Division Offload for WRITE */ if (fusion->r56_div_offload && (raid->level >= 5) && !isRead) { - mr_get_phy_params_r56_rmw(instance, ld, start_strip, io_info, - (struct RAID_CONTEXT_G35 *)pRAID_Context, - map); + if (mr_get_phy_params_r56_rmw(instance, ld, start_strip, io_info, + (struct RAID_CONTEXT_G35 *)pRAID_Context, + map)) + return false; return true; } @@ -1219,7 +1245,7 @@ MR_BuildRaidContext(struct megasas_instance *instance, * ldSpanInfo - ldSpanInfo per HBA instance * */ -void mr_update_span_set(struct MR_DRV_RAID_MAP_ALL *map, +static u8 mr_update_span_set(struct MR_DRV_RAID_MAP_ALL *map, PLD_SPAN_INFO ldSpanInfo) { u8 span, count; @@ -1265,6 +1291,8 @@ void mr_update_span_set(struct MR_DRV_RAID_MAP_ALL *map, } span_set->span_row_data_width = span_row_width; + if (le32_to_cpu(quad->diff) == 0) + return 1; span_row = mega_div64_32(((le64_to_cpu(quad->logEnd) - le64_to_cpu(quad->logStart)) + le32_to_cpu(quad->diff)), le32_to_cpu(quad->diff)); @@ -1318,6 +1346,7 @@ void mr_update_span_set(struct MR_DRV_RAID_MAP_ALL *map, break; } } + return 0; } void mr_update_load_balance_params(struct MR_DRV_RAID_MAP_ALL *drv_map, diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c index 17b4496d8..31e2f64c9 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.c +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c @@ -1,10 +1,22 @@ -// SPDX-License-Identifier: GPL-2.0-or-later /* * Linux MegaRAID driver for SAS based RAID controllers * - * Copyright (c) 2009-2013 LSI Corporation - * Copyright (c) 2013-2016 Avago Technologies - * Copyright (c) 2016-2018 Broadcom Inc. + * Copyright (c) 2009-2018 LSI Corporation. + * Copyright (c) 2009-2018 Avago Technologies. + * Copyright (c) 2009-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 . * * FILE: megaraid_sas_fusion.c * @@ -17,6 +29,7 @@ * Send feedback to: megaraidlinux.pdl@broadcom.com */ +#include #include #include #include @@ -35,7 +48,6 @@ #include #include #include -#include #include #include @@ -73,6 +85,7 @@ int megasas_transition_to_ready(struct megasas_instance *instance, int ocr); void megaraid_sas_kill_hba(struct megasas_instance *instance); extern u32 megasas_dbg_lvl; +extern int disable_ext_io; int megasas_sriov_start_heartbeat(struct megasas_instance *instance, int initial); void megasas_start_timer(struct megasas_instance *instance); @@ -87,6 +100,7 @@ static void megasas_fusion_crash_dump(struct megasas_instance *instance); extern u32 megasas_readl(struct megasas_instance *instance, const volatile void __iomem *addr); + /** * megasas_adp_reset_wait_for_ready - initiate chip reset and wait for * controller to come to ready state @@ -95,7 +109,7 @@ extern u32 megasas_readl(struct megasas_instance *instance, * @ocr_context - If called from OCR context this will * be set to 1, else 0 * - * This function initates a chip reset followed by a wait for controller to + * This functon initiates a chip reset followed by a wait for controller to * transition to ready state. * During this, driver will block all access to PCI config space from userspace */ @@ -106,6 +120,7 @@ megasas_adp_reset_wait_for_ready(struct megasas_instance *instance, { int ret = FAILED; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0)) /* * Block access to PCI config space from userspace * when diag reset is initiated from driver @@ -116,6 +131,7 @@ megasas_adp_reset_wait_for_ready(struct megasas_instance *instance, __func__, __LINE__); pci_cfg_access_lock(instance->pdev); +#endif if (do_adp_reset) { if (instance->instancet->adp_reset @@ -133,13 +149,14 @@ megasas_adp_reset_wait_for_ready(struct megasas_instance *instance, ret = SUCCESS; out: +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0)) if (megasas_dbg_lvl & OCR_DEBUG) dev_info(&instance->pdev->dev, "Unlock access to PCI config space %s %d\n", __func__, __LINE__); pci_cfg_access_unlock(instance->pdev); - +#endif return ret; } @@ -191,7 +208,8 @@ megasas_enable_intr_fusion(struct megasas_instance *instance) /* Dummy readl to force pci flush */ dev_info(&instance->pdev->dev, "%s is called outbound_intr_mask:0x%08x\n", - __func__, readl(®s->outbound_intr_mask)); + __func__, readl(®s->outbound_intr_mask)); + } /** @@ -209,7 +227,7 @@ megasas_disable_intr_fusion(struct megasas_instance *instance) writel(mask, ®s->outbound_intr_mask); /* Dummy readl to force pci flush */ dev_info(&instance->pdev->dev, "%s is called outbound_intr_mask:0x%08x\n", - __func__, readl(®s->outbound_intr_mask)); + __func__, readl(®s->outbound_intr_mask)); } int @@ -235,6 +253,69 @@ megasas_clear_intr_fusion(struct megasas_instance *instance) return 1; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)) + +static inline void +megasas_sdev_busy_inc(struct megasas_instance *instance, + struct scsi_cmnd *scmd) +{ + if (instance->perf_mode == MR_BALANCED_PERF_MODE) { + struct MR_PRIV_DEVICE *mr_device_priv_data = + scmd->device->hostdata; + atomic_inc(&mr_device_priv_data->sdev_priv_busy); + } +} + +static inline void +megasas_sdev_busy_dec(struct megasas_instance *instance, + struct scsi_cmnd *scmd) +{ + if (instance->perf_mode == MR_BALANCED_PERF_MODE) { + struct MR_PRIV_DEVICE *mr_device_priv_data = + scmd->device->hostdata; + atomic_dec(&mr_device_priv_data->sdev_priv_busy); + } +} + +static inline int +megasas_sdev_busy_read(struct megasas_instance *instance, + struct scsi_cmnd *scmd) +{ + if (instance->perf_mode == MR_BALANCED_PERF_MODE) { + struct MR_PRIV_DEVICE *mr_device_priv_data = + scmd->device->hostdata; + return atomic_read(&mr_device_priv_data->sdev_priv_busy); + } + return 0; +} + +#else + +static inline void +megasas_sdev_busy_inc(struct megasas_instance *instance, + struct scsi_cmnd *scmd) +{ + return; +} + +static inline void +megasas_sdev_busy_dec(struct megasas_instance *instance, + struct scsi_cmnd *scmd) +{ + return; +} + +static inline int +megasas_sdev_busy_read(struct megasas_instance *instance, + struct scsi_cmnd *scmd) +{ + if (instance->perf_mode == MR_BALANCED_PERF_MODE) + return atomic_read(&scmd->device->device_busy); + + return 0; +} +#endif + /** * megasas_get_cmd_fusion - Get a command from the free pool * @instance: Adapter soft state @@ -265,33 +346,37 @@ inline void megasas_return_cmd_fusion(struct megasas_instance *instance, } /** - * megasas_write_64bit_req_desc - PCI writes 64bit request descriptor - * @instance: Adapter soft state - * @req_desc: 64bit Request descriptor + * megasas_write_64bit_req_desc - PCI writes 64bit request descriptor + * @instance: Adapter soft state + * @req_desc: 64bit Request descriptor */ static void megasas_write_64bit_req_desc(struct megasas_instance *instance, union MEGASAS_REQUEST_DESCRIPTOR_UNION *req_desc) { #if defined(writeq) && defined(CONFIG_64BIT) - u64 req_data = (((u64)le32_to_cpu(req_desc->u.high) << 32) | - le32_to_cpu(req_desc->u.low)); - writeq(req_data, &instance->reg_set->inbound_low_queue_port); + u64 req_data = (((u64)le32_to_cpu(req_desc->u.high) << 32) | + le32_to_cpu(req_desc->u.low)); + writeq(req_data, &instance->reg_set->inbound_low_queue_port); #else - unsigned long flags; - spin_lock_irqsave(&instance->hba_lock, flags); - writel(le32_to_cpu(req_desc->u.low), - &instance->reg_set->inbound_low_queue_port); - writel(le32_to_cpu(req_desc->u.high), - &instance->reg_set->inbound_high_queue_port); - spin_unlock_irqrestore(&instance->hba_lock, flags); + unsigned long flags; + spin_lock_irqsave(&instance->hba_lock, flags); + writel(le32_to_cpu(req_desc->u.low), + &instance->reg_set->inbound_low_queue_port); + writel(le32_to_cpu(req_desc->u.high), + &instance->reg_set->inbound_high_queue_port); +#if !((defined(RHEL_MAJOR) && (RHEL_MAJOR == 8) && (RHEL_MINOR >= 2)) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(5,2,0))) + mmiowb(); +#endif + spin_unlock_irqrestore(&instance->hba_lock, flags); #endif } /** * megasas_fire_cmd_fusion - Sends command to the FW - * @instance: Adapter soft state - * @req_desc: 32bit or 64bit Request descriptor + * @instance: Adapter soft state + * @req_desc: 32bit or 64bit Request descriptor * * Perform PCI Write. AERO SERIES supports 32 bit Descriptor. * Prior to AERO_SERIES support 64 bit Descriptor. @@ -323,6 +408,9 @@ megasas_fusion_update_can_queue(struct megasas_instance *instance, int fw_boot_c { u16 cur_max_fw_cmds = 0; u16 ldio_threshold = 0; + struct megasas_register_set __iomem *reg_set; + + reg_set = instance->reg_set; /* ventura FW does not fill outbound_scratch_pad_2 with queue depth */ if (instance->adapter_type < VENTURA_SERIES) @@ -573,7 +661,6 @@ megasas_alloc_cmdlist_fusion(struct megasas_instance *instance) return 0; } - static int megasas_alloc_request_fusion(struct megasas_instance *instance) { @@ -592,10 +679,17 @@ retry_alloc: return -ENOMEM; } +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0)) fusion->io_request_frames = - dma_pool_alloc(fusion->io_request_frames_pool, + dma_pool_alloc(fusion->io_request_frames_pool, + ((GFP_KERNEL & (~__GFP_WAIT)) | __GFP_NOWARN), + &fusion->io_request_frames_phys); +#else + fusion->io_request_frames = + dma_pool_alloc(fusion->io_request_frames_pool, GFP_KERNEL | __GFP_NOWARN, &fusion->io_request_frames_phys); +#endif if (!fusion->io_request_frames) { if (instance->max_fw_cmds >= (MEGASAS_REDUCE_QD_COUNT * 2)) { instance->max_fw_cmds -= MEGASAS_REDUCE_QD_COUNT; @@ -630,11 +724,17 @@ retry_alloc: "Failed from %s %d\n", __func__, __LINE__); return -ENOMEM; } - +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0)) + fusion->io_request_frames = + dma_pool_alloc(fusion->io_request_frames_pool, + ((GFP_KERNEL & (~__GFP_WAIT)) | __GFP_NOWARN), + &fusion->io_request_frames_phys); +#else fusion->io_request_frames = dma_pool_alloc(fusion->io_request_frames_pool, GFP_KERNEL | __GFP_NOWARN, &fusion->io_request_frames_phys); +#endif if (!fusion->io_request_frames) { dev_err(&instance->pdev->dev, @@ -665,6 +765,8 @@ megasas_alloc_reply_fusion(struct megasas_instance *instance) fusion = instance->ctrl_context; count = instance->msix_vectors > 0 ? instance->msix_vectors : 1; + count += instance->iopoll_q_count; + fusion->reply_frames_desc_pool = dma_pool_create("mr_reply", &instance->pdev->dev, fusion->reply_alloc_sz * count, 16, 0); @@ -757,8 +859,9 @@ megasas_alloc_rdpq_fusion(struct megasas_instance *instance) "Failed from %s %d\n", __func__, __LINE__); return -ENOMEM; } - + memset(fusion->rdpq_virt, 0, array_size); msix_count = instance->msix_vectors > 0 ? instance->msix_vectors : 1; + msix_count += instance->iopoll_q_count; fusion->reply_frames_desc_pool = dma_pool_create("mr_rdpq", &instance->pdev->dev, @@ -873,8 +976,10 @@ megasas_free_rdpq_fusion(struct megasas_instance *instance) { } - dma_pool_destroy(fusion->reply_frames_desc_pool); - dma_pool_destroy(fusion->reply_frames_desc_pool_align); + if (fusion->reply_frames_desc_pool) + dma_pool_destroy(fusion->reply_frames_desc_pool); + if (fusion->reply_frames_desc_pool_align) + dma_pool_destroy(fusion->reply_frames_desc_pool_align); if (fusion->rdpq_virt) dma_free_coherent(&instance->pdev->dev, @@ -894,7 +999,8 @@ megasas_free_reply_fusion(struct megasas_instance *instance) { fusion->reply_frames_desc[0], fusion->reply_frames_desc_phys[0]); - dma_pool_destroy(fusion->reply_frames_desc_pool); + if (fusion->reply_frames_desc_pool) + dma_pool_destroy(fusion->reply_frames_desc_pool); } @@ -941,9 +1047,6 @@ megasas_alloc_cmds_fusion(struct megasas_instance *instance) if (megasas_alloc_cmdlist_fusion(instance)) goto fail_exit; - dev_info(&instance->pdev->dev, "Configured max firmware commands: %d\n", - instance->max_fw_cmds); - /* The first 256 bytes (SMID 0) is not used. Don't add to the cmd list */ io_req_base = fusion->io_request_frames + MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE; io_req_base_phys = fusion->io_request_frames_phys + MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE; @@ -1086,8 +1189,9 @@ megasas_ioc_init_fusion(struct megasas_instance *instance) MR_HIGH_IOPS_QUEUE_COUNT) && cur_intr_coalescing) instance->perf_mode = MR_BALANCED_PERF_MODE; - dev_info(&instance->pdev->dev, "Performance mode :%s\n", - MEGASAS_PERF_MODE_2STR(instance->perf_mode)); + dev_info(&instance->pdev->dev, "Performance mode :%s (latency index = %d)\n", + MEGASAS_PERF_MODE_2STR(instance->perf_mode), + instance->low_latency_index_start); instance->fw_sync_cache_support = (scratch_pad_1 & MR_CAN_HANDLE_SYNC_CACHE_OFFSET) ? 1 : 0; @@ -1110,7 +1214,7 @@ megasas_ioc_init_fusion(struct megasas_instance *instance) MPI2_IOCINIT_MSGFLAG_RDPQ_ARRAY_MODE : 0; IOCInitMessage->SystemRequestFrameBaseAddress = cpu_to_le64(fusion->io_request_frames_phys); IOCInitMessage->SenseBufferAddressHigh = cpu_to_le32(upper_32_bits(fusion->sense_phys_addr)); - IOCInitMessage->HostMSIxVectors = instance->msix_vectors; + IOCInitMessage->HostMSIxVectors = instance->msix_vectors + instance->iopoll_q_count; IOCInitMessage->HostPageSize = MR_DEFAULT_NVME_PAGE_SHIFT; time = ktime_get_real(); @@ -1139,7 +1243,8 @@ megasas_ioc_init_fusion(struct megasas_instance *instance) drv_ops->mfi_capabilities.support_ndrive_r1_lb = 1; drv_ops->mfi_capabilities.security_protocol_cmds_fw = 1; - if (instance->max_chain_frame_sz > MEGASAS_CHAIN_FRAME_SZ_MIN) + if ((!disable_ext_io) && + (instance->max_chain_frame_sz > MEGASAS_CHAIN_FRAME_SZ_MIN)) drv_ops->mfi_capabilities.support_ext_io_size = 1; drv_ops->mfi_capabilities.support_fp_rlbypass = 1; @@ -1172,10 +1277,10 @@ megasas_ioc_init_fusion(struct megasas_instance *instance) init_frame->queue_info_new_phys_addr_lo = cpu_to_le32(lower_32_bits(ioc_init_handle)); init_frame->data_xfer_len = cpu_to_le32(sizeof(struct MPI2_IOC_INIT_REQUEST)); - + /* - * Each bit in replyqueue_mask represents one group of MSI-x vectors - * (each group has 8 vectors) + * Each bit in replyqueue_mask represents one group of MSI-x vectors + * (each group has 8 vectors) */ switch (instance->perf_mode) { case MR_BALANCED_PERF_MODE: @@ -1187,7 +1292,16 @@ megasas_ioc_init_fusion(struct megasas_instance *instance) cpu_to_le16(~(~0 << instance->msix_vectors/8)); break; } + + if (instance->verbuf) { + snprintf((char *)instance->verbuf, strlen(MEGASAS_VERSION) + 2, + "%s", MEGASAS_VERSION); + if (reset_devices) + strcat((char *)instance->verbuf, "-kdump"); + init_frame->driver_ver_lo = cpu_to_le32(lower_32_bits(instance->verbuf_h)); + init_frame->driver_ver_hi = cpu_to_le32(upper_32_bits(instance->verbuf_h)); + } req_desc.u.low = cpu_to_le32(lower_32_bits(cmd->frame_phys_addr)); req_desc.u.high = cpu_to_le32(upper_32_bits(cmd->frame_phys_addr)); @@ -1218,15 +1332,16 @@ megasas_ioc_init_fusion(struct megasas_instance *instance) goto fail_fw_init; } + if (instance->adapter_type >= AERO_SERIES) { - scratch_pad_1 = megasas_readl - (instance, &instance->reg_set->outbound_scratch_pad_1); + scratch_pad_1 = megasas_readl + (instance, &instance->reg_set->outbound_scratch_pad_1); - instance->atomic_desc_support = - (scratch_pad_1 & MR_ATOMIC_DESCRIPTOR_SUPPORT_OFFSET) ? 1 : 0; + instance->atomic_desc_support = + (scratch_pad_1 & MR_ATOMIC_DESCRIPTOR_SUPPORT_OFFSET) ? 1 : 0; - dev_info(&instance->pdev->dev, "FW supports atomic descriptor\t: %s\n", - instance->atomic_desc_support ? "Yes" : "No"); + dev_info(&instance->pdev->dev, "FW supports atomic descriptor\t: %s\n", + instance->atomic_desc_support ? "Yes" : "No"); } return 0; @@ -1260,7 +1375,9 @@ megasas_sync_pd_seq_num(struct megasas_instance *instance, bool pend) { pd_sync = (void *)fusion->pd_seq_sync[(instance->pd_seq_map_id & 1)]; pd_seq_h = fusion->pd_seq_phys[(instance->pd_seq_map_id & 1)]; - pd_seq_map_sz = struct_size(pd_sync, seq, MAX_PHYSICAL_DEVICES - 1); + pd_seq_map_sz = sizeof(struct MR_PD_CFG_SEQ_NUM_SYNC) + + (sizeof(struct MR_PD_CFG_SEQ) * + (MAX_PHYSICAL_DEVICES - 1)); cmd = megasas_get_cmd(instance); if (!cmd) { @@ -1313,7 +1430,9 @@ megasas_sync_pd_seq_num(struct megasas_instance *instance, bool pend) { } if (ret == DCMD_TIMEOUT) - megaraid_sas_kill_hba(instance); + dev_warn(&instance->pdev->dev, + "%s DCMD timed out, continue without JBOD sequence map\n", + __func__); if (ret == DCMD_SUCCESS) instance->pd_seq_map_id++; @@ -1395,7 +1514,9 @@ megasas_get_ld_map_info(struct megasas_instance *instance) ret = megasas_issue_polled(instance, cmd); if (ret == DCMD_TIMEOUT) - megaraid_sas_kill_hba(instance); + dev_warn(&instance->pdev->dev, + "%s DCMD timed out, RAID map is disabled\n", + __func__); megasas_return_cmd(instance, cmd); @@ -1512,6 +1633,8 @@ megasas_display_intel_branding(struct megasas_instance *instance) switch (instance->pdev->device) { case PCI_DEVICE_ID_LSI_INVADER: + case PCI_DEVICE_ID_LSI_INTRUDER: + case PCI_DEVICE_ID_LSI_INTRUDER_24: switch (instance->pdev->subsystem_device) { case MEGARAID_INTEL_RS3DC080_SSDID: dev_info(&instance->pdev->dev, "scsi host %d: %s\n", @@ -1726,7 +1849,8 @@ static inline void megasas_free_ioc_init_cmd(struct megasas_instance *instance) fusion->ioc_init_cmd->frame, fusion->ioc_init_cmd->frame_phys_addr); - kfree(fusion->ioc_init_cmd); + if (fusion->ioc_init_cmd) + kfree(fusion->ioc_init_cmd); } /** @@ -1779,6 +1903,13 @@ megasas_init_adapter_fusion(struct megasas_instance *instance) instance->max_chain_frame_sz = MEGASAS_CHAIN_FRAME_SZ_MIN; } + /* 1MB IO support enable/disable only for Invader series */ + if (instance->adapter_type >= VENTURA_SERIES) + disable_ext_io = 0; + + if (disable_ext_io) + instance->max_chain_frame_sz = MEGASAS_CHAIN_FRAME_SZ_MIN; + fusion->max_sge_in_main_msg = (MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE - offsetof(struct MPI2_RAID_SCSI_IO_REQUEST, SGL))/16; @@ -1800,6 +1931,8 @@ megasas_init_adapter_fusion(struct megasas_instance *instance) sizeof(union MPI2_SGE_IO_UNION))/16; count = instance->msix_vectors > 0 ? instance->msix_vectors : 1; + count += instance->iopoll_q_count; + for (i = 0 ; i < count; i++) fusion->last_reply_idx[i] = 0; @@ -1812,6 +1945,9 @@ megasas_init_adapter_fusion(struct megasas_instance *instance) MEGASAS_FUSION_IOCTL_CMDS); sema_init(&instance->ioctl_sem, MEGASAS_FUSION_IOCTL_CMDS); + for (i = 0; i < MAX_MSIX_QUEUES_FUSION; i++) + atomic_set(&fusion->busy_mq_poll[i], 0); + if (megasas_alloc_ioc_init_frame(instance)) return 1; @@ -1872,7 +2008,7 @@ fail_alloc_mfi_cmds: /** * megasas_fault_detect_work - Worker function of - * FW fault handling workqueue. + * FW fault handling workqueue. */ static void megasas_fault_detect_work(struct work_struct *work) @@ -1887,13 +2023,31 @@ megasas_fault_detect_work(struct work_struct *work) MFI_STATE_MASK; if (fw_state == MFI_STATE_FAULT) { - dma_state = instance->instancet->read_fw_status_reg(instance) & - MFI_STATE_DMADONE; - /* Start collecting crash, if DMA bit is done */ if (instance->crash_dump_drv_support && - instance->crash_dump_app_support && dma_state) { - megasas_fusion_crash_dump(instance); + instance->crash_dump_app_support) { + /* Start collecting crash, if DMA bit is done */ + dma_state = instance->instancet->read_fw_status_reg(instance) & + MFI_STATE_DMADONE; + if (dma_state) + megasas_fusion_crash_dump(instance); + else { + dev_warn(&instance->pdev->dev, + "Iop2SysDoorbellInt for scsi%d\n", + instance->host->host_no); + if (instance->unload == 0) { + status = megasas_reset_fusion(instance->host, 0); + if (status != SUCCESS) { + dev_err(&instance->pdev->dev, + "Failed from %s %d, do not re-arm timer\n", + __func__, __LINE__); + return; + } + } + } } else { + dev_warn(&instance->pdev->dev, + "Iop2SysDoorbellInt for scsi%d\n", + instance->host->host_no); if (instance->unload == 0) { status = megasas_reset_fusion(instance->host, 0); if (status != SUCCESS) { @@ -2037,7 +2191,6 @@ static bool megasas_is_prp_possible(struct megasas_instance *instance, struct scsi_cmnd *scmd, int sge_count) { - int i; u32 data_length = 0; struct scatterlist *sg_scmd; bool build_prp = false; @@ -2066,63 +2219,6 @@ megasas_is_prp_possible(struct megasas_instance *instance, build_prp = true; } -/* - * Below code detects gaps/holes in IO data buffers. - * What does holes/gaps mean? - * Any SGE except first one in a SGL starts at non NVME page size - * aligned address OR Any SGE except last one in a SGL ends at - * non NVME page size boundary. - * - * Driver has already informed block layer by setting boundary rules for - * bio merging done at NVME page size boundary calling kernel API - * blk_queue_virt_boundary inside slave_config. - * Still there is possibility of IO coming with holes to driver because of - * IO merging done by IO scheduler. - * - * With SCSI BLK MQ enabled, there will be no IO with holes as there is no - * IO scheduling so no IO merging. - * - * With SCSI BLK MQ disabled, IO scheduler may attempt to merge IOs and - * then sending IOs with holes. - * - * Though driver can request block layer to disable IO merging by calling- - * blk_queue_flag_set(QUEUE_FLAG_NOMERGES, sdev->request_queue) but - * user may tune sysfs parameter- nomerges again to 0 or 1. - * - * If in future IO scheduling is enabled with SCSI BLK MQ, - * this algorithm to detect holes will be required in driver - * for SCSI BLK MQ enabled case as well. - * - * - */ - scsi_for_each_sg(scmd, sg_scmd, sge_count, i) { - if ((i != 0) && (i != (sge_count - 1))) { - if (mega_mod64(sg_dma_len(sg_scmd), mr_nvme_pg_size) || - mega_mod64(sg_dma_address(sg_scmd), - mr_nvme_pg_size)) { - build_prp = false; - break; - } - } - - if ((sge_count > 1) && (i == 0)) { - if ((mega_mod64((sg_dma_address(sg_scmd) + - sg_dma_len(sg_scmd)), - mr_nvme_pg_size))) { - build_prp = false; - break; - } - } - - if ((sge_count > 1) && (i == (sge_count - 1))) { - if (mega_mod64(sg_dma_address(sg_scmd), - mr_nvme_pg_size)) { - build_prp = false; - break; - } - } - } - return build_prp; } @@ -2704,6 +2800,55 @@ megasas_set_raidflag_cpu_affinity(struct fusion_context *fusion, } } +/* + * megasas_atomic_dec_if_positive - decrement by 1 if old value positive + * @v: pointer of type atomic_t + * + * The function returns the old value of *v minus 1, even if + * the atomic variable, v, was not decremented. + */ +static inline int megasas_atomic_dec_if_positive(atomic_t *v) +{ + int c, old, dec; + c = atomic_read(v); + for (;;) { + dec = c - 1; + if (unlikely(dec < 0)) + break; + old = atomic_cmpxchg((v), c, dec); + if (likely(old == c)) + break; + c = old; + } + return dec; +} + +static inline void +megasas_get_msix_index(struct megasas_instance *instance, + struct scsi_cmnd *scmd, + struct megasas_cmd_fusion *cmd, + u8 data_arms) +{ + if ((instance->perf_mode == MR_BALANCED_PERF_MODE) && + (megasas_sdev_busy_read(instance, scmd) > + (data_arms * MR_DEVICE_HIGH_IOPS_DEPTH))) { + cmd->request_desc->SCSIIO.MSIxIndex = + mega_mod64((atomic64_add_return(1, &instance->high_iops_outstanding) / + MR_HIGH_IOPS_BATCH_COUNT), instance->low_latency_index_start); + } else if (instance->msix_load_balance) { + cmd->request_desc->SCSIIO.MSIxIndex = + (mega_mod64(atomic64_add_return(1, &instance->total_io_count), + instance->msix_vectors)); + } else if (instance->host->nr_hw_queues > 1) { + u32 tag = blk_mq_unique_tag(scmd->request); + cmd->request_desc->SCSIIO.MSIxIndex = blk_mq_unique_tag_to_hwq(tag) + + instance->low_latency_index_start; + } else { + cmd->request_desc->SCSIIO.MSIxIndex = + instance->reply_map[raw_smp_processor_id()]; + } +} + /** * megasas_build_ldio_fusion - Prepares IOs to devices * @instance: Adapter soft state @@ -2804,9 +2949,9 @@ megasas_build_ldio_fusion(struct megasas_instance *instance, io_info.numBlocks = datalength; io_info.ldTgtId = device_id; io_info.r1_alt_dev_handle = MR_DEVHANDLE_INVALID; + io_info.data_arms = 1; scsi_buff_len = scsi_bufflen(scp); io_request->DataLength = cpu_to_le32(scsi_buff_len); - io_info.data_arms = 1; if (scp->sc_data_direction == DMA_FROM_DEVICE) io_info.isRead = 1; @@ -2826,20 +2971,8 @@ megasas_build_ldio_fusion(struct megasas_instance *instance, fp_possible = (io_info.fpOkForIo > 0) ? true : false; } - if ((instance->perf_mode == MR_BALANCED_PERF_MODE) && - atomic_read(&scp->device->device_busy) > - (io_info.data_arms * MR_DEVICE_HIGH_IOPS_DEPTH)) - cmd->request_desc->SCSIIO.MSIxIndex = - mega_mod64((atomic64_add_return(1, &instance->high_iops_outstanding) / - MR_HIGH_IOPS_BATCH_COUNT), instance->low_latency_index_start); - else if (instance->msix_load_balance) - cmd->request_desc->SCSIIO.MSIxIndex = - (mega_mod64(atomic64_add_return(1, &instance->total_io_count), - instance->msix_vectors)); - else - cmd->request_desc->SCSIIO.MSIxIndex = - instance->reply_map[raw_smp_processor_id()]; - + megasas_get_msix_index(instance, scp, cmd, io_info.data_arms); + if (instance->adapter_type >= VENTURA_SERIES) { /* FP for Optimal raid level 1. * All large RAID-1 writes (> 32 KiB, both WT and WB modes) @@ -2856,9 +2989,9 @@ megasas_build_ldio_fusion(struct megasas_instance *instance, (instance->host->can_queue)) { fp_possible = false; atomic_dec(&instance->fw_outstanding); - } else if (fusion->pcie_bw_limitation && + } else if ((fusion->pcie_bw_limitation) && ((scsi_buff_len > MR_LARGE_IO_MIN_SIZE) || - (atomic_dec_if_positive(&mrdev_priv->r1_ldio_hint) > 0))) { + megasas_atomic_dec_if_positive(&mrdev_priv->r1_ldio_hint) > 0)) { fp_possible = false; atomic_dec(&instance->fw_outstanding); if (scsi_buff_len > MR_LARGE_IO_MIN_SIZE) @@ -2903,8 +3036,7 @@ megasas_build_ldio_fusion(struct megasas_instance *instance, rctx->nseg = 0x1; io_request->IoFlags |= cpu_to_le16(MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH); rctx->reg_lock_flags |= - (MR_RL_FLAGS_GRANT_DESTINATION_CUDA | - MR_RL_FLAGS_SEQ_NUM_ENABLE); + (MR_RL_FLAGS_GRANT_DESTINATION_CUDA | MR_RL_FLAGS_SEQ_NUM_ENABLE); } else if (instance->adapter_type >= VENTURA_SERIES) { rctx_g35->nseg_type |= (1 << RAID_CONTEXT_NSEG_SHIFT); rctx_g35->nseg_type |= (MPI2_TYPE_CUDA << RAID_CONTEXT_TYPE_SHIFT); @@ -3106,22 +3238,19 @@ megasas_build_syspd_fusion(struct megasas_instance *instance, << MR_RAID_CTX_RAID_FLAGS_IO_SUB_TYPE_SHIFT; /* If FW supports PD sequence number */ - if (!reset_devices && instance->support_seqnum_jbod_fp) { + if (instance->support_seqnum_jbod_fp) { if (instance->use_seqnum_jbod_fp && instance->pd_list[pd_index].driveType == TYPE_DISK) { - + /* More than 256 PD/JBOD support for Ventura */ if (instance->support_morethan256jbod) pRAID_Context->virtual_disk_tgt_id = pd_sync->seq[pd_index].pd_target_id; else pRAID_Context->virtual_disk_tgt_id = - cpu_to_le16(device_id + - (MAX_PHYSICAL_DEVICES - 1)); - pRAID_Context->config_seq_num = - pd_sync->seq[pd_index].seqNum; - io_request->DevHandle = - pd_sync->seq[pd_index].devHandle; + cpu_to_le16(device_id + (MAX_PHYSICAL_DEVICES - 1)); + pRAID_Context->config_seq_num = pd_sync->seq[pd_index].seqNum; + io_request->DevHandle = pd_sync->seq[pd_index].devHandle; if (instance->adapter_type >= VENTURA_SERIES) { io_request->RaidContext.raid_context_g35.routing_flags |= (1 << MR_RAID_CTX_ROUTINGFLAGS_SQN_SHIFT); @@ -3133,23 +3262,27 @@ megasas_build_syspd_fusion(struct megasas_instance *instance, pRAID_Context->type = MPI2_TYPE_CUDA; pRAID_Context->nseg = 0x1; pRAID_Context->reg_lock_flags |= - (MR_RL_FLAGS_SEQ_NUM_ENABLE | - MR_RL_FLAGS_GRANT_DESTINATION_CUDA); + (MR_RL_FLAGS_SEQ_NUM_ENABLE|MR_RL_FLAGS_GRANT_DESTINATION_CUDA); } } else { - pRAID_Context->virtual_disk_tgt_id = - cpu_to_le16(device_id + - (MAX_PHYSICAL_DEVICES - 1)); - pRAID_Context->config_seq_num = 0; - io_request->DevHandle = cpu_to_le16(0xFFFF); + pRAID_Context->virtual_disk_tgt_id = + cpu_to_le16(device_id + (MAX_PHYSICAL_DEVICES - 1)); + pRAID_Context->config_seq_num = 0; + io_request->DevHandle = cpu_to_le16(0xFFFF); + + if (instance->pd_list[pd_index].driveType == TYPE_TAPE) { + local_map_ptr = fusion->ld_drv_map[(instance->map_id & 1)]; + io_request->DevHandle = + local_map_ptr->raidMap.devHndlInfo[device_id].curDevHdl; + } } + } else { pRAID_Context->virtual_disk_tgt_id = cpu_to_le16(device_id); pRAID_Context->config_seq_num = 0; - + if (fusion->fast_path_io) { - local_map_ptr = - fusion->ld_drv_map[(instance->map_id & 1)]; + local_map_ptr = fusion->ld_drv_map[(instance->map_id & 1)]; io_request->DevHandle = local_map_ptr->raidMap.devHndlInfo[device_id].curDevHdl; } else { @@ -3158,20 +3291,9 @@ megasas_build_syspd_fusion(struct megasas_instance *instance, } cmd->request_desc->SCSIIO.DevHandle = io_request->DevHandle; - - if ((instance->perf_mode == MR_BALANCED_PERF_MODE) && - atomic_read(&scmd->device->device_busy) > MR_DEVICE_HIGH_IOPS_DEPTH) - cmd->request_desc->SCSIIO.MSIxIndex = - mega_mod64((atomic64_add_return(1, &instance->high_iops_outstanding) / - MR_HIGH_IOPS_BATCH_COUNT), instance->low_latency_index_start); - else if (instance->msix_load_balance) - cmd->request_desc->SCSIIO.MSIxIndex = - (mega_mod64(atomic64_add_return(1, &instance->total_io_count), - instance->msix_vectors)); - else - cmd->request_desc->SCSIIO.MSIxIndex = - instance->reply_map[raw_smp_processor_id()]; - + + megasas_get_msix_index(instance, scmd, cmd, 1); + if (!fp_possible) { /* system pd firmware path */ io_request->Function = MEGASAS_MPI2_FUNCTION_LD_IO_REQUEST; @@ -3181,6 +3303,9 @@ megasas_build_syspd_fusion(struct megasas_instance *instance, pRAID_Context->timeout_value = cpu_to_le16(os_timeout_value); pRAID_Context->virtual_disk_tgt_id = cpu_to_le16(device_id); } else { + /* Hardware errata. Fast Path engine need one second extra + * to avoid early timeout. + */ if (os_timeout_value) os_timeout_value++; @@ -3217,6 +3342,8 @@ megasas_build_io_fusion(struct megasas_instance *instance, { int sge_count; u8 cmd_type; + u16 pd_index = 0; + u8 driveType = 0; struct MPI2_RAID_SCSI_IO_REQUEST *io_request = cmd->io_request; struct MR_PRIV_DEVICE *mr_device_priv_data; mr_device_priv_data = scp->device->hostdata; @@ -3251,8 +3378,12 @@ megasas_build_io_fusion(struct megasas_instance *instance, megasas_build_syspd_fusion(instance, scp, cmd, true); break; case NON_READ_WRITE_SYSPDIO: - if (instance->secure_jbod_support || - mr_device_priv_data->is_tm_capable) + pd_index = MEGASAS_PD_INDEX(scp); + driveType = instance->pd_list[pd_index].driveType; + if ((instance->secure_jbod_support || + mr_device_priv_data->is_tm_capable) || + (instance->adapter_type >= VENTURA_SERIES && + driveType == TYPE_ENCLOSURE)) megasas_build_syspd_fusion(instance, scp, cmd, false); else megasas_build_syspd_fusion(instance, scp, cmd, true); @@ -3325,8 +3456,8 @@ megasas_get_request_descriptor(struct megasas_instance *instance, u16 index) * It prepares the raid 1 second IO */ static void megasas_prepare_secondRaid1_IO(struct megasas_instance *instance, - struct megasas_cmd_fusion *cmd, - struct megasas_cmd_fusion *r1_cmd) + struct megasas_cmd_fusion *cmd, + struct megasas_cmd_fusion *r1_cmd) { union MEGASAS_REQUEST_DESCRIPTOR_UNION *req_desc, *req_desc2 = NULL; struct fusion_context *fusion; @@ -3440,6 +3571,7 @@ megasas_build_and_issue_cmd_fusion(struct megasas_instance *instance, * Issue the command to the FW */ + megasas_sdev_busy_inc(instance, scmd); megasas_fire_cmd_fusion(instance, req_desc); if (r1_cmd) @@ -3500,18 +3632,54 @@ megasas_complete_r1_command(struct megasas_instance *instance, scmd_local->SCp.ptr = NULL; megasas_return_cmd_fusion(instance, cmd); scsi_dma_unmap(scmd_local); + megasas_sdev_busy_dec(instance, scmd_local); scmd_local->scsi_done(scmd_local); } } +/** + * access_irq_context: Access to reply processing + * @irq_context: IRQ context + * + * Synchronize access to reply processing. + * + * Return: true on success, false on failure. + */ +static inline +bool access_irq_context(struct megasas_irq_context *irq_context) +{ + if (!irq_context) + return true; + + if (atomic_add_unless(&irq_context->in_use, 1, 1)) + return true; + + return false; +} + +/** + * release_irq_context: Release reply processing + * @irq_context: IRQ context + * + * Release access of reply processing. + * + * Return: Nothing. + */ +static inline +void release_irq_context(struct megasas_irq_context *irq_context) +{ + if (irq_context) + atomic_dec(&irq_context->in_use); +} + /** * complete_cmd_fusion - Completes command * @instance: Adapter soft state * Completes all commands that is in reply descriptor queue */ -static int +int complete_cmd_fusion(struct megasas_instance *instance, u32 MSIxIndex, - struct megasas_irq_context *irq_context) + struct megasas_irq_context *irq_context) { union MPI2_REPLY_DESCRIPTORS_UNION *desc; struct MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR *reply_desc; @@ -3519,7 +3687,7 @@ complete_cmd_fusion(struct megasas_instance *instance, u32 MSIxIndex, struct fusion_context *fusion; struct megasas_cmd *cmd_mfi; struct megasas_cmd_fusion *cmd_fusion; - u16 smid, num_completed; + u16 smid, num_completed = 0; u8 reply_descript_type, *sense, status, extStatus; u32 device_id, data_length; union desc_value d_val; @@ -3534,6 +3702,9 @@ complete_cmd_fusion(struct megasas_instance *instance, u32 MSIxIndex, if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR) return IRQ_HANDLED; + if (!access_irq_context(irq_context)) + return 0; + desc = fusion->reply_frames_desc[MSIxIndex] + fusion->last_reply_idx[MSIxIndex]; @@ -3544,10 +3715,10 @@ complete_cmd_fusion(struct megasas_instance *instance, u32 MSIxIndex, reply_descript_type = reply_desc->ReplyFlags & MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK; - if (reply_descript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED) + if (reply_descript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED) { + release_irq_context(irq_context); return IRQ_NONE; - - num_completed = 0; + } while (d_val.u.low != cpu_to_le32(UINT_MAX) && d_val.u.high != cpu_to_le32(UINT_MAX)) { @@ -3597,6 +3768,7 @@ complete_cmd_fusion(struct megasas_instance *instance, u32 MSIxIndex, scmd_local->SCp.ptr = NULL; megasas_return_cmd_fusion(instance, cmd_fusion); scsi_dma_unmap(scmd_local); + megasas_sdev_busy_dec(instance, scmd_local); scmd_local->scsi_done(scmd_local); } else /* Optimal VD - R1 FP command completion. */ megasas_complete_r1_command(instance, cmd_fusion); @@ -3654,14 +3826,17 @@ complete_cmd_fusion(struct megasas_instance *instance, u32 MSIxIndex, fusion->last_reply_idx[MSIxIndex], instance->reply_post_host_index_addr[0]); threshold_reply_count = 0; +#if defined(ENABLE_IRQ_POLL) if (irq_context) { if (!irq_context->irq_poll_scheduled) { irq_context->irq_poll_scheduled = true; irq_context->irq_line_enable = true; irq_poll_sched(&irq_context->irqpoll); } + release_irq_context(irq_context); return num_completed; } +#endif } } @@ -3677,14 +3852,46 @@ complete_cmd_fusion(struct megasas_instance *instance, u32 MSIxIndex, instance->reply_post_host_index_addr[0]); megasas_check_and_restore_queue_depth(instance); } + + release_irq_context(irq_context); return num_completed; } -/** +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)) +int megasas_blk_mq_poll(struct Scsi_Host *shost, unsigned int queue_num) +{ + + struct megasas_instance *instance; + int num_entries = 0; + struct fusion_context *fusion; + + instance = (struct megasas_instance *)shost->hostdata; + + fusion = instance->ctrl_context; + + queue_num = queue_num + instance->low_latency_index_start; + + if (!atomic_add_unless(&fusion->busy_mq_poll[queue_num], 1, 1)) + return 0; + + num_entries = complete_cmd_fusion(instance, queue_num, NULL); + atomic_dec(&fusion->busy_mq_poll[queue_num]); + + return num_entries; +} +#else +int megasas_blk_mq_poll(struct Scsi_Host *shost, unsigned int queue_num) +{ + return 0; +} +#endif + +/* * megasas_enable_irq_poll() - enable irqpoll */ -static void megasas_enable_irq_poll(struct megasas_instance *instance) +void megasas_enable_irq_poll(struct megasas_instance *instance) { +#if defined(ENABLE_IRQ_POLL) u32 count, i; struct megasas_irq_context *irq_ctx; @@ -3694,33 +3901,40 @@ static void megasas_enable_irq_poll(struct megasas_instance *instance) irq_ctx = &instance->irq_context[i]; irq_poll_enable(&irq_ctx->irqpoll); } +#endif } /** * megasas_sync_irqs - Synchronizes all IRQs owned by adapter * @instance: Adapter soft state */ -static void megasas_sync_irqs(unsigned long instance_addr) +void megasas_sync_irqs(unsigned long instance_addr) { u32 count, i; struct megasas_instance *instance = (struct megasas_instance *)instance_addr; +#if defined(ENABLE_IRQ_POLL) struct megasas_irq_context *irq_ctx; +#endif count = instance->msix_vectors > 0 ? instance->msix_vectors : 1; for (i = 0; i < count; i++) { - synchronize_irq(pci_irq_vector(instance->pdev, i)); + synchronize_irq(megasas_get_irq(instance, i)); +#if defined(ENABLE_IRQ_POLL) irq_ctx = &instance->irq_context[i]; irq_poll_disable(&irq_ctx->irqpoll); if (irq_ctx->irq_poll_scheduled) { irq_ctx->irq_poll_scheduled = false; enable_irq(irq_ctx->os_irq); + complete_cmd_fusion(instance, irq_ctx->MSIxIndex, irq_ctx); } +#endif } } -/** +#if defined(ENABLE_IRQ_POLL) +/* * megasas_irqpoll() - process a queue for completed reply descriptors * @irqpoll: IRQ poll structure associated with queue to poll. * @budget: Threshold of reply descriptors to process per poll. @@ -3733,7 +3947,6 @@ int megasas_irqpoll(struct irq_poll *irqpoll, int budget) struct megasas_irq_context *irq_ctx; struct megasas_instance *instance; int num_entries; - irq_ctx = container_of(irqpoll, struct megasas_irq_context, irqpoll); instance = irq_ctx->instance; @@ -3747,10 +3960,12 @@ int megasas_irqpoll(struct irq_poll *irqpoll, int budget) irq_poll_complete(irqpoll); irq_ctx->irq_poll_scheduled = false; enable_irq(irq_ctx->os_irq); + complete_cmd_fusion(instance, irq_ctx->MSIxIndex, irq_ctx); } return num_entries; -} + } +#endif /** * megasas_complete_cmd_dpc_fusion - Completes command @@ -3758,12 +3973,13 @@ int megasas_irqpoll(struct irq_poll *irqpoll, int budget) * * Tasklet to complete cmds */ -static void +void megasas_complete_cmd_dpc_fusion(unsigned long instance_addr) { struct megasas_instance *instance = (struct megasas_instance *)instance_addr; u32 count, MSIxIndex; + struct megasas_irq_context *irq_ctx; count = instance->msix_vectors > 0 ? instance->msix_vectors : 1; @@ -3771,14 +3987,16 @@ megasas_complete_cmd_dpc_fusion(unsigned long instance_addr) if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR) return; - for (MSIxIndex = 0 ; MSIxIndex < count; MSIxIndex++) - complete_cmd_fusion(instance, MSIxIndex, NULL); + for (MSIxIndex = 0 ; MSIxIndex < count; MSIxIndex++) { + irq_ctx = &instance->irq_context[MSIxIndex]; + complete_cmd_fusion(instance, MSIxIndex, irq_ctx); + } } /** * megasas_isr_fusion - isr entry point */ -static irqreturn_t megasas_isr_fusion(int irq, void *devp) +irqreturn_t megasas_isr_fusion(int irq, void *devp) { struct megasas_irq_context *irq_context = devp; struct megasas_instance *instance = irq_context->instance; @@ -3787,8 +4005,10 @@ static irqreturn_t megasas_isr_fusion(int irq, void *devp) if (instance->mask_interrupts) return IRQ_NONE; +#if defined(ENABLE_IRQ_POLL) if (irq_context->irq_poll_scheduled) return IRQ_HANDLED; +#endif if (!instance->msix_vectors) { mfiStatus = instance->instancet->clear_intr(instance); @@ -3802,8 +4022,7 @@ static irqreturn_t megasas_isr_fusion(int irq, void *devp) return IRQ_HANDLED; } - return complete_cmd_fusion(instance, irq_context->MSIxIndex, irq_context) - ? IRQ_HANDLED : IRQ_NONE; + return complete_cmd_fusion(instance, irq_context->MSIxIndex, irq_context) ? IRQ_HANDLED : IRQ_NONE; } /** @@ -3812,7 +4031,7 @@ static irqreturn_t megasas_isr_fusion(int irq, void *devp) * mfi_cmd: megasas_cmd pointer * */ -static void +void build_mpt_mfi_pass_thru(struct megasas_instance *instance, struct megasas_cmd *mfi_cmd) { @@ -3870,7 +4089,7 @@ build_mpt_mfi_pass_thru(struct megasas_instance *instance, * @cmd: mfi cmd to build * */ -static union MEGASAS_REQUEST_DESCRIPTOR_UNION * +union MEGASAS_REQUEST_DESCRIPTOR_UNION * build_mpt_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd) { union MEGASAS_REQUEST_DESCRIPTOR_UNION *req_desc = NULL; @@ -3896,7 +4115,7 @@ build_mpt_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd) * @cmd: mfi cmd pointer * */ -static void +void megasas_issue_dcmd_fusion(struct megasas_instance *instance, struct megasas_cmd *cmd) { @@ -3931,7 +4150,8 @@ megasas_release_fusion(struct megasas_instance *instance) static u32 megasas_read_fw_status_reg_fusion(struct megasas_instance *instance) { - return megasas_readl(instance, &instance->reg_set->outbound_scratch_pad_0); + return megasas_readl(instance, + &instance->reg_set->outbound_scratch_pad_0); } /** @@ -4015,7 +4235,8 @@ megasas_adp_reset_fusion(struct megasas_instance *instance, msleep(3000); /* Make sure reset adapter bit is cleared */ - host_diag = megasas_readl(instance, &instance->reg_set->fusion_host_diag); + host_diag = megasas_readl(instance, + &instance->reg_set->fusion_host_diag); retry = 0; while (host_diag & HOST_DIAG_RESET_ADAPTER) { msleep(100); @@ -4063,7 +4284,7 @@ megasas_check_reset_fusion(struct megasas_instance *instance, /** * megasas_trigger_snap_dump - Trigger snap dump in FW - * @instance: Soft instance of adapter + * @instance: Soft instance of adapter */ static inline void megasas_trigger_snap_dump(struct megasas_instance *instance) { @@ -4073,28 +4294,29 @@ static inline void megasas_trigger_snap_dump(struct megasas_instance *instance) if (!instance->disableOnlineCtrlReset) { dev_info(&instance->pdev->dev, "Trigger snap dump\n"); writel(MFI_ADP_TRIGGER_SNAP_DUMP, - &instance->reg_set->doorbell); + &instance->reg_set->doorbell); readl(&instance->reg_set->doorbell); } - + for (j = 0; j < instance->snapdump_wait_time; j++) { abs_state = instance->instancet->read_fw_status_reg(instance); fw_state = abs_state & MFI_STATE_MASK; if (fw_state == MFI_STATE_FAULT) { 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__); + "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__); return; } msleep(1000); } + } /* This function waits for outstanding commands on fusion to complete */ -static int -megasas_wait_for_outstanding_fusion(struct megasas_instance *instance, - int reason, int *convert) +int megasas_wait_for_outstanding_fusion(struct megasas_instance *instance, + int reason, int *convert) { int i, outstanding, retval = 0, hb_seconds_missed = 0; u32 fw_state, abs_state; @@ -4120,9 +4342,10 @@ megasas_wait_for_outstanding_fusion(struct megasas_instance *instance, fw_state = abs_state & MFI_STATE_MASK; if (fw_state == MFI_STATE_FAULT) { 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__); + "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__); megasas_complete_cmd_dpc_fusion((unsigned long)instance); if (instance->requestorId && reason) { dev_warn(&instance->pdev->dev, "SR-IOV Found FW in FAULT" @@ -4168,7 +4391,10 @@ megasas_wait_for_outstanding_fusion(struct megasas_instance *instance, } megasas_complete_cmd_dpc_fusion((unsigned long)instance); + outstanding = atomic_read(&instance->fw_outstanding); + + if (!outstanding) goto out; @@ -4206,6 +4432,8 @@ void megasas_reset_reply_desc(struct megasas_instance *instance) fusion = instance->ctrl_context; count = instance->msix_vectors > 0 ? instance->msix_vectors : 1; + count += instance->iopoll_q_count; + for (i = 0 ; i < count ; i++) { fusion->last_reply_idx[i] = 0; reply_desc = fusion->reply_frames_desc[i]; @@ -4218,7 +4446,7 @@ void megasas_reset_reply_desc(struct megasas_instance *instance) * megasas_refire_mgmt_cmd : Re-fire management commands * @instance: Controller's soft instance */ -static void megasas_refire_mgmt_cmd(struct megasas_instance *instance) +void megasas_refire_mgmt_cmd(struct megasas_instance *instance, bool return_ioctl) { int j; struct megasas_cmd_fusion *cmd_fusion; @@ -4282,9 +4510,18 @@ static void megasas_refire_mgmt_cmd(struct megasas_instance *instance) default: break; } + + if (return_ioctl && cmd_mfi->sync_cmd && + (cmd_mfi->frame->hdr.cmd != MFI_CMD_ABORT)) { + dev_err(&instance->pdev->dev, + "return -EBUSY from %s %d cmd 0x%x opcode 0x%x \n", + __func__, __LINE__, cmd_mfi->frame->hdr.cmd, le32_to_cpu(cmd_mfi->frame->dcmd.opcode)); + cmd_mfi->cmd_status_drv = DCMD_BUSY; + result = COMPLETE_CMD; + } - scsi_io_req = (struct MPI2_RAID_SCSI_IO_REQUEST *) - cmd_fusion->io_request; + /* Do not re-fire TM command */ + scsi_io_req = (struct MPI2_RAID_SCSI_IO_REQUEST *) cmd_fusion->io_request; if (scsi_io_req->Function == MPI2_FUNCTION_SCSI_TASK_MGMT) result = RETURN_CMD; @@ -4302,6 +4539,37 @@ static void megasas_refire_mgmt_cmd(struct megasas_instance *instance) } } +/* + * megasas_return_polled_cmds: Return polled mode commands back to the pool + * before initiating an OCR. + * @instance: Controller's soft instance +*/ +static void +megasas_return_polled_cmds(struct megasas_instance *instance) +{ + int i; + struct megasas_cmd_fusion *cmd_fusion; + struct fusion_context *fusion; + struct megasas_cmd *cmd_mfi; + + fusion = instance->ctrl_context; + + for (i = instance->max_scsi_cmds; i < instance->max_fw_cmds; i++) { + cmd_fusion = fusion->cmd_list[i]; + cmd_mfi = instance->cmd_list[cmd_fusion->sync_cmd_idx]; + + if (cmd_mfi->flags & DRV_DCMD_POLLED_MODE) { + if (megasas_dbg_lvl & OCR_DEBUG) + dev_info(&instance->pdev->dev, + "%s %d return cmd 0x%x opcode 0x%x\n", + __func__, __LINE__, cmd_mfi->frame->hdr.cmd, + le32_to_cpu(cmd_mfi->frame->dcmd.opcode)); + cmd_mfi->flags &= ~DRV_DCMD_POLLED_MODE; + megasas_return_cmd(instance, cmd_mfi); + } + } +} + /* * megasas_track_scsiio : Track SCSI IOs outstanding to a SCSI device * @instance: per adapter struct @@ -4708,8 +4976,8 @@ int megasas_reset_target_fusion(struct scsi_cmnd *scmd) } if (!mr_device_priv_data) { - sdev_printk(KERN_INFO, scmd->device, - "device been deleted! scmd: (0x%p)\n", scmd); + sdev_printk(KERN_INFO, scmd->device, "device been deleted! " + "scmd: (0x%p)\n", scmd); scmd->result = DID_NO_CONNECT << 16; ret = SUCCESS; goto out; @@ -4743,14 +5011,13 @@ int megasas_reset_target_fusion(struct scsi_cmnd *scmd) mutex_unlock(&instance->reset_mutex); scmd_printk(KERN_NOTICE, scmd, "target reset %s!!\n", (ret == SUCCESS) ? "SUCCESS" : "FAILED"); - out: + return ret; } /*SRIOV get other instance in cluster if any*/ -static struct -megasas_instance *megasas_get_peer_instance(struct megasas_instance *instance) +struct megasas_instance *megasas_get_peer_instance(struct megasas_instance *instance) { int i; @@ -4820,7 +5087,8 @@ int megasas_reset_fusion(struct Scsi_Host *shost, int reason) dev_info(&instance->pdev->dev, "IO/DCMD timeout is detected, " "forcibly FAULT Firmware\n"); atomic_set(&instance->adprecovery, MEGASAS_ADPRESET_SM_INFAULT); - status_reg = megasas_readl(instance, &instance->reg_set->doorbell); + status_reg = megasas_readl(instance, + &instance->reg_set->doorbell); writel(status_reg | MFI_STATE_FORCE_OCR, &instance->reg_set->doorbell); readl(&instance->reg_set->doorbell); @@ -4900,7 +5168,7 @@ int megasas_reset_fusion(struct Scsi_Host *shost, int reason) scmd_local->scsi_done(scmd_local); } } - + dev_info(&instance->pdev->dev, "Outstanding fastpath IOs: %d\n", fpio_count); @@ -4932,16 +5200,10 @@ int megasas_reset_fusion(struct Scsi_Host *shost, int reason) * controller to transition to ready */ if (megasas_adp_reset_wait_for_ready(instance, - do_adp_reset, 1) == FAILED) - continue; - - /* Wait for FW to become ready */ - if (megasas_transition_to_ready(instance, 1)) { - dev_warn(&instance->pdev->dev, - "Failed to transition controller to ready for " - "scsi%d.\n", instance->host->host_no); + do_adp_reset, 1) == FAILED) { continue; } + megasas_reset_reply_desc(instance); megasas_fusion_update_can_queue(instance, OCR_CONTEXT); @@ -4956,7 +5218,7 @@ int megasas_reset_fusion(struct Scsi_Host *shost, int reason) goto kill_hba; } - megasas_refire_mgmt_cmd(instance); + megasas_refire_mgmt_cmd(instance, (i == (MEGASAS_FUSION_MAX_RESET_TRIES - 1) ? 1 : 0)); /* Reset load balance info */ if (fusion->load_balance_info) @@ -4964,8 +5226,16 @@ int megasas_reset_fusion(struct Scsi_Host *shost, int reason) (sizeof(struct LD_LOAD_BALANCE_INFO) * MAX_LOGICAL_DRIVES_EXT)); - if (!megasas_get_map_info(instance)) + if (!megasas_get_map_info(instance)) { megasas_sync_map_info(instance); + } else { + /* + * Return pending polled mode cmds before + * retrying OCR + */ + megasas_return_polled_cmds(instance); + continue; + } megasas_setup_jbod_map(instance); @@ -4983,20 +5253,29 @@ int megasas_reset_fusion(struct Scsi_Host *shost, int reason) &instance->reset_flags); instance->instancet->enable_intr(instance); megasas_enable_irq_poll(instance); + shost_for_each_device(sdev, shost) { - if ((instance->tgt_prop) && - (instance->nvme_page_size)) + if ((instance->tgt_prop) && (instance->nvme_page_size)) ret_target_prop = megasas_get_target_prop(instance, sdev); is_target_prop = (ret_target_prop == DCMD_SUCCESS) ? true : false; megasas_set_dynamic_target_properties(sdev, is_target_prop); } + status_reg = instance->instancet->read_fw_status_reg(instance); + abs_state = status_reg & MFI_STATE_MASK; + if (abs_state != MFI_STATE_OPERATIONAL) { + dev_info(&instance->pdev->dev, + "Adapter is not OPERATIONAL, state 0x%x for scsi:%d\n", + abs_state, instance->host->host_no); + goto out; + } + atomic_set(&instance->adprecovery, MEGASAS_HBA_OPERATIONAL); dev_info(&instance->pdev->dev, - "Adapter is OPERATIONAL for scsi:%d\n", - instance->host->host_no); + "Adapter is OPERATIONAL for scsi:%d\n", + instance->host->host_no); /* Restart SR-IOV heartbeat */ if (instance->requestorId) { @@ -5057,7 +5336,7 @@ out: } /* Fusion Crash dump collection */ -static void megasas_fusion_crash_dump(struct megasas_instance *instance) +void megasas_fusion_crash_dump(struct megasas_instance *instance) { u32 status_reg; u8 partial_copy = 0; @@ -5090,7 +5369,7 @@ static void megasas_fusion_crash_dump(struct megasas_instance *instance) } while (!(status_reg & MFI_STATE_CRASH_DUMP_DONE) && - (wait < MEGASAS_WATCHDOG_WAIT_COUNT)) { + (wait < MEGASAS_WATCHDOG_WAIT_COUNT)) { if (!(status_reg & MFI_STATE_DMADONE)) { /* * Next crash dump buffer is not yet DMA'd by FW @@ -5099,8 +5378,7 @@ static void megasas_fusion_crash_dump(struct megasas_instance *instance) */ wait++; msleep(MEGASAS_WAIT_FOR_NEXT_DMA_MSECS); - status_reg = instance->instancet->read_fw_status_reg( - instance); + status_reg = instance->instancet->read_fw_status_reg(instance); continue; } @@ -5109,12 +5387,12 @@ static void megasas_fusion_crash_dump(struct megasas_instance *instance) dev_info(&instance->pdev->dev, "Driver is done copying the buffer: %d\n", instance->drv_buf_alloc); - status_reg |= MFI_STATE_CRASH_DUMP_DONE; + status_reg |= MFI_STATE_CRASH_DUMP_DONE; /* notify FW that driver is done copying crash dump*/ partial_copy = 1; break; } else { - memcpy(instance->crash_buf[instance->drv_buf_index], - instance->crash_dump_buf, CRASH_DMA_BUF_SIZE); + memcpy(instance->crash_buf[instance->drv_buf_index], instance->crash_dump_buf, + CRASH_DMA_BUF_SIZE); instance->drv_buf_index++; status_reg &= ~MFI_STATE_DMADONE; } @@ -5172,11 +5450,12 @@ megasas_alloc_fusion_context(struct megasas_instance *instance) fusion->log_to_span_pages); if (!fusion->log_to_span) { fusion->log_to_span = - vzalloc(array_size(MAX_LOGICAL_DRIVES_EXT, - sizeof(LD_SPAN_INFO))); + vzalloc(MAX_LOGICAL_DRIVES_EXT * + sizeof(LD_SPAN_INFO)); if (!fusion->log_to_span) { dev_err(&instance->pdev->dev, "Failed from %s %d\n", __func__, __LINE__); + kfree(instance->ctrl_context); return -ENOMEM; } } @@ -5188,8 +5467,8 @@ megasas_alloc_fusion_context(struct megasas_instance *instance) fusion->load_balance_info_pages); if (!fusion->load_balance_info) { fusion->load_balance_info = - vzalloc(array_size(MAX_LOGICAL_DRIVES_EXT, - sizeof(struct LD_LOAD_BALANCE_INFO))); + vzalloc(MAX_LOGICAL_DRIVES_EXT * + sizeof(struct LD_LOAD_BALANCE_INFO)); if (!fusion->load_balance_info) dev_err(&instance->pdev->dev, "Failed to allocate load_balance_info, " "continuing without Load Balance support\n"); diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.h b/drivers/scsi/megaraid/megaraid_sas_fusion.h index dd2e37e40..4879ff8fc 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.h +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.h @@ -1,10 +1,22 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Linux MegaRAID driver for SAS based RAID controllers * - * Copyright (c) 2009-2013 LSI Corporation - * Copyright (c) 2013-2016 Avago Technologies - * Copyright (c) 2016-2018 Broadcom Inc. + * Copyright (c) 2009-2018 LSI Corporation. + * Copyright (c) 2009-2018 Avago Technologies. + * Copyright (c) 2009-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 . * * FILE: megaraid_sas_fusion.h * @@ -140,16 +152,17 @@ struct RAID_CONTEXT_G35 { u16 nseg_type; u16 timeout_value; /* 0x02 -0x03 */ u16 routing_flags; // 0x04 -0x05 routing flags - u16 virtual_disk_tgt_id; /* 0x06 -0x07 */ - __le64 reg_lock_row_lba; /* 0x08 - 0x0F */ - u32 reg_lock_length; /* 0x10 - 0x13 */ - union { // flow specific - u16 rmw_op_index; /* 0x14 - 0x15, R5/6 RMW: rmw operation index*/ - u16 peer_smid; /* 0x14 - 0x15, R1 Write: peer smid*/ - u16 r56_arm_map; /* 0x14 - 0x15, Unused [15], LogArm[14:10], P-Arm[9:5], Q-Arm[4:0] */ + u16 virtual_disk_tgt_id; /* 0x06 -0x07 */ + __le64 reg_lock_row_lba; /* 0x08 - 0x0F */ + u32 reg_lock_length; /* 0x10 - 0x13 */ + union { // flow specific + u16 rmw_op_index; /* 0x14 - 0x15, R5/6 RMW: rmw operation index*/ + u16 peer_smid; /* 0x14 - 0x15, R1 Write: peer smid*/ + u16 r56_arm_map; /* 0x14 - 0x15, Unused [15], LogArm[14:10], P-Arm[9:5], Q-Arm[4:0] */ } flow_specific; + u8 ex_status; /* 0x16 : OUT */ u8 status; /* 0x17 status */ u8 raid_flags; /* 0x18 resvd[7:6], ioSubType[5:4], @@ -246,7 +259,7 @@ union RAID_CONTEXT_UNION { #define RAID_CTX_R56_P_ARM_MASK (0x3E0) #define RAID_CTX_R56_LOG_ARM_SHIFT (10) #define RAID_CTX_R56_LOG_ARM_MASK (0x7C00) - + /* number of bits per index in U32 TrackStream */ #define BITS_PER_INDEX_STREAM 4 #define INVALID_STREAM_NUM 16 @@ -774,7 +787,7 @@ struct MR_SPAN_BLOCK_INFO { struct MR_CPU_AFFINITY_MASK { union { struct { -#ifndef MFI_BIG_ENDIAN +#ifndef __BIG_ENDIAN_BITFIELD u8 hw_path:1; u8 cpu0:1; u8 cpu1:1; @@ -866,8 +879,19 @@ struct MR_LD_RAID { __le16 seqNum; struct { +#ifndef __BIG_ENDIAN_BITFIELD u32 ldSyncRequired:1; - u32 reserved:31; + u32 regTypeReqOnReadIsValid:1; // Qualifier bit for regTypeOnRead + u32 isEPD:1; // Set if LD is an "Enhanced Physical Drive" + u32 enableSLDOnAllRWIOs:1; // If set, driver should unconditionally set SLD bit for all Read Write IOs + u32 reserved:28; +#else + u32 reserved:28; + u32 enableSLDOnAllRWIOs:1; // If set, driver should unconditionally set SLD bit for all Read Write IOs + u32 isEPD:1; // Set if LD is an "Enhanced Physical Drive" + u32 regTypeReqOnReadIsValid:1; // Qualifier bit for regTypeOnRead + u32 ldSyncRequired:1; +#endif } flags; u8 LUN[8]; /* 0x24 8 byte LUN field used for SCSI IO's */ @@ -878,7 +902,7 @@ struct MR_LD_RAID { /* 0x30 - 0x33, Logical block size for the LD */ u32 logical_block_length; struct { -#ifndef MFI_BIG_ENDIAN +#ifndef __BIG_ENDIAN_BITFIELD /* 0x34, P_I_EXPONENT from READ CAPACITY 16 */ u32 ld_pi_exp:4; /* 0x34, LOGICAL BLOCKS PER PHYSICAL @@ -1063,6 +1087,7 @@ struct MR_FW_RAID_MAP_DYNAMIC { #define MPI26_IEEE_SGE_FLAGS_NSF_NVME_PRP (0x08) #define MPI26_IEEE_SGE_FLAGS_NSF_NVME_SGL (0x10) + #define MEGASAS_DEFAULT_SNAP_DUMP_WAIT_TIME 15 #define MEGASAS_MAX_SNAP_DUMP_WAIT_TIME 60 @@ -1292,6 +1317,7 @@ struct fusion_context { u8 *sense; dma_addr_t sense_phys_addr; + atomic_t busy_mq_poll[MAX_MSIX_QUEUES_FUSION]; dma_addr_t reply_frames_desc_phys[MAX_MSIX_QUEUES_FUSION]; union MPI2_REPLY_DESCRIPTORS_UNION *reply_frames_desc[MAX_MSIX_QUEUES_FUSION]; struct rdpq_alloc_detail rdpq_tracker[RDPQ_MAX_CHUNK_COUNT]; @@ -1355,13 +1381,14 @@ enum CMD_RET_VALUES { }; struct MR_SNAPDUMP_PROPERTIES { - u8 offload_num; - u8 max_num_supported; - u8 cur_num_supported; - u8 trigger_min_num_sec_before_ocr; - u8 reserved[12]; + u8 offload_num; + u8 max_num_supported; + u8 cur_num_supported; + u8 trigger_min_num_sec_before_ocr; + u8 reserved[12]; }; + struct megasas_debugfs_buffer { void *buf; u32 len;