diff options
Diffstat (limited to 'drivers/scsi/mpt3sas')
| -rw-r--r-- | drivers/scsi/mpt3sas/Kconfig | 11 | ||||
| -rw-r--r-- | drivers/scsi/mpt3sas/Makefile | 3 | ||||
| -rw-r--r-- | drivers/scsi/mpt3sas/mpi/mpi2.h | 18 | ||||
| -rw-r--r-- | drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h | 275 | ||||
| -rw-r--r-- | drivers/scsi/mpt3sas/mpi/mpi2_image.h | 78 | ||||
| -rw-r--r-- | drivers/scsi/mpt3sas/mpi/mpi2_ioc.h | 95 | ||||
| -rw-r--r-- | drivers/scsi/mpt3sas/mpi/mpi2_pci.h | 13 | ||||
| -rw-r--r-- | drivers/scsi/mpt3sas/mpi/mpi2_tool.h | 13 | ||||
| -rw-r--r-- | drivers/scsi/mpt3sas/mpt3sas_base.c | 3481 | ||||
| -rw-r--r-- | drivers/scsi/mpt3sas/mpt3sas_base.h | 466 | ||||
| -rw-r--r-- | drivers/scsi/mpt3sas/mpt3sas_config.c | 1036 | ||||
| -rw-r--r-- | drivers/scsi/mpt3sas/mpt3sas_ctl.c | 1501 | ||||
| -rw-r--r-- | drivers/scsi/mpt3sas/mpt3sas_ctl.h | 86 | ||||
| -rw-r--r-- | drivers/scsi/mpt3sas/mpt3sas_debugfs.c | 157 | ||||
| -rw-r--r-- | drivers/scsi/mpt3sas/mpt3sas_scsih.c | 2818 | ||||
| -rw-r--r-- | drivers/scsi/mpt3sas/mpt3sas_transport.c | 392 | ||||
| -rw-r--r-- | drivers/scsi/mpt3sas/mpt3sas_trigger_diag.c | 50 | ||||
| -rw-r--r-- | drivers/scsi/mpt3sas/mpt3sas_trigger_diag.h | 2 | ||||
| -rw-r--r-- | drivers/scsi/mpt3sas/mpt3sas_trigger_pages.h | 94 | ||||
| -rw-r--r-- | drivers/scsi/mpt3sas/mpt3sas_warpdrive.c | 5 |
20 files changed, 8746 insertions, 1848 deletions
diff --git a/drivers/scsi/mpt3sas/Kconfig b/drivers/scsi/mpt3sas/Kconfig index b736dbc80485..c299f7e078fb 100644 --- a/drivers/scsi/mpt3sas/Kconfig +++ b/drivers/scsi/mpt3sas/Kconfig @@ -45,7 +45,8 @@ config SCSI_MPT3SAS depends on PCI && SCSI select SCSI_SAS_ATTRS select RAID_ATTRS - ---help--- + select IRQ_POLL + help This driver supports PCI-Express SAS 12Gb/s Host Adapters. config SCSI_MPT2SAS_MAX_SGE @@ -53,7 +54,7 @@ config SCSI_MPT2SAS_MAX_SGE depends on PCI && SCSI && SCSI_MPT3SAS default "128" range 16 256 - ---help--- + help This option allows you to specify the maximum number of scatter- gather entries per I/O. The driver default is 128, which matches MAX_PHYS_SEGMENTS in most kernels. However in SuSE kernels this @@ -65,7 +66,7 @@ config SCSI_MPT3SAS_MAX_SGE depends on PCI && SCSI && SCSI_MPT3SAS default "128" range 16 256 - ---help--- + help This option allows you to specify the maximum number of scatter- gather entries per I/O. The driver default is 128, which matches MAX_PHYS_SEGMENTS in most kernels. However in SuSE kernels this @@ -77,6 +78,6 @@ config SCSI_MPT2SAS default n select SCSI_MPT3SAS depends on PCI && SCSI - ---help--- - Dummy config option for backwards compatiblity: configure the MPT3SAS + help + Dummy config option for backwards compatibility: configure the MPT3SAS driver instead. diff --git a/drivers/scsi/mpt3sas/Makefile b/drivers/scsi/mpt3sas/Makefile index 84fb3fbdb0ca..e76d994dbed3 100644 --- a/drivers/scsi/mpt3sas/Makefile +++ b/drivers/scsi/mpt3sas/Makefile @@ -7,4 +7,5 @@ mpt3sas-y += mpt3sas_base.o \ mpt3sas_transport.o \ mpt3sas_ctl.o \ mpt3sas_trigger_diag.o \ - mpt3sas_warpdrive.o + mpt3sas_warpdrive.o \ + mpt3sas_debugfs.o \ diff --git a/drivers/scsi/mpt3sas/mpi/mpi2.h b/drivers/scsi/mpt3sas/mpi/mpi2.h index 7efd17a3c25b..b181b113fc80 100644 --- a/drivers/scsi/mpt3sas/mpi/mpi2.h +++ b/drivers/scsi/mpt3sas/mpi/mpi2.h @@ -9,7 +9,7 @@ * scatter/gather formats. * Creation Date: June 21, 2006 * - * mpi2.h Version: 02.00.53 + * mpi2.h Version: 02.00.54 * * NOTE: Names (typedefs, defines, etc.) beginning with an MPI25 or Mpi25 * prefix are for use only on MPI v2.5 products, and must not be used @@ -121,6 +121,16 @@ * 08-15-18 02.00.52 Bumped MPI2_HEADER_VERSION_UNIT. * 08-28-18 02.00.53 Bumped MPI2_HEADER_VERSION_UNIT. * Added MPI2_IOCSTATUS_FAILURE + * 12-17-18 02.00.54 Bumped MPI2_HEADER_VERSION_UNIT + * 06-24-19 02.00.55 Bumped MPI2_HEADER_VERSION_UNIT + * 08-01-19 02.00.56 Bumped MPI2_HEADER_VERSION_UNIT + * 10-02-19 02.00.57 Bumped MPI2_HEADER_VERSION_UNIT + * 07-20-20 02.00.58 Bumped MPI2_HEADER_VERSION_UNIT + * 03-30-21 02.00.59 Bumped MPI2_HEADER_VERSION_UNIT + * 06-03-22 02.00.60 Bumped MPI2_HEADER_VERSION_UNIT + * 09-20-23 02.00.61 Bumped MPI2_HEADER_VERSION_UNIT + * 09-13-24 02.00.62 Bumped MPI2_HEADER_VERSION_UNIT + * Added MPI2_FUNCTION_MCTP_PASSTHROUGH * -------------------------------------------------------------------------- */ @@ -161,7 +171,7 @@ /* Unit and Dev versioning for this MPI header set */ -#define MPI2_HEADER_VERSION_UNIT (0x35) +#define MPI2_HEADER_VERSION_UNIT (0x3E) #define MPI2_HEADER_VERSION_DEV (0x00) #define MPI2_HEADER_VERSION_UNIT_MASK (0xFF00) #define MPI2_HEADER_VERSION_UNIT_SHIFT (8) @@ -180,6 +190,7 @@ #define MPI2_IOC_STATE_READY (0x10000000) #define MPI2_IOC_STATE_OPERATIONAL (0x20000000) #define MPI2_IOC_STATE_FAULT (0x40000000) +#define MPI2_IOC_STATE_COREDUMP (0x50000000) #define MPI2_IOC_STATE_MASK (0xF0000000) #define MPI2_IOC_STATE_SHIFT (28) @@ -194,7 +205,7 @@ * *****************************************************************************/ -typedef volatile struct _MPI2_SYSTEM_INTERFACE_REGS { +typedef struct _MPI2_SYSTEM_INTERFACE_REGS { U32 Doorbell; /*0x00 */ U32 WriteSequence; /*0x04 */ U32 HostDiagnostic; /*0x08 */ @@ -664,6 +675,7 @@ typedef union _MPI2_REPLY_DESCRIPTORS_UNION { #define MPI2_FUNCTION_PWR_MGMT_CONTROL (0x30) #define MPI2_FUNCTION_SEND_HOST_MESSAGE (0x31) #define MPI2_FUNCTION_NVME_ENCAPSULATED (0x33) +#define MPI2_FUNCTION_MCTP_PASSTHROUGH (0x34) #define MPI2_FUNCTION_MIN_PRODUCT_SPECIFIC (0xF0) #define MPI2_FUNCTION_MAX_PRODUCT_SPECIFIC (0xFF) diff --git a/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h b/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h index 398fa6fde960..02bf26ca976e 100644 --- a/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h +++ b/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h @@ -7,7 +7,7 @@ * Title: MPI Configuration messages and pages * Creation Date: November 10, 2006 * - * mpi2_cnfg.h Version: 02.00.46 + * mpi2_cnfg.h Version: 02.00.47 * * NOTE: Names (typedefs, defines, etc.) beginning with an MPI25 or Mpi25 * prefix are for use only on MPI v2.5 products, and must not be used @@ -244,6 +244,14 @@ * Added DMDReport Delay Time defines to * PCIeIOUnitPage1 * -------------------------------------------------------------------------- + * 08-02-18 02.00.44 Added Slotx2, Slotx4 to ManPage 7. + * 08-15-18 02.00.45 Added ProductSpecific field at end of IOC Page 1 + * 08-28-18 02.00.46 Added NVMs Write Cache flag to IOUnitPage1 + * Added DMDReport Delay Time defines to PCIeIOUnitPage1 + * 12-17-18 02.00.47 Swap locations of Slotx2 and Slotx4 in ManPage 7. + * 08-01-19 02.00.49 Add MPI26_MANPAGE7_FLAG_X2_X4_SLOT_INFO_VALID + * Add MPI26_IOUNITPAGE1_NVME_WRCACHE_SHIFT + * 09-13-24 02.00.50 Added PCIe 32 GT/s link rate */ #ifndef MPI2_CNFG_H @@ -527,6 +535,7 @@ typedef struct _MPI2_CONFIG_REPLY { ****************************************************************************/ #define MPI2_MFGPAGE_VENDORID_LSI (0x1000) +#define MPI2_MFGPAGE_VENDORID_ATTO (0x117C) /*MPI v2.0 SAS products */ #define MPI2_MFGPAGE_DEVID_SAS2004 (0x0070) @@ -548,7 +557,8 @@ typedef struct _MPI2_CONFIG_REPLY { #define MPI2_MFGPAGE_DEVID_SAS2308_1 (0x0086) #define MPI2_MFGPAGE_DEVID_SAS2308_2 (0x0087) #define MPI2_MFGPAGE_DEVID_SAS2308_3 (0x006E) -#define MPI2_MFGPAGE_DEVID_SAS2308_MPI_EP (0x02B0) +#define MPI2_MFGPAGE_DEVID_SWITCH_MPI_EP (0x02B0) +#define MPI2_MFGPAGE_DEVID_SWITCH_MPI_EP_1 (0x02B1) /*MPI v2.5 SAS products */ #define MPI25_MFGPAGE_DEVID_SAS3004 (0x0096) @@ -597,7 +607,7 @@ typedef struct _MPI2_CONFIG_REPLY { typedef struct _MPI2_CONFIG_PAGE_MAN_0 { MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ - U8 ChipName[16]; /*0x04 */ + U8 ChipName[16] __nonstring; /*0x04 */ U8 ChipRevision[8]; /*0x14 */ U8 BoardName[16]; /*0x1C */ U8 BoardAssembly[16]; /*0x2C */ @@ -634,18 +644,14 @@ typedef struct _MPI2_CHIP_REVISION_ID { /*Manufacturing Page 2 */ /* - *Host code (drivers, BIOS, utilities, etc.) should leave this define set to - *one and check Header.PageLength at runtime. + *Host code (drivers, BIOS, utilities, etc.) should check Header.PageLength at + *runtime before using HwSettings[]. */ -#ifndef MPI2_MAN_PAGE_2_HW_SETTINGS_WORDS -#define MPI2_MAN_PAGE_2_HW_SETTINGS_WORDS (1) -#endif typedef struct _MPI2_CONFIG_PAGE_MAN_2 { MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ MPI2_CHIP_REVISION_ID ChipId; /*0x04 */ - U32 - HwSettings[MPI2_MAN_PAGE_2_HW_SETTINGS_WORDS];/*0x08 */ + U32 HwSettings[]; /*0x08 */ } MPI2_CONFIG_PAGE_MAN_2, *PTR_MPI2_CONFIG_PAGE_MAN_2, Mpi2ManufacturingPage2_t, @@ -657,18 +663,14 @@ typedef struct _MPI2_CONFIG_PAGE_MAN_2 { /*Manufacturing Page 3 */ /* - *Host code (drivers, BIOS, utilities, etc.) should leave this define set to - *one and check Header.PageLength at runtime. + *Host code (drivers, BIOS, utilities, etc.) should check Header.PageLength at + *runtime before using Info[]. */ -#ifndef MPI2_MAN_PAGE_3_INFO_WORDS -#define MPI2_MAN_PAGE_3_INFO_WORDS (1) -#endif typedef struct _MPI2_CONFIG_PAGE_MAN_3 { MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ MPI2_CHIP_REVISION_ID ChipId; /*0x04 */ - U32 - Info[MPI2_MAN_PAGE_3_INFO_WORDS];/*0x08 */ + U32 Info[]; /*0x08 */ } MPI2_CONFIG_PAGE_MAN_3, *PTR_MPI2_CONFIG_PAGE_MAN_3, Mpi2ManufacturingPage3_t, @@ -756,12 +758,9 @@ typedef struct _MPI2_CONFIG_PAGE_MAN_4 { /*Manufacturing Page 5 */ /* - *Host code (drivers, BIOS, utilities, etc.) should leave this define set to - *one and check the value returned for NumPhys at runtime. + *Host code (drivers, BIOS, utilities, etc.) should check the value returned + *for NumPhys at runtime before using Phy[]. */ -#ifndef MPI2_MAN_PAGE_5_PHY_ENTRIES -#define MPI2_MAN_PAGE_5_PHY_ENTRIES (1) -#endif typedef struct _MPI2_MANUFACTURING5_ENTRY { U64 WWID; /*0x00 */ @@ -778,8 +777,7 @@ typedef struct _MPI2_CONFIG_PAGE_MAN_5 { U16 Reserved2; /*0x06 */ U32 Reserved3; /*0x08 */ U32 Reserved4; /*0x0C */ - MPI2_MANUFACTURING5_ENTRY - Phy[MPI2_MAN_PAGE_5_PHY_ENTRIES];/*0x08 */ + MPI2_MANUFACTURING5_ENTRY Phy[]; /*0x10 */ } MPI2_CONFIG_PAGE_MAN_5, *PTR_MPI2_CONFIG_PAGE_MAN_5, Mpi2ManufacturingPage5_t, @@ -809,7 +807,8 @@ typedef struct _MPI2_MANPAGE7_CONNECTOR_INFO { U8 Location; /*0x14 */ U8 ReceptacleID; /*0x15 */ U16 Slot; /*0x16 */ - U32 Reserved2; /*0x18 */ + U16 Slotx2; /*0x18 */ + U16 Slotx4; /*0x1A */ } MPI2_MANPAGE7_CONNECTOR_INFO, *PTR_MPI2_MANPAGE7_CONNECTOR_INFO, Mpi2ManPage7ConnectorInfo_t, @@ -854,12 +853,9 @@ typedef struct _MPI2_MANPAGE7_CONNECTOR_INFO { #define MPI2_MANPAGE7_SLOT_UNKNOWN (0xFFFF) /* - *Host code (drivers, BIOS, utilities, etc.) should leave this define set to - *one and check the value returned for NumPhys at runtime. + *Host code (drivers, BIOS, utilities, etc.) should check the value returned + *for NumPhys at runtime before using ConnectorInfo[]. */ -#ifndef MPI2_MANPAGE7_CONNECTOR_INFO_MAX -#define MPI2_MANPAGE7_CONNECTOR_INFO_MAX (1) -#endif typedef struct _MPI2_CONFIG_PAGE_MAN_7 { MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ @@ -870,8 +866,7 @@ typedef struct _MPI2_CONFIG_PAGE_MAN_7 { U8 NumPhys; /*0x20 */ U8 Reserved3; /*0x21 */ U16 Reserved4; /*0x22 */ - MPI2_MANPAGE7_CONNECTOR_INFO - ConnectorInfo[MPI2_MANPAGE7_CONNECTOR_INFO_MAX]; /*0x24 */ + MPI2_MANPAGE7_CONNECTOR_INFO ConnectorInfo[]; /*0x24 */ } MPI2_CONFIG_PAGE_MAN_7, *PTR_MPI2_CONFIG_PAGE_MAN_7, Mpi2ManufacturingPage7_t, @@ -884,6 +879,8 @@ typedef struct _MPI2_CONFIG_PAGE_MAN_7 { #define MPI2_MANPAGE7_FLAG_EVENTREPLAY_SLOT_ORDER (0x00000002) #define MPI2_MANPAGE7_FLAG_USE_SLOT_INFO (0x00000001) +#define MPI26_MANPAGE7_FLAG_CONN_LANE_USE_PINOUT (0x00000020) +#define MPI26_MANPAGE7_FLAG_X2_X4_SLOT_INFO_VALID (0x00000010) /* *Generic structure to use for product-specific manufacturing pages @@ -955,9 +952,10 @@ typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_1 { /* IO Unit Page 1 Flags defines */ #define MPI26_IOUNITPAGE1_NVME_WRCACHE_MASK (0x00030000) -#define MPI26_IOUNITPAGE1_NVME_WRCACHE_ENABLE (0x00000000) -#define MPI26_IOUNITPAGE1_NVME_WRCACHE_DISABLE (0x00010000) -#define MPI26_IOUNITPAGE1_NVME_WRCACHE_NO_CHANGE (0x00020000) +#define MPI26_IOUNITPAGE1_NVME_WRCACHE_SHIFT (16) +#define MPI26_IOUNITPAGE1_NVME_WRCACHE_NO_CHANGE (0x00000000) +#define MPI26_IOUNITPAGE1_NVME_WRCACHE_ENABLE (0x00010000) +#define MPI26_IOUNITPAGE1_NVME_WRCACHE_DISABLE (0x00020000) #define MPI2_IOUNITPAGE1_ATA_SECURITY_FREEZE_LOCK (0x00004000) #define MPI25_IOUNITPAGE1_NEW_DEVICE_FAST_PATH_DISABLE (0x00002000) #define MPI25_IOUNITPAGE1_DISABLE_FAST_PATH (0x00001000) @@ -977,10 +975,10 @@ typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_1 { /* *Host code (drivers, BIOS, utilities, etc.) should leave this define set to - *one and check the value returned for GPIOCount at runtime. + *36 and check the value returned for GPIOCount at runtime. */ #ifndef MPI2_IO_UNIT_PAGE_3_GPIO_VAL_MAX -#define MPI2_IO_UNIT_PAGE_3_GPIO_VAL_MAX (1) +#define MPI2_IO_UNIT_PAGE_3_GPIO_VAL_MAX (36) #endif typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_3 { @@ -1006,12 +1004,9 @@ typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_3 { /*IO Unit Page 5 */ /* - *Upper layer code (drivers, utilities, etc.) should leave this define set to - *one and check the value returned for NumDmaEngines at runtime. + *Upper layer code (drivers, utilities, etc.) should check the value returned + *for NumDmaEngines at runtime before using DmaEngineCapabilities[]. */ -#ifndef MPI2_IOUNITPAGE5_DMAENGINE_ENTRIES -#define MPI2_IOUNITPAGE5_DMAENGINE_ENTRIES (1) -#endif typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_5 { MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ @@ -1029,7 +1024,7 @@ typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_5 { U32 Reserved2; /*0x24 */ U32 Reserved3; /*0x28 */ U32 - DmaEngineCapabilities[MPI2_IOUNITPAGE5_DMAENGINE_ENTRIES]; /*0x2C */ + DmaEngineCapabilities[]; /*0x2C */ } MPI2_CONFIG_PAGE_IO_UNIT_5, *PTR_MPI2_CONFIG_PAGE_IO_UNIT_5, Mpi2IOUnitPage5_t, *pMpi2IOUnitPage5_t; @@ -1127,6 +1122,7 @@ typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_7 { #define MPI2_IOUNITPAGE7_PCIE_SPEED_5_0_GBPS (0x01) #define MPI2_IOUNITPAGE7_PCIE_SPEED_8_0_GBPS (0x02) #define MPI2_IOUNITPAGE7_PCIE_SPEED_16_0_GBPS (0x03) +#define MPI2_IOUNITPAGE7_PCIE_SPEED_32_0_GBPS (0x04) /*defines for IO Unit Page 7 ProcessorState field */ #define MPI2_IOUNITPAGE7_PSTATE_MASK_SECOND (0x0000000F) @@ -1206,12 +1202,9 @@ typedef struct _MPI2_IOUNIT8_SENSOR { #define MPI2_IOUNIT8_SENSOR_FLAGS_T0_ENABLE (0x0001) /* - *Host code (drivers, BIOS, utilities, etc.) should leave this define set to - *one and check the value returned for NumSensors at runtime. + *Host code (drivers, BIOS, utilities, etc.) should check the value returned + *for NumSensors at runtime before using Sensor[]. */ -#ifndef MPI2_IOUNITPAGE8_SENSOR_ENTRIES -#define MPI2_IOUNITPAGE8_SENSOR_ENTRIES (1) -#endif typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_8 { MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ @@ -1220,8 +1213,7 @@ typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_8 { U8 NumSensors; /*0x0C */ U8 PollingInterval; /*0x0D */ U16 Reserved3; /*0x0E */ - MPI2_IOUNIT8_SENSOR - Sensor[MPI2_IOUNITPAGE8_SENSOR_ENTRIES];/*0x10 */ + MPI2_IOUNIT8_SENSOR Sensor[]; /*0x10 */ } MPI2_CONFIG_PAGE_IO_UNIT_8, *PTR_MPI2_CONFIG_PAGE_IO_UNIT_8, Mpi2IOUnitPage8_t, *pMpi2IOUnitPage8_t; @@ -1246,12 +1238,9 @@ typedef struct _MPI2_IOUNIT9_SENSOR { #define MPI2_IOUNIT9_SENSOR_FLAGS_TEMP_VALID (0x01) /* - *Host code (drivers, BIOS, utilities, etc.) should leave this define set to - *one and check the value returned for NumSensors at runtime. + *Host code (drivers, BIOS, utilities, etc.) should check the value returned + *for NumSensors at runtime before using Sensor[]. */ -#ifndef MPI2_IOUNITPAGE9_SENSOR_ENTRIES -#define MPI2_IOUNITPAGE9_SENSOR_ENTRIES (1) -#endif typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_9 { MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ @@ -1260,8 +1249,7 @@ typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_9 { U8 NumSensors; /*0x0C */ U8 Reserved4; /*0x0D */ U16 Reserved3; /*0x0E */ - MPI2_IOUNIT9_SENSOR - Sensor[MPI2_IOUNITPAGE9_SENSOR_ENTRIES];/*0x10 */ + MPI2_IOUNIT9_SENSOR Sensor[]; /*0x10 */ } MPI2_CONFIG_PAGE_IO_UNIT_9, *PTR_MPI2_CONFIG_PAGE_IO_UNIT_9, Mpi2IOUnitPage9_t, *pMpi2IOUnitPage9_t; @@ -1281,12 +1269,9 @@ typedef struct _MPI2_IOUNIT10_FUNCTION { *pMpi2IOUnit10Function_t; /* - *Host code (drivers, BIOS, utilities, etc.) should leave this define set to - *one and check the value returned for NumFunctions at runtime. + *Host code (drivers, BIOS, utilities, etc.) should check the value returned + *for NumFunctions at runtime before using Function[]. */ -#ifndef MPI2_IOUNITPAGE10_FUNCTION_ENTRIES -#define MPI2_IOUNITPAGE10_FUNCTION_ENTRIES (1) -#endif typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_10 { MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ @@ -1295,8 +1280,7 @@ typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_10 { U16 Reserved2; /*0x06 */ U32 Reserved3; /*0x08 */ U32 Reserved4; /*0x0C */ - MPI2_IOUNIT10_FUNCTION - Function[MPI2_IOUNITPAGE10_FUNCTION_ENTRIES];/*0x10 */ + MPI2_IOUNIT10_FUNCTION Function[]; /*0x10 */ } MPI2_CONFIG_PAGE_IO_UNIT_10, *PTR_MPI2_CONFIG_PAGE_IO_UNIT_10, Mpi2IOUnitPage10_t, *pMpi2IOUnitPage10_t; @@ -1397,7 +1381,7 @@ typedef struct _MPI2_CONFIG_PAGE_IOC_1 { U8 PCIBusNum; /*0x0E */ U8 PCIDomainSegment; /*0x0F */ U32 Reserved1; /*0x10 */ - U32 Reserved2; /*0x14 */ + U32 ProductSpecific; /* 0x14 */ } MPI2_CONFIG_PAGE_IOC_1, *PTR_MPI2_CONFIG_PAGE_IOC_1, Mpi2IOCPage1_t, *pMpi2IOCPage1_t; @@ -1751,12 +1735,9 @@ typedef struct _MPI2_CONFIG_PAGE_BIOS_3 { /*BIOS Page 4 */ /* - *Host code (drivers, BIOS, utilities, etc.) should leave this define set to - *one and check the value returned for NumPhys at runtime. + *Host code (drivers, BIOS, utilities, etc.) should check the value returned + *for NumPhys at runtime before using Phy[]. */ -#ifndef MPI2_BIOS_PAGE_4_PHY_ENTRIES -#define MPI2_BIOS_PAGE_4_PHY_ENTRIES (1) -#endif typedef struct _MPI2_BIOS4_ENTRY { U64 ReassignmentWWID; /*0x00 */ @@ -1769,8 +1750,7 @@ typedef struct _MPI2_CONFIG_PAGE_BIOS_4 { U8 NumPhys; /*0x04 */ U8 Reserved1; /*0x05 */ U16 Reserved2; /*0x06 */ - MPI2_BIOS4_ENTRY - Phy[MPI2_BIOS_PAGE_4_PHY_ENTRIES]; /*0x08 */ + MPI2_BIOS4_ENTRY Phy[]; /*0x08 */ } MPI2_CONFIG_PAGE_BIOS_4, *PTR_MPI2_CONFIG_PAGE_BIOS_4, Mpi2BiosPage4_t, *pMpi2BiosPage4_t; @@ -1823,12 +1803,9 @@ typedef struct _MPI2_RAIDVOL0_SETTINGS { #define MPI2_RAIDVOL0_SETTING_ENABLE_WRITE_CACHING (0x0002) /* - *Host code (drivers, BIOS, utilities, etc.) should leave this define set to - *one and check the value returned for NumPhysDisks at runtime. + *Host code (drivers, BIOS, utilities, etc.) should check the value returned + *for NumPhysDisks at runtime before using PhysDisk[]. */ -#ifndef MPI2_RAID_VOL_PAGE_0_PHYSDISK_MAX -#define MPI2_RAID_VOL_PAGE_0_PHYSDISK_MAX (1) -#endif typedef struct _MPI2_CONFIG_PAGE_RAID_VOL_0 { MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ @@ -1848,8 +1825,7 @@ typedef struct _MPI2_CONFIG_PAGE_RAID_VOL_0 { U8 Reserved2; /*0x25 */ U8 Reserved3; /*0x26 */ U8 InactiveStatus; /*0x27 */ - MPI2_RAIDVOL0_PHYS_DISK - PhysDisk[MPI2_RAID_VOL_PAGE_0_PHYSDISK_MAX]; /*0x28 */ + MPI2_RAIDVOL0_PHYS_DISK PhysDisk[]; /*0x28 */ } MPI2_CONFIG_PAGE_RAID_VOL_0, *PTR_MPI2_CONFIG_PAGE_RAID_VOL_0, Mpi2RaidVolPage0_t, *pMpi2RaidVolPage0_t; @@ -2032,12 +2008,9 @@ typedef struct _MPI2_CONFIG_PAGE_RD_PDISK_0 { /*RAID Physical Disk Page 1 */ /* - *Host code (drivers, BIOS, utilities, etc.) should leave this define set to - *one and check the value returned for NumPhysDiskPaths at runtime. + *Host code (drivers, BIOS, utilities, etc.) should check the value returned + *for NumPhysDiskPaths at runtime before using PhysicalDiskPath[]. */ -#ifndef MPI2_RAID_PHYS_DISK1_PATH_MAX -#define MPI2_RAID_PHYS_DISK1_PATH_MAX (1) -#endif typedef struct _MPI2_RAIDPHYSDISK1_PATH { U16 DevHandle; /*0x00 */ @@ -2062,8 +2035,7 @@ typedef struct _MPI2_CONFIG_PAGE_RD_PDISK_1 { U8 PhysDiskNum; /*0x05 */ U16 Reserved1; /*0x06 */ U32 Reserved2; /*0x08 */ - MPI2_RAIDPHYSDISK1_PATH - PhysicalDiskPath[MPI2_RAID_PHYS_DISK1_PATH_MAX];/*0x0C */ + MPI2_RAIDPHYSDISK1_PATH PhysicalDiskPath[]; /*0x0C */ } MPI2_CONFIG_PAGE_RD_PDISK_1, *PTR_MPI2_CONFIG_PAGE_RD_PDISK_1, Mpi2RaidPhysDiskPage1_t, @@ -2208,12 +2180,9 @@ typedef struct _MPI2_SAS_IO_UNIT0_PHY_DATA { *pMpi2SasIOUnit0PhyData_t; /* - *Host code (drivers, BIOS, utilities, etc.) should leave this define set to - *one and check the value returned for NumPhys at runtime. + *Host code (drivers, BIOS, utilities, etc.) should check the value returned + *for NumPhys at runtime before using PhyData[]. */ -#ifndef MPI2_SAS_IOUNIT0_PHY_MAX -#define MPI2_SAS_IOUNIT0_PHY_MAX (1) -#endif typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_0 { MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */ @@ -2221,8 +2190,7 @@ typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_0 { U8 NumPhys; /*0x0C */ U8 Reserved2;/*0x0D */ U16 Reserved3;/*0x0E */ - MPI2_SAS_IO_UNIT0_PHY_DATA - PhyData[MPI2_SAS_IOUNIT0_PHY_MAX]; /*0x10 */ + MPI2_SAS_IO_UNIT0_PHY_DATA PhyData[];/*0x10 */ } MPI2_CONFIG_PAGE_SASIOUNIT_0, *PTR_MPI2_CONFIG_PAGE_SASIOUNIT_0, Mpi2SasIOUnitPage0_t, *pMpi2SasIOUnitPage0_t; @@ -2283,12 +2251,9 @@ typedef struct _MPI2_SAS_IO_UNIT1_PHY_DATA { *pMpi2SasIOUnit1PhyData_t; /* - *Host code (drivers, BIOS, utilities, etc.) should leave this define set to - *one and check the value returned for NumPhys at runtime. + *Host code (drivers, BIOS, utilities, etc.) should check the value returned + *for NumPhys at runtime before using PhyData[]. */ -#ifndef MPI2_SAS_IOUNIT1_PHY_MAX -#define MPI2_SAS_IOUNIT1_PHY_MAX (1) -#endif typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_1 { MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */ @@ -2309,7 +2274,7 @@ typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_1 { U8 IODeviceMissingDelay; /*0x13 */ MPI2_SAS_IO_UNIT1_PHY_DATA - PhyData[MPI2_SAS_IOUNIT1_PHY_MAX]; /*0x14 */ + PhyData[]; /*0x14 */ } MPI2_CONFIG_PAGE_SASIOUNIT_1, *PTR_MPI2_CONFIG_PAGE_SASIOUNIT_1, Mpi2SasIOUnitPage1_t, *pMpi2SasIOUnitPage1_t; @@ -2338,6 +2303,7 @@ typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_1 { #define MPI2_SASIOUNIT1_CONTROL_CLEAR_AFFILIATION (0x0001) /*values for SAS IO Unit Page 1 AdditionalControlFlags */ +#define MPI2_SASIOUNIT1_ACONTROL_PROD_SPECIFIC_1 (0x8000) #define MPI2_SASIOUNIT1_ACONTROL_DA_PERSIST_CONNECT (0x0100) #define MPI2_SASIOUNIT1_ACONTROL_MULTI_PORT_DOMAIN_ILLEGAL (0x0080) #define MPI2_SASIOUNIT1_ACONTROL_SATA_ASYNCHROUNOUS_NOTIFICATION (0x0040) @@ -2489,12 +2455,9 @@ typedef struct _MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS { #define MPI2_SASIOUNIT5_ITE_ONE_MICROSECOND (0) /* - *Host code (drivers, BIOS, utilities, etc.) should leave this define set to - *one and check the value returned for NumPhys at runtime. + *Host code (drivers, BIOS, utilities, etc.) should check the value returned + *for NumPhys at runtime before using SASPhyPowerManagementSettings[]. */ -#ifndef MPI2_SAS_IOUNIT5_PHY_MAX -#define MPI2_SAS_IOUNIT5_PHY_MAX (1) -#endif typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_5 { MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */ @@ -2503,7 +2466,7 @@ typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_5 { U16 Reserved2;/*0x0A */ U32 Reserved3;/*0x0C */ MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS - SASPhyPowerManagementSettings[MPI2_SAS_IOUNIT5_PHY_MAX];/*0x10 */ + SASPhyPowerManagementSettings[]; /*0x10 */ } MPI2_CONFIG_PAGE_SASIOUNIT_5, *PTR_MPI2_CONFIG_PAGE_SASIOUNIT_5, Mpi2SasIOUnitPage5_t, *pMpi2SasIOUnitPage5_t; @@ -2541,12 +2504,9 @@ typedef struct _MPI2_SAS_IO_UNIT6_PORT_WIDTH_MOD_GROUP_STATUS { #define MPI2_SASIOUNIT6_MODULATION_100_PERCENT (0x03) /* - *Host code (drivers, BIOS, utilities, etc.) should leave this define set to - *one and check the value returned for NumGroups at runtime. + *Host code (drivers, BIOS, utilities, etc.) should check the value returned + *for NumGroups at runtime before using PortWidthModulationGroupStatus[]. */ -#ifndef MPI2_SAS_IOUNIT6_GROUP_MAX -#define MPI2_SAS_IOUNIT6_GROUP_MAX (1) -#endif typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_6 { MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */ @@ -2556,7 +2516,7 @@ typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_6 { U8 Reserved3; /*0x11 */ U16 Reserved4; /*0x12 */ MPI2_SAS_IO_UNIT6_PORT_WIDTH_MOD_GROUP_STATUS - PortWidthModulationGroupStatus[MPI2_SAS_IOUNIT6_GROUP_MAX]; /*0x14 */ + PortWidthModulationGroupStatus[]; /*0x14 */ } MPI2_CONFIG_PAGE_SASIOUNIT_6, *PTR_MPI2_CONFIG_PAGE_SASIOUNIT_6, Mpi2SasIOUnitPage6_t, *pMpi2SasIOUnitPage6_t; @@ -2584,12 +2544,9 @@ typedef struct _MPI2_SAS_IO_UNIT7_PORT_WIDTH_MOD_GROUP_SETTINGS { /* - *Host code (drivers, BIOS, utilities, etc.) should leave this define set to - *one and check the value returned for NumGroups at runtime. + *Host code (drivers, BIOS, utilities, etc.) should check the value returned + *for NumGroups at runtime before using PortWidthModulationGroupSettings[]. */ -#ifndef MPI2_SAS_IOUNIT7_GROUP_MAX -#define MPI2_SAS_IOUNIT7_GROUP_MAX (1) -#endif typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_7 { MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */ @@ -2602,7 +2559,7 @@ typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_7 { U8 Reserved4; /*0x15 */ U16 Reserved5; /*0x16 */ MPI2_SAS_IO_UNIT7_PORT_WIDTH_MOD_GROUP_SETTINGS - PortWidthModulationGroupSettings[MPI2_SAS_IOUNIT7_GROUP_MAX];/*0x18 */ + PortWidthModulationGroupSettings[]; /*0x18 */ } MPI2_CONFIG_PAGE_SASIOUNIT_7, *PTR_MPI2_CONFIG_PAGE_SASIOUNIT_7, Mpi2SasIOUnitPage7_t, *pMpi2SasIOUnitPage7_t; @@ -3073,12 +3030,9 @@ typedef struct _MPI2_SASPHY2_PHY_EVENT { /* - *Host code (drivers, BIOS, utilities, etc.) should leave this define set to - *one and check the value returned for NumPhyEvents at runtime. + *Host code (drivers, BIOS, utilities, etc.) should check the value returned + *for NumPhyEvents at runtime before using PhyEvent[]. */ -#ifndef MPI2_SASPHY2_PHY_EVENT_MAX -#define MPI2_SASPHY2_PHY_EVENT_MAX (1) -#endif typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_2 { MPI2_CONFIG_EXTENDED_PAGE_HEADER @@ -3092,7 +3046,7 @@ typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_2 { U16 Reserved3; /*0x0E */ MPI2_SASPHY2_PHY_EVENT - PhyEvent[MPI2_SASPHY2_PHY_EVENT_MAX]; /*0x10 */ + PhyEvent[]; /*0x10 */ } MPI2_CONFIG_PAGE_SAS_PHY_2, *PTR_MPI2_CONFIG_PAGE_SAS_PHY_2, Mpi2SasPhyPage2_t, @@ -3187,12 +3141,9 @@ typedef struct _MPI2_SASPHY3_PHY_EVENT_CONFIG { #define MPI2_SASPHY3_TFLAGS_EVENT_NOTIFY (0x0001) /* - *Host code (drivers, BIOS, utilities, etc.) should leave this define set to - *one and check the value returned for NumPhyEvents at runtime. + *Host code (drivers, BIOS, utilities, etc.) should check the value returned + *for NumPhyEvents at runtime before using PhyEventConfig[]. */ -#ifndef MPI2_SASPHY3_PHY_EVENT_MAX -#define MPI2_SASPHY3_PHY_EVENT_MAX (1) -#endif typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_3 { MPI2_CONFIG_EXTENDED_PAGE_HEADER @@ -3206,7 +3157,7 @@ typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_3 { U16 Reserved3; /*0x0E */ MPI2_SASPHY3_PHY_EVENT_CONFIG - PhyEventConfig[MPI2_SASPHY3_PHY_EVENT_MAX]; /*0x10 */ + PhyEventConfig[]; /*0x10 */ } MPI2_CONFIG_PAGE_SAS_PHY_3, *PTR_MPI2_CONFIG_PAGE_SAS_PHY_3, Mpi2SasPhyPage3_t, *pMpi2SasPhyPage3_t; @@ -3345,12 +3296,9 @@ typedef struct _MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0 { /*Log Page 0 */ /* - *Host code (drivers, BIOS, utilities, etc.) should leave this define set to - *one and check the value returned for NumLogEntries at runtime. + *Host code (drivers, BIOS, utilities, etc.) should check the value returned + *for NumLogEntries at runtime before using LogEntry[]. */ -#ifndef MPI2_LOG_0_NUM_LOG_ENTRIES -#define MPI2_LOG_0_NUM_LOG_ENTRIES (1) -#endif #define MPI2_LOG_0_LOG_DATA_LENGTH (0x1C) @@ -3380,8 +3328,7 @@ typedef struct _MPI2_CONFIG_PAGE_LOG_0 { U32 Reserved2; /*0x0C */ U16 NumLogEntries;/*0x10 */ U16 Reserved3; /*0x12 */ - MPI2_LOG_0_ENTRY - LogEntry[MPI2_LOG_0_NUM_LOG_ENTRIES]; /*0x14 */ + MPI2_LOG_0_ENTRY LogEntry[]; /*0x14 */ } MPI2_CONFIG_PAGE_LOG_0, *PTR_MPI2_CONFIG_PAGE_LOG_0, Mpi2LogPage0_t, *pMpi2LogPage0_t; @@ -3395,12 +3342,9 @@ typedef struct _MPI2_CONFIG_PAGE_LOG_0 { /*RAID Page 0 */ /* - *Host code (drivers, BIOS, utilities, etc.) should leave this define set to - *one and check the value returned for NumElements at runtime. + *Host code (drivers, BIOS, utilities, etc.) should check the value returned + *for NumElements at runtime before using ConfigElement[]. */ -#ifndef MPI2_RAIDCONFIG0_MAX_ELEMENTS -#define MPI2_RAIDCONFIG0_MAX_ELEMENTS (1) -#endif typedef struct _MPI2_RAIDCONFIG0_CONFIG_ELEMENT { U16 ElementFlags; /*0x00 */ @@ -3433,8 +3377,7 @@ typedef struct _MPI2_CONFIG_PAGE_RAID_CONFIGURATION_0 { U8 NumElements; /*0x2C */ U8 Reserved2; /*0x2D */ U16 Reserved3; /*0x2E */ - MPI2_RAIDCONFIG0_CONFIG_ELEMENT - ConfigElement[MPI2_RAIDCONFIG0_MAX_ELEMENTS]; /*0x30 */ + MPI2_RAIDCONFIG0_CONFIG_ELEMENT ConfigElement[];/*0x30 */ } MPI2_CONFIG_PAGE_RAID_CONFIGURATION_0, *PTR_MPI2_CONFIG_PAGE_RAID_CONFIGURATION_0, Mpi2RaidConfigurationPage0_t, @@ -3651,6 +3594,7 @@ typedef struct _MPI2_CONFIG_PAGE_EXT_MAN_PS { #define MPI26_PCIE_NEG_LINK_RATE_5_0 (0x03) #define MPI26_PCIE_NEG_LINK_RATE_8_0 (0x04) #define MPI26_PCIE_NEG_LINK_RATE_16_0 (0x05) +#define MPI26_PCIE_NEG_LINK_RATE_32_0 (0x06) /**************************************************************************** @@ -3674,12 +3618,9 @@ typedef struct _MPI26_PCIE_IO_UNIT0_PHY_DATA { Mpi26PCIeIOUnit0PhyData_t, *pMpi26PCIeIOUnit0PhyData_t; /* - *Host code (drivers, BIOS, utilities, etc.) should leave this define set to - *one and check the value returned for NumPhys at runtime. + *Host code (drivers, BIOS, utilities, etc.) should check the value returned + *for NumPhys at runtime before using PhyData[]. */ -#ifndef MPI26_PCIE_IOUNIT0_PHY_MAX -#define MPI26_PCIE_IOUNIT0_PHY_MAX (1) -#endif typedef struct _MPI26_CONFIG_PAGE_PIOUNIT_0 { MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */ @@ -3688,7 +3629,7 @@ typedef struct _MPI26_CONFIG_PAGE_PIOUNIT_0 { U8 InitStatus; /*0x0D */ U16 Reserved3; /*0x0E */ MPI26_PCIE_IO_UNIT0_PHY_DATA - PhyData[MPI26_PCIE_IOUNIT0_PHY_MAX]; /*0x10 */ + PhyData[]; /*0x10 */ } MPI26_CONFIG_PAGE_PIOUNIT_0, *PTR_MPI26_CONFIG_PAGE_PIOUNIT_0, Mpi26PCIeIOUnitPage0_t, *pMpi26PCIeIOUnitPage0_t; @@ -3731,12 +3672,9 @@ typedef struct _MPI26_PCIE_IO_UNIT1_PHY_DATA { #define MPI26_PCIEIOUNIT1_LINKFLAGS_SRNS_EN (0x02) /* - *Host code (drivers, BIOS, utilities, etc.) should leave this define set to - *one and check the value returned for NumPhys at runtime. + *Host code (drivers, BIOS, utilities, etc.) should check the value returned + *for NumPhys at runtime before using PhyData[]. */ -#ifndef MPI26_PCIE_IOUNIT1_PHY_MAX -#define MPI26_PCIE_IOUNIT1_PHY_MAX (1) -#endif typedef struct _MPI26_CONFIG_PAGE_PIOUNIT_1 { MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */ @@ -3748,7 +3686,7 @@ typedef struct _MPI26_CONFIG_PAGE_PIOUNIT_1 { U8 DMDReportPCIe; /*0x11 */ U16 Reserved2; /*0x12 */ MPI26_PCIE_IO_UNIT1_PHY_DATA - PhyData[MPI26_PCIE_IOUNIT1_PHY_MAX];/*0x14 */ + PhyData[]; /*0x14 */ } MPI26_CONFIG_PAGE_PIOUNIT_1, *PTR_MPI26_CONFIG_PAGE_PIOUNIT_1, Mpi26PCIeIOUnitPage1_t, *pMpi26PCIeIOUnitPage1_t; @@ -3766,6 +3704,7 @@ typedef struct _MPI26_CONFIG_PAGE_PIOUNIT_1 { #define MPI26_PCIEIOUNIT1_MAX_RATE_5_0 (0x30) #define MPI26_PCIEIOUNIT1_MAX_RATE_8_0 (0x40) #define MPI26_PCIEIOUNIT1_MAX_RATE_16_0 (0x50) +#define MPI26_PCIEIOUNIT1_MAX_RATE_32_0 (0x60) /*values for PCIe IO Unit Page 1 DMDReportPCIe */ #define MPI26_PCIEIOUNIT1_DMDRPT_UNIT_MASK (0x80) @@ -3924,7 +3863,13 @@ typedef struct _MPI26_CONFIG_PAGE_PCIEDEV_2 { U32 MaximumDataTransferSize; /*0x0C */ U32 Capabilities; /*0x10 */ U16 NOIOB; /* 0x14 */ - U16 Reserved2; /* 0x16 */ + U16 ShutdownLatency; /* 0x16 */ + U16 VendorID; /* 0x18 */ + U16 DeviceID; /* 0x1A */ + U16 SubsystemVendorID; /* 0x1C */ + U16 SubsystemID; /* 0x1E */ + U8 RevisionID; /* 0x20 */ + U8 Reserved21[3]; /* 0x21 */ } MPI26_CONFIG_PAGE_PCIEDEV_2, *PTR_MPI26_CONFIG_PAGE_PCIEDEV_2, Mpi26PCIeDevicePage2_t, *pMpi26PCIeDevicePage2_t; @@ -3974,12 +3919,9 @@ typedef struct _MPI26_PCIELINK2_LINK_EVENT { /* - *Host code (drivers, BIOS, utilities, etc.) should leave this define set to - *one and check the value returned for NumLinkEvents at runtime. + *Host code (drivers, BIOS, utilities, etc.) should check the value returned + *for NumLinkEvents at runtime before using LinkEvent[]. */ -#ifndef MPI26_PCIELINK2_LINK_EVENT_MAX -#define MPI26_PCIELINK2_LINK_EVENT_MAX (1) -#endif typedef struct _MPI26_CONFIG_PAGE_PCIELINK_2 { MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */ @@ -3990,7 +3932,7 @@ typedef struct _MPI26_CONFIG_PAGE_PCIELINK_2 { U8 Reserved3; /*0x0D */ U16 Reserved4; /*0x0E */ MPI26_PCIELINK2_LINK_EVENT - LinkEvent[MPI26_PCIELINK2_LINK_EVENT_MAX]; /*0x10 */ + LinkEvent[]; /*0x10 */ } MPI26_CONFIG_PAGE_PCIELINK_2, *PTR_MPI26_CONFIG_PAGE_PCIELINK_2, Mpi26PcieLinkPage2_t, *pMpi26PcieLinkPage2_t; @@ -4048,12 +3990,9 @@ typedef struct _MPI26_PCIELINK3_LINK_EVENT_CONFIG { #define MPI26_PCIELINK3_TFLAGS_EVENT_NOTIFY (0x0001) /* - *Host code (drivers, BIOS, utilities, etc.) should leave this define set to - *one and check the value returned for NumLinkEvents at runtime. + *Host code (drivers, BIOS, utilities, etc.) should check the value returned + *for NumLinkEvents at runtime before using LinkEventConfig[]. */ -#ifndef MPI26_PCIELINK3_LINK_EVENT_MAX -#define MPI26_PCIELINK3_LINK_EVENT_MAX (1) -#endif typedef struct _MPI26_CONFIG_PAGE_PCIELINK_3 { MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */ @@ -4064,7 +4003,7 @@ typedef struct _MPI26_CONFIG_PAGE_PCIELINK_3 { U8 Reserved3; /*0x0D */ U16 Reserved4; /*0x0E */ MPI26_PCIELINK3_LINK_EVENT_CONFIG - LinkEventConfig[MPI26_PCIELINK3_LINK_EVENT_MAX]; /*0x10 */ + LinkEventConfig[]; /*0x10 */ } MPI26_CONFIG_PAGE_PCIELINK_3, *PTR_MPI26_CONFIG_PAGE_PCIELINK_3, Mpi26PcieLinkPage3_t, *pMpi26PcieLinkPage3_t; diff --git a/drivers/scsi/mpt3sas/mpi/mpi2_image.h b/drivers/scsi/mpt3sas/mpi/mpi2_image.h index 4959585f029d..798ab6e33eb9 100644 --- a/drivers/scsi/mpt3sas/mpi/mpi2_image.h +++ b/drivers/scsi/mpt3sas/mpi/mpi2_image.h @@ -5,7 +5,7 @@ * Name: mpi2_image.h * Description: Contains definitions for firmware and other component images * Creation Date: 04/02/2018 - * Version: 02.06.03 + * Version: 02.06.04 * * * Version History @@ -17,6 +17,12 @@ * 08-14-18 02.06.01 Corrected define for MPI26_IMAGE_HEADER_SIGNATURE0_MPI26 * 08-28-18 02.06.02 Added MPI2_EXT_IMAGE_TYPE_RDE * 09-07-18 02.06.03 Added MPI26_EVENT_PCIE_TOPO_PI_16_LANES + * 12-17-18 02.06.04 Addd MPI2_EXT_IMAGE_TYPE_PBLP + * Shorten some defines to be compatible with DOS + * 06-24-19 02.06.05 Whitespace adjustments to help with identifier + * checking tool. + * 10-02-19 02.06.06 Added MPI26_IMAGE_HEADER_SIG1_COREDUMP + * Added MPI2_FLASH_REGION_COREDUMP */ #ifndef MPI2_IMAGE_H #define MPI2_IMAGE_H @@ -200,17 +206,19 @@ typedef struct _MPI26_COMPONENT_IMAGE_HEADER { #define MPI26_IMAGE_HEADER_SIGNATURE0_MPI26 (0xEB000042) /**** Definitions for Signature1 field ****/ -#define MPI26_IMAGE_HEADER_SIGNATURE1_APPLICATION (0x20505041) -#define MPI26_IMAGE_HEADER_SIGNATURE1_CBB (0x20424243) -#define MPI26_IMAGE_HEADER_SIGNATURE1_MFG (0x2047464D) -#define MPI26_IMAGE_HEADER_SIGNATURE1_BIOS (0x534F4942) -#define MPI26_IMAGE_HEADER_SIGNATURE1_HIIM (0x4D494948) -#define MPI26_IMAGE_HEADER_SIGNATURE1_HIIA (0x41494948) -#define MPI26_IMAGE_HEADER_SIGNATURE1_CPLD (0x444C5043) -#define MPI26_IMAGE_HEADER_SIGNATURE1_SPD (0x20445053) -#define MPI26_IMAGE_HEADER_SIGNATURE1_NVDATA (0x5444564E) -#define MPI26_IMAGE_HEADER_SIGNATURE1_GAS_GAUGE (0x20534147) -#define MPI26_IMAGE_HEADER_SIGNATURE1_PBLP (0x50424C50) +#define MPI26_IMAGE_HEADER_SIG1_APPLICATION (0x20505041) +#define MPI26_IMAGE_HEADER_SIG1_CBB (0x20424243) +#define MPI26_IMAGE_HEADER_SIG1_MFG (0x2047464D) +#define MPI26_IMAGE_HEADER_SIG1_BIOS (0x534F4942) +#define MPI26_IMAGE_HEADER_SIG1_HIIM (0x4D494948) +#define MPI26_IMAGE_HEADER_SIG1_HIIA (0x41494948) +#define MPI26_IMAGE_HEADER_SIG1_CPLD (0x444C5043) +#define MPI26_IMAGE_HEADER_SIG1_SPD (0x20445053) +#define MPI26_IMAGE_HEADER_SIG1_NVDATA (0x5444564E) +#define MPI26_IMAGE_HEADER_SIG1_GAS_GAUGE (0x20534147) +#define MPI26_IMAGE_HEADER_SIG1_PBLP (0x504C4250) +/* little-endian "DUMP" */ +#define MPI26_IMAGE_HEADER_SIG1_COREDUMP (0x504D5544) /**** Definitions for Signature2 field ****/ #define MPI26_IMAGE_HEADER_SIGNATURE2_VALUE (0x50584546) @@ -278,6 +286,7 @@ typedef struct _MPI2_EXT_IMAGE_HEADER { #define MPI2_EXT_IMAGE_TYPE_MEGARAID (0x08) #define MPI2_EXT_IMAGE_TYPE_ENCRYPTED_HASH (0x09) #define MPI2_EXT_IMAGE_TYPE_RDE (0x0A) +#define MPI2_EXT_IMAGE_TYPE_PBLP (0x0B) #define MPI2_EXT_IMAGE_TYPE_MIN_PRODUCT_SPECIFIC (0x80) #define MPI2_EXT_IMAGE_TYPE_MAX_PRODUCT_SPECIFIC (0xFF) @@ -286,20 +295,9 @@ typedef struct _MPI2_EXT_IMAGE_HEADER { /*FLASH Layout Extended Image Data */ /* - *Host code (drivers, BIOS, utilities, etc.) should leave this define set to - *one and check RegionsPerLayout at runtime. + *Host code (drivers, BIOS, utilities, etc.) should check NumberOfLayouts and + *RegionsPerLayout at runtime before using Layout[] and Region[]. */ -#ifndef MPI2_FLASH_NUMBER_OF_REGIONS -#define MPI2_FLASH_NUMBER_OF_REGIONS (1) -#endif - -/* - *Host code (drivers, BIOS, utilities, etc.) should leave this define set to - *one and check NumberOfLayouts at runtime. - */ -#ifndef MPI2_FLASH_NUMBER_OF_LAYOUTS -#define MPI2_FLASH_NUMBER_OF_LAYOUTS (1) -#endif typedef struct _MPI2_FLASH_REGION { U8 RegionType; /*0x00 */ @@ -316,7 +314,7 @@ typedef struct _MPI2_FLASH_LAYOUT { U32 Reserved1; /*0x04 */ U32 Reserved2; /*0x08 */ U32 Reserved3; /*0x0C */ - MPI2_FLASH_REGION Region[MPI2_FLASH_NUMBER_OF_REGIONS]; /*0x10 */ + MPI2_FLASH_REGION Region[]; /*0x10 */ } MPI2_FLASH_LAYOUT, *PTR_MPI2_FLASH_LAYOUT, Mpi2FlashLayout_t, *pMpi2FlashLayout_t; @@ -330,7 +328,7 @@ typedef struct _MPI2_FLASH_LAYOUT_DATA { U16 MinimumSectorAlignment; /*0x08 */ U16 Reserved3; /*0x0A */ U32 Reserved4; /*0x0C */ - MPI2_FLASH_LAYOUT Layout[MPI2_FLASH_NUMBER_OF_LAYOUTS]; /*0x10 */ + MPI2_FLASH_LAYOUT Layout[]; /*0x10 */ } MPI2_FLASH_LAYOUT_DATA, *PTR_MPI2_FLASH_LAYOUT_DATA, Mpi2FlashLayoutData_t, *pMpi2FlashLayoutData_t; @@ -356,6 +354,7 @@ typedef struct _MPI2_FLASH_LAYOUT_DATA { #define MPI2_FLASH_REGION_MR_NVDATA (0x14) #define MPI2_FLASH_REGION_CPLD (0x15) #define MPI2_FLASH_REGION_PSOC (0x16) +#define MPI2_FLASH_REGION_COREDUMP (0x17) /*ImageRevision */ #define MPI2_FLASH_LAYOUT_IMAGE_REVISION (0x00) @@ -363,12 +362,9 @@ typedef struct _MPI2_FLASH_LAYOUT_DATA { /*Supported Devices Extended Image Data */ /* - *Host code (drivers, BIOS, utilities, etc.) should leave this define set to - *one and check NumberOfDevices at runtime. + *Host code (drivers, BIOS, utilities, etc.) should check NumberOfDevices at + *runtime before using SupportedDevice[]. */ -#ifndef MPI2_SUPPORTED_DEVICES_IMAGE_NUM_DEVICES -#define MPI2_SUPPORTED_DEVICES_IMAGE_NUM_DEVICES (1) -#endif typedef struct _MPI2_SUPPORTED_DEVICE { U16 DeviceID; /*0x00 */ @@ -389,7 +385,7 @@ typedef struct _MPI2_SUPPORTED_DEVICES_DATA { U8 Reserved2; /*0x03 */ U32 Reserved3; /*0x04 */ MPI2_SUPPORTED_DEVICE - SupportedDevice[MPI2_SUPPORTED_DEVICES_IMAGE_NUM_DEVICES];/*0x08 */ + SupportedDevice[]; /*0x08 */ } MPI2_SUPPORTED_DEVICES_DATA, *PTR_MPI2_SUPPORTED_DEVICES_DATA, Mpi2SupportedDevicesData_t, *pMpi2SupportedDevicesData_t; @@ -454,7 +450,7 @@ typedef struct _MPI25_ENCRYPTED_HASH_ENTRY { U8 EncryptionAlgorithm; /*0x02 */ U8 Reserved1; /*0x03 */ U32 Reserved2; /*0x04 */ - U32 EncryptedHash[1]; /*0x08 */ /* variable length */ + U32 EncryptedHash[]; /*0x08 */ } MPI25_ENCRYPTED_HASH_ENTRY, *PTR_MPI25_ENCRYPTED_HASH_ENTRY, Mpi25EncryptedHashEntry_t, *pMpi25EncryptedHashEntry_t; @@ -472,12 +468,12 @@ Mpi25EncryptedHashEntry_t, *pMpi25EncryptedHashEntry_t; #define MPI25_HASH_ALGORITHM_UNUSED (0x00) #define MPI25_HASH_ALGORITHM_SHA256 (0x01) -#define MPI26_HASH_ALGORITHM_VERSION_MASK (0xE0) -#define MPI26_HASH_ALGORITHM_VERSION_NONE (0x00) -#define MPI26_HASH_ALGORITHM_VERSION_SHA1 (0x20) -#define MPI26_HASH_ALGORITHM_VERSION_SHA2 (0x40) -#define MPI26_HASH_ALGORITHM_VERSION_SHA3 (0x60) -#define MPI26_HASH_ALGORITHM_SIZE_MASK (0x1F) +#define MPI26_HASH_ALGORITHM_VER_MASK (0xE0) +#define MPI26_HASH_ALGORITHM_VER_NONE (0x00) +#define MPI26_HASH_ALGORITHM_VER_SHA1 (0x20) +#define MPI26_HASH_ALGORITHM_VER_SHA2 (0x40) +#define MPI26_HASH_ALGORITHM_VER_SHA3 (0x60) +#define MPI26_HASH_ALGORITHM_SIZE_MASK (0x1F) #define MPI26_HASH_ALGORITHM_SIZE_256 (0x01) #define MPI26_HASH_ALGORITHM_SIZE_512 (0x02) @@ -498,7 +494,7 @@ typedef struct _MPI25_ENCRYPTED_HASH_DATA { U8 NumHash; /*0x01 */ U16 Reserved1; /*0x02 */ U32 Reserved2; /*0x04 */ - MPI25_ENCRYPTED_HASH_ENTRY EncryptedHashEntry[1]; /*0x08 */ + MPI25_ENCRYPTED_HASH_ENTRY EncryptedHashEntry[]; /*0x08 */ } MPI25_ENCRYPTED_HASH_DATA, *PTR_MPI25_ENCRYPTED_HASH_DATA, Mpi25EncryptedHashData_t, *pMpi25EncryptedHashData_t; diff --git a/drivers/scsi/mpt3sas/mpi/mpi2_ioc.h b/drivers/scsi/mpt3sas/mpi/mpi2_ioc.h index 68ea408cd5c5..1a279c6e1a9f 100644 --- a/drivers/scsi/mpt3sas/mpi/mpi2_ioc.h +++ b/drivers/scsi/mpt3sas/mpi/mpi2_ioc.h @@ -175,6 +175,11 @@ * Moved FW image definitions ionto new mpi2_image,h * 08-14-18 02.00.36 Fixed definition of MPI2_FW_DOWNLOAD_ITYPE_PSOC (0x16) * 09-07-18 02.00.37 Added MPI26_EVENT_PCIE_TOPO_PI_16_LANES + * 10-02-19 02.00.38 Added MPI26_IOCINIT_CFGFLAGS_COREDUMP_ENABLE + * Added MPI26_IOCFACTS_CAPABILITY_COREDUMP_ENABLED + * Added MPI2_FW_DOWNLOAD_ITYPE_COREDUMP + * Added MPI2_FW_UPLOAD_ITYPE_COREDUMP + * 9-13-24 02.00.39 Added MPI26_MCTP_PASSTHROUGH messages * -------------------------------------------------------------------------- */ @@ -248,6 +253,7 @@ typedef struct _MPI2_IOC_INIT_REQUEST { /*ConfigurationFlags */ #define MPI26_IOCINIT_CFGFLAGS_NVME_SGL_FORMAT (0x0001) +#define MPI26_IOCINIT_CFGFLAGS_COREDUMP_ENABLE (0x0002) /*minimum depth for a Reply Descriptor Post Queue */ #define MPI2_RDPQ_DEPTH_MIN (16) @@ -377,6 +383,8 @@ typedef struct _MPI2_IOC_FACTS_REPLY { /*ProductID field uses MPI2_FW_HEADER_PID_ */ /*IOCCapabilities */ +#define MPI26_IOCFACTS_CAPABILITY_MCTP_PASSTHRU (0x00800000) +#define MPI26_IOCFACTS_CAPABILITY_COREDUMP_ENABLED (0x00200000) #define MPI26_IOCFACTS_CAPABILITY_PCIE_SRIOV (0x00100000) #define MPI26_IOCFACTS_CAPABILITY_ATOMIC_REQ (0x00080000) #define MPI2_IOCFACTS_CAPABILITY_RDPQ_ARRAY_CAPABLE (0x00040000) @@ -531,7 +539,7 @@ typedef struct _MPI2_EVENT_NOTIFICATION_REPLY { U16 Event; /*0x14 */ U16 Reserved4; /*0x16 */ U32 EventContext; /*0x18 */ - U32 EventData[1]; /*0x1C */ + U32 EventData[]; /*0x1C */ } MPI2_EVENT_NOTIFICATION_REPLY, *PTR_MPI2_EVENT_NOTIFICATION_REPLY, Mpi2EventNotificationReply_t, *pMpi2EventNotificationReply_t; @@ -633,7 +641,7 @@ typedef struct _MPI2_EVENT_DATA_HOST_MESSAGE { U8 Reserved1; /*0x01 */ U16 Reserved2; /*0x02 */ U32 Reserved3; /*0x04 */ - U32 HostData[1]; /*0x08 */ + U32 HostData[]; /*0x08 */ } MPI2_EVENT_DATA_HOST_MESSAGE, *PTR_MPI2_EVENT_DATA_HOST_MESSAGE, Mpi2EventDataHostMessage_t, *pMpi2EventDataHostMessage_t; @@ -802,12 +810,9 @@ typedef struct _MPI2_EVENT_DATA_IR_PHYSICAL_DISK { /*Integrated RAID Configuration Change List Event data */ /* - *Host code (drivers, BIOS, utilities, etc.) should leave this define set to - *one and check NumElements at runtime. + *Host code (drivers, BIOS, utilities, etc.) should check NumElements at + *runtime before using ConfigElement[]. */ -#ifndef MPI2_EVENT_IR_CONFIG_ELEMENT_COUNT -#define MPI2_EVENT_IR_CONFIG_ELEMENT_COUNT (1) -#endif typedef struct _MPI2_EVENT_IR_CONFIG_ELEMENT { U16 ElementFlags; /*0x00 */ @@ -842,7 +847,7 @@ typedef struct _MPI2_EVENT_DATA_IR_CONFIG_CHANGE_LIST { U8 ConfigNum; /*0x03 */ U32 Flags; /*0x04 */ MPI2_EVENT_IR_CONFIG_ELEMENT - ConfigElement[MPI2_EVENT_IR_CONFIG_ELEMENT_COUNT];/*0x08 */ + ConfigElement[];/*0x08 */ } MPI2_EVENT_DATA_IR_CONFIG_CHANGE_LIST, *PTR_MPI2_EVENT_DATA_IR_CONFIG_CHANGE_LIST, Mpi2EventDataIrConfigChangeList_t, @@ -963,12 +968,9 @@ typedef struct _MPI2_EVENT_DATA_SAS_INIT_TABLE_OVERFLOW { /*SAS Topology Change List Event data */ /* - *Host code (drivers, BIOS, utilities, etc.) should leave this define set to - *one and check NumEntries at runtime. + *Host code (drivers, BIOS, utilities, etc.) should check NumEntries at + *runtime before using PHY[]. */ -#ifndef MPI2_EVENT_SAS_TOPO_PHY_COUNT -#define MPI2_EVENT_SAS_TOPO_PHY_COUNT (1) -#endif typedef struct _MPI2_EVENT_SAS_TOPO_PHY_ENTRY { U16 AttachedDevHandle; /*0x00 */ @@ -988,7 +990,7 @@ typedef struct _MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST { U8 ExpStatus; /*0x0A */ U8 PhysicalPort; /*0x0B */ MPI2_EVENT_SAS_TOPO_PHY_ENTRY - PHY[MPI2_EVENT_SAS_TOPO_PHY_COUNT]; /*0x0C */ + PHY[]; /*0x0C */ } MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST, *PTR_MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST, Mpi2EventDataSasTopologyChangeList_t, @@ -1223,12 +1225,9 @@ typedef struct _MPI26_EVENT_DATA_PCIE_ENUMERATION { /*PCIe Topology Change List Event data (MPI v2.6 and later) */ /* - *Host code (drivers, BIOS, utilities, etc.) should leave this define set to - *one and check NumEntries at runtime. + *Host code (drivers, BIOS, utilities, etc.) should check NumEntries at + *runtime before using PortEntry[]. */ -#ifndef MPI26_EVENT_PCIE_TOPO_PORT_COUNT -#define MPI26_EVENT_PCIE_TOPO_PORT_COUNT (1) -#endif typedef struct _MPI26_EVENT_PCIE_TOPO_PORT_ENTRY { U16 AttachedDevHandle; /*0x00 */ @@ -1280,7 +1279,7 @@ typedef struct _MPI26_EVENT_DATA_PCIE_TOPOLOGY_CHANGE_LIST { U8 SwitchStatus; /*0x0A */ U8 PhysicalPort; /*0x0B */ MPI26_EVENT_PCIE_TOPO_PORT_ENTRY - PortEntry[MPI26_EVENT_PCIE_TOPO_PORT_COUNT]; /*0x0C */ + PortEntry[]; /*0x0C */ } MPI26_EVENT_DATA_PCIE_TOPOLOGY_CHANGE_LIST, *PTR_MPI26_EVENT_DATA_PCIE_TOPOLOGY_CHANGE_LIST, Mpi26EventDataPCIeTopologyChangeList_t, @@ -1391,7 +1390,7 @@ typedef struct _MPI2_SEND_HOST_MESSAGE_REQUEST { U32 Reserved8; /*0x18 */ U32 Reserved9; /*0x1C */ U32 Reserved10; /*0x20 */ - U32 HostData[1]; /*0x24 */ + U32 HostData[]; /*0x24 */ } MPI2_SEND_HOST_MESSAGE_REQUEST, *PTR_MPI2_SEND_HOST_MESSAGE_REQUEST, Mpi2SendHostMessageRequest_t, @@ -1458,8 +1457,8 @@ typedef struct _MPI2_FW_DOWNLOAD_REQUEST { /*MPI v2.6 and newer */ #define MPI2_FW_DOWNLOAD_ITYPE_CPLD (0x15) #define MPI2_FW_DOWNLOAD_ITYPE_PSOC (0x16) +#define MPI2_FW_DOWNLOAD_ITYPE_COREDUMP (0x17) #define MPI2_FW_DOWNLOAD_ITYPE_MIN_PRODUCT_SPECIFIC (0xF0) -#define MPI2_FW_DOWNLOAD_ITYPE_TERMINATE (0xFF) /*MPI v2.0 FWDownload TransactionContext Element */ typedef struct _MPI2_FW_DOWNLOAD_TCSGE { @@ -1801,5 +1800,57 @@ typedef struct _MPI26_IOUNIT_CONTROL_REPLY { Mpi26IoUnitControlReply_t, *pMpi26IoUnitControlReply_t; +/**************************************************************************** + * MCTP Passthrough messages (MPI v2.6 and later only.) + ****************************************************************************/ + +/* MCTP Passthrough Request Message */ +typedef struct _MPI26_MCTP_PASSTHROUGH_REQUEST { + U8 MsgContext; /* 0x00 */ + U8 Reserved1[2]; /* 0x01 */ + U8 Function; /* 0x03 */ + U8 Reserved2[3]; /* 0x04 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved3; /* 0x0A */ + U32 Reserved4; /* 0x0C */ + U8 Flags; /* 0x10 */ + U8 Reserved5[3]; /* 0x11 */ + U32 Reserved6; /* 0x14 */ + U32 H2DLength; /* 0x18 */ + U32 D2HLength; /* 0x1C */ + MPI25_SGE_IO_UNION H2DSGL; /* 0x20 */ + MPI25_SGE_IO_UNION D2HSGL; /* 0x30 */ +} MPI26_MCTP_PASSTHROUGH_REQUEST, + *PTR_MPI26_MCTP_PASSTHROUGH_REQUEST, + Mpi26MctpPassthroughRequest_t, + *pMpi26MctpPassthroughRequest_t; + +/* values for the MsgContext field */ +#define MPI26_MCTP_MSG_CONEXT_UNUSED (0x00) + +/* values for the Flags field */ +#define MPI26_MCTP_FLAGS_MSG_FORMAT_MPT (0x01) + +/* MCTP Passthrough Reply Message */ +typedef struct _MPI26_MCTP_PASSTHROUGH_REPLY { + U8 MsgContext; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U8 MsgLength; /* 0x02 */ + U8 Function; /* 0x03 */ + U8 Reserved2[3]; /* 0x04 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved3; /* 0x0A */ + U16 Reserved4; /* 0x0C */ + U16 IOCStatus; /* 0x0E */ + U32 IOCLogInfo; /* 0x10 */ + U32 ResponseDataLength; /* 0x14 */ +} MPI26_MCTP_PASSTHROUGH_REPLY, + *PTR_MPI26_MCTP_PASSTHROUGH_REPLY, + Mpi26MctpPassthroughReply_t, + *pMpi26MctpPassthroughReply_t; #endif diff --git a/drivers/scsi/mpt3sas/mpi/mpi2_pci.h b/drivers/scsi/mpt3sas/mpi/mpi2_pci.h index 63a09509d7d1..bb7b79cfa558 100644 --- a/drivers/scsi/mpt3sas/mpi/mpi2_pci.h +++ b/drivers/scsi/mpt3sas/mpi/mpi2_pci.h @@ -6,7 +6,7 @@ * Title: MPI PCIe Attached Devices structures and definitions. * Creation Date: October 9, 2012 * - * mpi2_pci.h Version: 02.00.03 + * mpi2_pci.h Version: 02.00.04 * * NOTE: Names (typedefs, defines, etc.) beginning with an MPI25 or Mpi25 * prefix are for use only on MPI v2.5 products, and must not be used @@ -24,6 +24,8 @@ * 07-01-16 02.00.02 Added MPI26_NVME_FLAGS_FORCE_ADMIN_ERR_RESP to * NVME Encapsulated Request. * 07-22-18 02.00.03 Updted flags field for NVME Encapsulated req + * 12-17-18 02.00.04 Added MPI26_PCIE_DEVINFO_SCSI + * Shortten some defines to be compatible with DOS * -------------------------------------------------------------------------- */ @@ -41,7 +43,7 @@ #define MPI26_PCIE_DEVINFO_NO_DEVICE (0x00000000) #define MPI26_PCIE_DEVINFO_PCI_SWITCH (0x00000001) #define MPI26_PCIE_DEVINFO_NVME (0x00000003) - +#define MPI26_PCIE_DEVINFO_SCSI (0x00000004) /**************************************************************************** * NVMe Encapsulated message @@ -75,10 +77,9 @@ typedef struct _MPI26_NVME_ENCAPSULATED_REQUEST { #define MPI26_NVME_FLAGS_SUBMISSIONQ_IO (0x0000) #define MPI26_NVME_FLAGS_SUBMISSIONQ_ADMIN (0x0010) /*Error Response Address Space */ -#define MPI26_NVME_FLAGS_MASK_ERROR_RSP_ADDR (0x000C) -#define MPI26_NVME_FLAGS_MASK_ERROR_RSP_ADDR_MASK (0x000C) -#define MPI26_NVME_FLAGS_SYSTEM_RSP_ADDR (0x0000) -#define MPI26_NVME_FLAGS_IOCCTL_RSP_ADDR (0x0008) +#define MPI26_NVME_FLAGS_ERR_RSP_ADDR_MASK (0x000C) +#define MPI26_NVME_FLAGS_ERR_RSP_ADDR_SYSTEM (0x0000) +#define MPI26_NVME_FLAGS_ERR_RSP_ADDR_IOCTL (0x0008) /* Data Direction*/ #define MPI26_NVME_FLAGS_DATADIRECTION_MASK (0x0003) #define MPI26_NVME_FLAGS_NODATATRANSFER (0x0000) diff --git a/drivers/scsi/mpt3sas/mpi/mpi2_tool.h b/drivers/scsi/mpt3sas/mpi/mpi2_tool.h index 3f966b6796b3..17ef7f63b938 100644 --- a/drivers/scsi/mpt3sas/mpi/mpi2_tool.h +++ b/drivers/scsi/mpt3sas/mpi/mpi2_tool.h @@ -7,7 +7,7 @@ * Title: MPI diagnostic tool structures and definitions * Creation Date: March 26, 2007 * - * mpi2_tool.h Version: 02.00.15 + * mpi2_tool.h Version: 02.00.16 * * Version History * --------------- @@ -40,6 +40,7 @@ * Tool Request Message. * 07-22-18 02.00.15 Added defines for new TOOLBOX_PCIE_LANE_MARGINING tool. * Added option for DeviceInfo field in ISTWI tool. + * 12-17-18 02.00.16 Shorten some defines to be compatible with DOS. * -------------------------------------------------------------------------- */ @@ -230,11 +231,11 @@ typedef struct _MPI2_TOOLBOX_ISTWI_READ_WRITE_REQUEST { #define MPI2_TOOL_ISTWI_FLAG_PAGE_ADDR_MASK (0x07) /*MPI26 TOOLBOX Request MsgFlags defines */ -#define MPI26_TOOLBOX_REQ_MSGFLAGS_ADDRESSING_MASK (0x01) +#define MPI26_TOOL_ISTWI_MSGFLG_ADDR_MASK (0x01) /*Request uses Man Page 43 device index addressing */ -#define MPI26_TOOLBOX_REQ_MSGFLAGS_ADDRESSING_DEVINDEX (0x00) +#define MPI26_TOOL_ISTWI_MSGFLG_ADDR_INDEX (0x00) /*Request uses Man Page 43 device info struct addressing */ -#define MPI26_TOOLBOX_REQ_MSGFLAGS_ADDRESSING_DEVINFO (0x01) +#define MPI26_TOOL_ISTWI_MSGFLG_ADDR_INFO (0x01) /*Toolbox ISTWI Read Write Tool reply message */ typedef struct _MPI2_TOOLBOX_ISTWI_REPLY { @@ -403,7 +404,7 @@ Mpi2ToolboxTextDisplayRequest_t, */ /*Toolbox Backend Lane Margining Tool request message */ -typedef struct _MPI26_TOOLBOX_LANE_MARGINING_REQUEST { +typedef struct _MPI26_TOOLBOX_LANE_MARGIN_REQUEST { U8 Tool; /*0x00 */ U8 Reserved1; /*0x01 */ U8 ChainOffset; /*0x02 */ @@ -434,7 +435,7 @@ typedef struct _MPI26_TOOLBOX_LANE_MARGINING_REQUEST { /*Toolbox Backend Lane Margining Tool reply message */ -typedef struct _MPI26_TOOLBOX_LANE_MARGINING_REPLY { +typedef struct _MPI26_TOOLBOX_LANE_MARGIN_REPLY { U8 Tool; /*0x00 */ U8 Reserved1; /*0x01 */ U8 MsgLength; /*0x02 */ diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c index 0a6cb8f0680c..0d652db8fe24 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.c +++ b/drivers/scsi/mpt3sas/mpt3sas_base.c @@ -60,7 +60,6 @@ #include <linux/ktime.h> #include <linux/kthread.h> #include <asm/page.h> /* To get host page size per arch */ -#include <linux/aer.h> #include "mpt3sas_base.h" @@ -74,32 +73,73 @@ static MPT_CALLBACK mpt_callbacks[MPT_MAX_CALLBACKS]; #define MAX_HBA_QUEUE_DEPTH 30000 #define MAX_CHAIN_DEPTH 100000 static int max_queue_depth = -1; -module_param(max_queue_depth, int, 0); +module_param(max_queue_depth, int, 0444); MODULE_PARM_DESC(max_queue_depth, " max controller queue depth "); static int max_sgl_entries = -1; -module_param(max_sgl_entries, int, 0); +module_param(max_sgl_entries, int, 0444); MODULE_PARM_DESC(max_sgl_entries, " max sg entries "); static int msix_disable = -1; -module_param(msix_disable, int, 0); +module_param(msix_disable, int, 0444); MODULE_PARM_DESC(msix_disable, " disable msix routed interrupts (default=0)"); static int smp_affinity_enable = 1; -module_param(smp_affinity_enable, int, S_IRUGO); +module_param(smp_affinity_enable, int, 0444); MODULE_PARM_DESC(smp_affinity_enable, "SMP affinity feature enable/disable Default: enable(1)"); static int max_msix_vectors = -1; -module_param(max_msix_vectors, int, 0); +module_param(max_msix_vectors, int, 0444); MODULE_PARM_DESC(max_msix_vectors, " max msix vectors"); +static int irqpoll_weight = -1; +module_param(irqpoll_weight, int, 0444); +MODULE_PARM_DESC(irqpoll_weight, + "irq poll weight (default= one fourth of HBA queue depth)"); + static int mpt3sas_fwfault_debug; MODULE_PARM_DESC(mpt3sas_fwfault_debug, " enable detection of firmware fault and halt firmware - (default=0)"); +static int perf_mode = -1; +module_param(perf_mode, int, 0444); +MODULE_PARM_DESC(perf_mode, + "Performance mode (only for Aero/Sea Generation), options:\n\t\t" + "0 - balanced: high iops mode is enabled &\n\t\t" + "interrupt coalescing is enabled only on high iops queues,\n\t\t" + "1 - iops: high iops mode is disabled &\n\t\t" + "interrupt coalescing is enabled on all queues,\n\t\t" + "2 - latency: high iops mode is disabled &\n\t\t" + "interrupt coalescing is enabled on all queues with timeout value 0xA,\n" + "\t\tdefault - default perf_mode is 'balanced'" + ); + +static int poll_queues; +module_param(poll_queues, int, 0444); +MODULE_PARM_DESC(poll_queues, "Number of queues to be use for io_uring poll mode.\n\t\t" + "This parameter is effective only if host_tagset_enable=1. &\n\t\t" + "when poll_queues are enabled then &\n\t\t" + "perf_mode is set to latency mode. &\n\t\t" + ); + +enum mpt3sas_perf_mode { + MPT_PERF_MODE_DEFAULT = -1, + MPT_PERF_MODE_BALANCED = 0, + MPT_PERF_MODE_IOPS = 1, + MPT_PERF_MODE_LATENCY = 2, +}; + +static int +_base_wait_on_iocstate(struct MPT3SAS_ADAPTER *ioc, + u32 ioc_state, int timeout); static int _base_get_ioc_facts(struct MPT3SAS_ADAPTER *ioc); +static void +_base_clear_outstanding_commands(struct MPT3SAS_ADAPTER *ioc); + +static u32 +_base_readl_ext_retry(const void __iomem *addr); /** * mpt3sas_base_check_cmd_timeout - Function @@ -111,7 +151,7 @@ _base_get_ioc_facts(struct MPT3SAS_ADAPTER *ioc); * @mpi_request:mf request pointer. * @sz: size of buffer. * - * @Returns - 1/0 Reset to be done or Not + * Return: 1/0 Reset to be done or Not */ u8 mpt3sas_base_check_cmd_timeout(struct MPT3SAS_ADAPTER *ioc, @@ -158,13 +198,13 @@ module_param_call(mpt3sas_fwfault_debug, _scsih_set_fwfault_debug, /** * _base_readl_aero - retry readl for max three times. - * @addr - MPT Fusion system interface register address + * @addr: MPT Fusion system interface register address * * Retry the readl() for max three times if it gets zero value * while reading the system interface register. */ static inline u32 -_base_readl_aero(const volatile void __iomem *addr) +_base_readl_aero(const void __iomem *addr) { u32 i = 0, ret_val; @@ -176,8 +216,22 @@ _base_readl_aero(const volatile void __iomem *addr) return ret_val; } +static u32 +_base_readl_ext_retry(const void __iomem *addr) +{ + u32 i, ret_val; + + for (i = 0 ; i < 30 ; i++) { + ret_val = readl(addr); + if (ret_val != 0) + break; + } + + return ret_val; +} + static inline u32 -_base_readl(const volatile void __iomem *addr) +_base_readl(const void __iomem *addr) { return readl(addr); } @@ -381,7 +435,7 @@ static void _clone_sg_entries(struct MPT3SAS_ADAPTER *ioc, { Mpi2SGESimple32_t *sgel, *sgel_next; u32 sgl_flags, sge_chain_count = 0; - bool is_write = 0; + bool is_write = false; u16 i = 0; void __iomem *buffer_iomem; phys_addr_t buffer_iomem_phys; @@ -410,7 +464,7 @@ static void _clone_sg_entries(struct MPT3SAS_ADAPTER *ioc, return; /* From smid we can get scsi_cmd, once we have sg_scmd, - * we just need to get sg_virt and sg_next to get virual + * we just need to get sg_virt and sg_next to get virtual * address associated with sgel->Address. */ @@ -450,7 +504,7 @@ static void _clone_sg_entries(struct MPT3SAS_ADAPTER *ioc, if (le32_to_cpu(sgel->FlagsLength) & (MPI2_SGE_FLAGS_HOST_TO_IOC << MPI2_SGE_FLAGS_SHIFT)) - is_write = 1; + is_write = true; for (i = 0; i < MPT_MIN_PHYS_SEGMENTS + ioc->facts.MaxChainDepth; i++) { @@ -567,6 +621,71 @@ static int mpt3sas_remove_dead_ioc_func(void *arg) } /** + * _base_sync_drv_fw_timestamp - Sync Drive-Fw TimeStamp. + * @ioc: Per Adapter Object + * + * Return: nothing. + */ +static void _base_sync_drv_fw_timestamp(struct MPT3SAS_ADAPTER *ioc) +{ + Mpi26IoUnitControlRequest_t *mpi_request; + Mpi26IoUnitControlReply_t *mpi_reply; + u16 smid; + ktime_t current_time; + u64 TimeStamp = 0; + u8 issue_reset = 0; + + mutex_lock(&ioc->scsih_cmds.mutex); + if (ioc->scsih_cmds.status != MPT3_CMD_NOT_USED) { + ioc_err(ioc, "scsih_cmd in use %s\n", __func__); + goto out; + } + ioc->scsih_cmds.status = MPT3_CMD_PENDING; + smid = mpt3sas_base_get_smid(ioc, ioc->scsih_cb_idx); + if (!smid) { + ioc_err(ioc, "Failed obtaining a smid %s\n", __func__); + ioc->scsih_cmds.status = MPT3_CMD_NOT_USED; + goto out; + } + mpi_request = mpt3sas_base_get_msg_frame(ioc, smid); + ioc->scsih_cmds.smid = smid; + memset(mpi_request, 0, sizeof(Mpi26IoUnitControlRequest_t)); + mpi_request->Function = MPI2_FUNCTION_IO_UNIT_CONTROL; + mpi_request->Operation = MPI26_CTRL_OP_SET_IOC_PARAMETER; + mpi_request->IOCParameter = MPI26_SET_IOC_PARAMETER_SYNC_TIMESTAMP; + current_time = ktime_get_real(); + TimeStamp = ktime_to_ms(current_time); + mpi_request->Reserved7 = cpu_to_le32(TimeStamp >> 32); + mpi_request->IOCParameterValue = cpu_to_le32(TimeStamp & 0xFFFFFFFF); + init_completion(&ioc->scsih_cmds.done); + ioc->put_smid_default(ioc, smid); + dinitprintk(ioc, ioc_info(ioc, + "Io Unit Control Sync TimeStamp (sending), @time %lld ms\n", + TimeStamp)); + wait_for_completion_timeout(&ioc->scsih_cmds.done, + MPT3SAS_TIMESYNC_TIMEOUT_SECONDS*HZ); + if (!(ioc->scsih_cmds.status & MPT3_CMD_COMPLETE)) { + mpt3sas_check_cmd_timeout(ioc, + ioc->scsih_cmds.status, mpi_request, + sizeof(Mpi2SasIoUnitControlRequest_t)/4, issue_reset); + goto issue_host_reset; + } + if (ioc->scsih_cmds.status & MPT3_CMD_REPLY_VALID) { + mpi_reply = ioc->scsih_cmds.reply; + dinitprintk(ioc, ioc_info(ioc, + "Io Unit Control sync timestamp (complete): ioc_status(0x%04x), loginfo(0x%08x)\n", + le16_to_cpu(mpi_reply->IOCStatus), + le32_to_cpu(mpi_reply->IOCLogInfo))); + } +issue_host_reset: + if (issue_reset) + mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); + ioc->scsih_cmds.status = MPT3_CMD_NOT_USED; +out: + mutex_unlock(&ioc->scsih_cmds.mutex); +} + +/** * _base_fault_reset_work - workq handling ioc fault conditions * @work: input argument, used to derive ioc * @@ -584,7 +703,8 @@ _base_fault_reset_work(struct work_struct *work) spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); - if (ioc->shost_recovery || ioc->pci_error_recovery) + if ((ioc->shost_recovery && (ioc->ioc_coredump_loop == 0)) || + ioc->pci_error_recovery) goto rearm_timer; spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); @@ -608,11 +728,12 @@ _base_fault_reset_work(struct work_struct *work) /* * Call _scsih_flush_pending_cmds callback so that we flush all - * pending commands back to OS. This call is required to aovid + * pending commands back to OS. This call is required to avoid * deadlock at block layer. Dead IOC will fail to do diag reset, * and this call is safe since dead ioc will never return any * command back from HW. */ + mpt3sas_base_pause_mq_polling(ioc); ioc->schedule_dead_ioc_flush_running_cmds(ioc); /* * Set remove_host flag early since kernel thread will @@ -631,21 +752,70 @@ _base_fault_reset_work(struct work_struct *work) return; /* don't rearm timer */ } - ioc->non_operational_loop = 0; + if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_COREDUMP) { + u8 timeout = (ioc->manu_pg11.CoreDumpTOSec) ? + ioc->manu_pg11.CoreDumpTOSec : + MPT3SAS_DEFAULT_COREDUMP_TIMEOUT_SECONDS; + + timeout /= (FAULT_POLLING_INTERVAL/1000); + + if (ioc->ioc_coredump_loop == 0) { + mpt3sas_print_coredump_info(ioc, + doorbell & MPI2_DOORBELL_DATA_MASK); + /* do not accept any IOs and disable the interrupts */ + spin_lock_irqsave( + &ioc->ioc_reset_in_progress_lock, flags); + ioc->shost_recovery = 1; + spin_unlock_irqrestore( + &ioc->ioc_reset_in_progress_lock, flags); + mpt3sas_base_mask_interrupts(ioc); + mpt3sas_base_pause_mq_polling(ioc); + _base_clear_outstanding_commands(ioc); + } + + ioc_info(ioc, "%s: CoreDump loop %d.", + __func__, ioc->ioc_coredump_loop); + + /* Wait until CoreDump completes or times out */ + if (ioc->ioc_coredump_loop++ < timeout) { + spin_lock_irqsave( + &ioc->ioc_reset_in_progress_lock, flags); + goto rearm_timer; + } + } + if (ioc->ioc_coredump_loop) { + if ((doorbell & MPI2_IOC_STATE_MASK) != MPI2_IOC_STATE_COREDUMP) + ioc_err(ioc, "%s: CoreDump completed. LoopCount: %d", + __func__, ioc->ioc_coredump_loop); + else + ioc_err(ioc, "%s: CoreDump Timed out. LoopCount: %d", + __func__, ioc->ioc_coredump_loop); + ioc->ioc_coredump_loop = MPT3SAS_COREDUMP_LOOP_DONE; + } + ioc->non_operational_loop = 0; if ((doorbell & MPI2_IOC_STATE_MASK) != MPI2_IOC_STATE_OPERATIONAL) { rc = mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); ioc_warn(ioc, "%s: hard reset: %s\n", __func__, rc == 0 ? "success" : "failed"); doorbell = mpt3sas_base_get_iocstate(ioc, 0); - if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) - mpt3sas_base_fault_info(ioc, doorbell & + if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) { + mpt3sas_print_fault_code(ioc, doorbell & + MPI2_DOORBELL_DATA_MASK); + } else if ((doorbell & MPI2_IOC_STATE_MASK) == + MPI2_IOC_STATE_COREDUMP) + mpt3sas_print_coredump_info(ioc, doorbell & MPI2_DOORBELL_DATA_MASK); if (rc && (doorbell & MPI2_IOC_STATE_MASK) != MPI2_IOC_STATE_OPERATIONAL) return; /* don't rearm timer */ } - + ioc->ioc_coredump_loop = 0; + if (ioc->time_sync_interval && + ++ioc->timestamp_update_count >= ioc->time_sync_interval) { + ioc->timestamp_update_count = 0; + _base_sync_drv_fw_timestamp(ioc); + } spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); rearm_timer: if (ioc->fault_reset_work_q) @@ -669,14 +839,15 @@ mpt3sas_base_start_watchdog(struct MPT3SAS_ADAPTER *ioc) if (ioc->fault_reset_work_q) return; + ioc->timestamp_update_count = 0; /* initialize fault polling */ INIT_DELAYED_WORK(&ioc->fault_reset_work, _base_fault_reset_work); snprintf(ioc->fault_reset_work_q_name, sizeof(ioc->fault_reset_work_q_name), "poll_%s%d_status", ioc->driver_name, ioc->id); - ioc->fault_reset_work_q = - create_singlethread_workqueue(ioc->fault_reset_work_q_name); + ioc->fault_reset_work_q = alloc_ordered_workqueue( + "%s", WQ_MEM_RECLAIM, ioc->fault_reset_work_q_name); if (!ioc->fault_reset_work_q) { ioc_err(ioc, "%s: failed (line=%d)\n", __func__, __LINE__); return; @@ -718,12 +889,56 @@ mpt3sas_base_stop_watchdog(struct MPT3SAS_ADAPTER *ioc) * @fault_code: fault code */ void -mpt3sas_base_fault_info(struct MPT3SAS_ADAPTER *ioc , u16 fault_code) +mpt3sas_base_fault_info(struct MPT3SAS_ADAPTER *ioc, u16 fault_code) { ioc_err(ioc, "fault_state(0x%04x)!\n", fault_code); } /** + * mpt3sas_base_coredump_info - verbose translation of firmware CoreDump state + * @ioc: per adapter object + * @fault_code: fault code + * + * Return: nothing. + */ +void +mpt3sas_base_coredump_info(struct MPT3SAS_ADAPTER *ioc, u16 fault_code) +{ + ioc_err(ioc, "coredump_state(0x%04x)!\n", fault_code); +} + +/** + * mpt3sas_base_wait_for_coredump_completion - Wait until coredump + * completes or times out + * @ioc: per adapter object + * @caller: caller function name + * + * Return: 0 for success, non-zero for failure. + */ +int +mpt3sas_base_wait_for_coredump_completion(struct MPT3SAS_ADAPTER *ioc, + const char *caller) +{ + u8 timeout = (ioc->manu_pg11.CoreDumpTOSec) ? + ioc->manu_pg11.CoreDumpTOSec : + MPT3SAS_DEFAULT_COREDUMP_TIMEOUT_SECONDS; + + int ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_FAULT, + timeout); + + if (ioc_state) + ioc_err(ioc, + "%s: CoreDump timed out. (ioc_state=0x%x)\n", + caller, ioc_state); + else + ioc_info(ioc, + "%s: CoreDump completed. (ioc_state=0x%x)\n", + caller, ioc_state); + + return ioc_state; +} + +/** * mpt3sas_halt_firmware - halt's mpt controller firmware * @ioc: per adapter object * @@ -742,10 +957,15 @@ mpt3sas_halt_firmware(struct MPT3SAS_ADAPTER *ioc) dump_stack(); - doorbell = ioc->base_readl(&ioc->chip->Doorbell); - if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) - mpt3sas_base_fault_info(ioc , doorbell); - else { + doorbell = ioc->base_readl_ext_retry(&ioc->chip->Doorbell); + if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) { + mpt3sas_print_fault_code(ioc, doorbell & + MPI2_DOORBELL_DATA_MASK); + } else if ((doorbell & MPI2_IOC_STATE_MASK) == + MPI2_IOC_STATE_COREDUMP) { + mpt3sas_print_coredump_info(ioc, doorbell & + MPI2_DOORBELL_DATA_MASK); + } else { writel(0xC0FFEE00, &ioc->chip->Doorbell); ioc_err(ioc, "Firmware is halted due to command timeout\n"); } @@ -781,6 +1001,20 @@ _base_sas_ioc_info(struct MPT3SAS_ADAPTER *ioc, MPI2DefaultReply_t *mpi_reply, if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) return; + /* + * Older Firmware version doesn't support driver trigger pages. + * So, skip displaying 'config invalid type' type + * of error message. + */ + if (request_hdr->Function == MPI2_FUNCTION_CONFIG) { + Mpi2ConfigRequest_t *rqst = (Mpi2ConfigRequest_t *)request_hdr; + + if ((rqst->ExtPageType == + MPI2_CONFIG_EXTPAGETYPE_DRIVER_PERSISTENT_TRIGGER) && + !(ioc->logging_level & MPT_DEBUG_CONFIG)) { + return; + } + } switch (ioc_status) { @@ -839,7 +1073,7 @@ _base_sas_ioc_info(struct MPT3SAS_ADAPTER *ioc, MPI2DefaultReply_t *mpi_reply, desc = "config no defaults"; break; case MPI2_IOCSTATUS_CONFIG_CANT_COMMIT: - desc = "config cant commit"; + desc = "config can't commit"; break; /**************************************************************************** @@ -968,6 +1202,11 @@ _base_sas_ioc_info(struct MPT3SAS_ADAPTER *ioc, MPI2DefaultReply_t *mpi_reply, ioc->sge_size; func_str = "nvme_encapsulated"; break; + case MPI2_FUNCTION_MCTP_PASSTHROUGH: + frame_sz = sizeof(Mpi26MctpPassthroughRequest_t) + + ioc->sge_size; + func_str = "mctp_passthru"; + break; default: frame_sz = 32; func_str = "unknown"; @@ -1103,7 +1342,7 @@ _base_display_event_data(struct MPT3SAS_ADAPTER *ioc, * @log_info: log info */ static void -_base_sas_log_info(struct MPT3SAS_ADAPTER *ioc , u32 log_info) +_base_sas_log_info(struct MPT3SAS_ADAPTER *ioc, u32 log_info) { union loginfo_type { u32 loginfo; @@ -1151,11 +1390,11 @@ _base_sas_log_info(struct MPT3SAS_ADAPTER *ioc , u32 log_info) } /** - * _base_display_reply_info - + * _base_display_reply_info - handle reply descriptors depending on IOC Status * @ioc: per adapter object * @smid: system request message index * @msix_index: MSIX table index supplied by the OS - * @reply: reply message frame(lower 32bit addr) + * @reply: reply message frame (lower 32bit addr) */ static void _base_display_reply_info(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, @@ -1175,13 +1414,19 @@ _base_display_reply_info(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, if ((ioc_status & MPI2_IOCSTATUS_MASK) && (ioc->logging_level & MPT_DEBUG_REPLY)) { - _base_sas_ioc_info(ioc , mpi_reply, + _base_sas_ioc_info(ioc, mpi_reply, mpt3sas_base_get_msg_frame(ioc, smid)); } if (ioc_status & MPI2_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE) { loginfo = le32_to_cpu(mpi_reply->IOCLogInfo); - _base_sas_log_info(ioc, loginfo); + if (ioc->logging_level & MPT_DEBUG_REPLY) + _base_sas_log_info(ioc, loginfo); + else { + if (!((ioc_status & MPI2_IOCSTATUS_MASK) & + MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)) + _base_sas_log_info(ioc, loginfo); + } } if (ioc_status || loginfo) { @@ -1277,7 +1522,7 @@ _base_async_event(struct MPT3SAS_ADAPTER *ioc, u8 msix_index, u32 reply) ack_request->EventContext = mpi_reply->EventContext; ack_request->VF_ID = 0; /* TODO */ ack_request->VP_ID = 0; - mpt3sas_base_put_smid_default(ioc, smid); + ioc->put_smid_default(ioc, smid); out: @@ -1340,13 +1585,62 @@ _base_get_cb_idx(struct MPT3SAS_ADAPTER *ioc, u16 smid) } /** - * _base_mask_interrupts - disable interrupts + * mpt3sas_base_pause_mq_polling - pause polling on the mq poll queues + * when driver is flushing out the IOs. + * @ioc: per adapter object + * + * Pause polling on the mq poll (io uring) queues when driver is flushing + * out the IOs. Otherwise we may see the race condition of completing the same + * IO from two paths. + * + * Returns nothing. + */ +void +mpt3sas_base_pause_mq_polling(struct MPT3SAS_ADAPTER *ioc) +{ + int iopoll_q_count = + ioc->reply_queue_count - ioc->iopoll_q_start_index; + int qid; + + for (qid = 0; qid < iopoll_q_count; qid++) + atomic_set(&ioc->io_uring_poll_queues[qid].pause, 1); + + /* + * wait for current poll to complete. + */ + for (qid = 0; qid < iopoll_q_count; qid++) { + while (atomic_read(&ioc->io_uring_poll_queues[qid].busy)) { + cpu_relax(); + udelay(500); + } + } +} + +/** + * mpt3sas_base_resume_mq_polling - Resume polling on mq poll queues. + * @ioc: per adapter object + * + * Returns nothing. + */ +void +mpt3sas_base_resume_mq_polling(struct MPT3SAS_ADAPTER *ioc) +{ + int iopoll_q_count = + ioc->reply_queue_count - ioc->iopoll_q_start_index; + int qid; + + for (qid = 0; qid < iopoll_q_count; qid++) + atomic_set(&ioc->io_uring_poll_queues[qid].pause, 0); +} + +/** + * mpt3sas_base_mask_interrupts - disable interrupts * @ioc: per adapter object * * Disabling ResetIRQ, Reply and Doorbell Interrupts */ -static void -_base_mask_interrupts(struct MPT3SAS_ADAPTER *ioc) +void +mpt3sas_base_mask_interrupts(struct MPT3SAS_ADAPTER *ioc) { u32 him_register; @@ -1358,13 +1652,13 @@ _base_mask_interrupts(struct MPT3SAS_ADAPTER *ioc) } /** - * _base_unmask_interrupts - enable interrupts + * mpt3sas_base_unmask_interrupts - enable interrupts * @ioc: per adapter object * * Enabling only Reply Interrupts */ -static void -_base_unmask_interrupts(struct MPT3SAS_ADAPTER *ioc) +void +mpt3sas_base_unmask_interrupts(struct MPT3SAS_ADAPTER *ioc) { u32 him_register; @@ -1382,20 +1676,30 @@ union reply_descriptor { } u; }; +static u32 base_mod64(u64 dividend, u32 divisor) +{ + u32 remainder; + + if (!divisor) + pr_err("mpt3sas: DIVISOR is zero, in div fn\n"); + remainder = do_div(dividend, divisor); + return remainder; +} + /** - * _base_interrupt - MPT adapter (IOC) specific interrupt handler. - * @irq: irq number (not used) - * @bus_id: bus identifier cookie == pointer to MPT_ADAPTER structure + * _base_process_reply_queue - Process reply descriptors from reply + * descriptor post queue. + * @reply_q: per IRQ's reply queue object. * - * Return: IRQ_HANDLED if processed, else IRQ_NONE. + * Return: number of reply descriptors processed from reply + * descriptor queue. */ -static irqreturn_t -_base_interrupt(int irq, void *bus_id) +static int +_base_process_reply_queue(struct adapter_reply_queue *reply_q) { - struct adapter_reply_queue *reply_q = bus_id; union reply_descriptor rd; - u32 completed_cmds; - u8 request_desript_type; + u64 completed_cmds; + u8 request_descript_type; u16 smid; u8 cb_idx; u32 reply; @@ -1404,21 +1708,18 @@ _base_interrupt(int irq, void *bus_id) Mpi2ReplyDescriptorsUnion_t *rpf; u8 rc; - if (ioc->mask_interrupts) - return IRQ_NONE; - + completed_cmds = 0; if (!atomic_add_unless(&reply_q->busy, 1, 1)) - return IRQ_NONE; + return completed_cmds; rpf = &reply_q->reply_post_free[reply_q->reply_post_host_index]; - request_desript_type = rpf->Default.ReplyFlags + request_descript_type = rpf->Default.ReplyFlags & MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK; - if (request_desript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED) { + if (request_descript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED) { atomic_dec(&reply_q->busy); - return IRQ_NONE; + return completed_cmds; } - completed_cmds = 0; cb_idx = 0xFF; do { rd.word = le64_to_cpu(rpf->Words); @@ -1426,11 +1727,11 @@ _base_interrupt(int irq, void *bus_id) goto out; reply = 0; smid = le16_to_cpu(rpf->Default.DescriptorTypeDependent1); - if (request_desript_type == + if (request_descript_type == MPI25_RPY_DESCRIPT_FLAGS_FAST_PATH_SCSI_IO_SUCCESS || - request_desript_type == + request_descript_type == MPI2_RPY_DESCRIPT_FLAGS_SCSI_IO_SUCCESS || - request_desript_type == + request_descript_type == MPI26_RPY_DESCRIPT_FLAGS_PCIE_ENCAPSULATED_SUCCESS) { cb_idx = _base_get_cb_idx(ioc, smid); if ((likely(cb_idx < MPT_MAX_CALLBACKS)) && @@ -1440,7 +1741,7 @@ _base_interrupt(int irq, void *bus_id) if (rc) mpt3sas_base_free_smid(ioc, smid); } - } else if (request_desript_type == + } else if (request_descript_type == MPI2_RPY_DESCRIPT_FLAGS_ADDRESS_REPLY) { reply = le32_to_cpu( rpf->AddressReply.ReplyFrameAddress); @@ -1486,7 +1787,7 @@ _base_interrupt(int irq, void *bus_id) (reply_q->reply_post_host_index == (ioc->reply_post_queue_depth - 1)) ? 0 : reply_q->reply_post_host_index + 1; - request_desript_type = + request_descript_type = reply_q->reply_post_free[reply_q->reply_post_host_index]. Default.ReplyFlags & MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK; completed_cmds++; @@ -1495,7 +1796,7 @@ _base_interrupt(int irq, void *bus_id) * So that FW can find enough entries to post the Reply * Descriptors in the reply descriptor post queue. */ - if (completed_cmds > ioc->hba_queue_depth/3) { + if (completed_cmds >= ioc->thresh_hold) { if (ioc->combined_reply_queue) { writel(reply_q->reply_post_host_index | ((msix_index & 7) << @@ -1507,9 +1808,15 @@ _base_interrupt(int irq, void *bus_id) MPI2_RPHI_MSIX_INDEX_SHIFT), &ioc->chip->ReplyPostHostIndex); } - completed_cmds = 1; + if (!reply_q->is_iouring_poll_q && + !reply_q->irq_poll_scheduled) { + reply_q->irq_poll_scheduled = true; + irq_poll_sched(&reply_q->irqpoll); + } + atomic_dec(&reply_q->busy); + return completed_cmds; } - if (request_desript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED) + if (request_descript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED) goto out; if (!reply_q->reply_post_host_index) rpf = reply_q->reply_post_free; @@ -1521,14 +1828,14 @@ _base_interrupt(int irq, void *bus_id) if (!completed_cmds) { atomic_dec(&reply_q->busy); - return IRQ_NONE; + return completed_cmds; } if (ioc->is_warpdrive) { writel(reply_q->reply_post_host_index, ioc->reply_post_host_index[msix_index]); atomic_dec(&reply_q->busy); - return IRQ_HANDLED; + return completed_cmds; } /* Update Reply Post Host Index. @@ -1555,7 +1862,118 @@ _base_interrupt(int irq, void *bus_id) MPI2_RPHI_MSIX_INDEX_SHIFT), &ioc->chip->ReplyPostHostIndex); atomic_dec(&reply_q->busy); - return IRQ_HANDLED; + return completed_cmds; +} + +/** + * mpt3sas_blk_mq_poll - poll the blk mq poll queue + * @shost: Scsi_Host object + * @queue_num: hw ctx queue number + * + * Return number of entries that has been processed from poll queue. + */ +int mpt3sas_blk_mq_poll(struct Scsi_Host *shost, unsigned int queue_num) +{ + struct MPT3SAS_ADAPTER *ioc = + (struct MPT3SAS_ADAPTER *)shost->hostdata; + struct adapter_reply_queue *reply_q; + int num_entries = 0; + int qid = queue_num - ioc->iopoll_q_start_index; + + if (atomic_read(&ioc->io_uring_poll_queues[qid].pause) || + !atomic_add_unless(&ioc->io_uring_poll_queues[qid].busy, 1, 1)) + return 0; + + reply_q = ioc->io_uring_poll_queues[qid].reply_q; + + num_entries = _base_process_reply_queue(reply_q); + atomic_dec(&ioc->io_uring_poll_queues[qid].busy); + + return num_entries; +} + +/** + * _base_interrupt - MPT adapter (IOC) specific interrupt handler. + * @irq: irq number (not used) + * @bus_id: bus identifier cookie == pointer to MPT_ADAPTER structure + * + * Return: IRQ_HANDLED if processed, else IRQ_NONE. + */ +static irqreturn_t +_base_interrupt(int irq, void *bus_id) +{ + struct adapter_reply_queue *reply_q = bus_id; + struct MPT3SAS_ADAPTER *ioc = reply_q->ioc; + + if (ioc->mask_interrupts) + return IRQ_NONE; + if (reply_q->irq_poll_scheduled) + return IRQ_HANDLED; + return ((_base_process_reply_queue(reply_q) > 0) ? + IRQ_HANDLED : IRQ_NONE); +} + +/** + * _base_irqpoll - IRQ poll callback handler + * @irqpoll: irq_poll object + * @budget: irq poll weight + * + * Return: number of reply descriptors processed + */ +static int +_base_irqpoll(struct irq_poll *irqpoll, int budget) +{ + struct adapter_reply_queue *reply_q; + int num_entries = 0; + + reply_q = container_of(irqpoll, struct adapter_reply_queue, + irqpoll); + if (reply_q->irq_line_enable) { + disable_irq_nosync(reply_q->os_irq); + reply_q->irq_line_enable = false; + } + num_entries = _base_process_reply_queue(reply_q); + if (num_entries < budget) { + irq_poll_complete(irqpoll); + reply_q->irq_poll_scheduled = false; + reply_q->irq_line_enable = true; + enable_irq(reply_q->os_irq); + /* + * Go for one more round of processing the + * reply descriptor post queue in case the HBA + * Firmware has posted some reply descriptors + * while reenabling the IRQ. + */ + _base_process_reply_queue(reply_q); + } + + return num_entries; +} + +/** + * _base_init_irqpolls - initliaze IRQ polls + * @ioc: per adapter object + * + * Return: nothing + */ +static void +_base_init_irqpolls(struct MPT3SAS_ADAPTER *ioc) +{ + struct adapter_reply_queue *reply_q, *next; + + if (list_empty(&ioc->reply_queue_list)) + return; + + list_for_each_entry_safe(reply_q, next, &ioc->reply_queue_list, list) { + if (reply_q->is_iouring_poll_q) + continue; + irq_poll_init(&reply_q->irqpoll, + ioc->hba_queue_depth/4, _base_irqpoll); + reply_q->irq_poll_scheduled = false; + reply_q->irq_line_enable = true; + reply_q->os_irq = pci_irq_vector(ioc->pdev, + reply_q->msix_index); + } } /** @@ -1574,12 +1992,14 @@ _base_is_controller_msix_enabled(struct MPT3SAS_ADAPTER *ioc) /** * mpt3sas_base_sync_reply_irqs - flush pending MSIX interrupts * @ioc: per adapter object - * Context: non ISR conext + * @poll: poll over reply descriptor pools incase interrupt for + * timed-out SCSI command got delayed + * Context: non-ISR context * * Called when a Task Management request has completed. */ void -mpt3sas_base_sync_reply_irqs(struct MPT3SAS_ADAPTER *ioc) +mpt3sas_base_sync_reply_irqs(struct MPT3SAS_ADAPTER *ioc, u8 poll) { struct adapter_reply_queue *reply_q; @@ -1596,7 +2016,31 @@ mpt3sas_base_sync_reply_irqs(struct MPT3SAS_ADAPTER *ioc) /* TMs are on msix_index == 0 */ if (reply_q->msix_index == 0) continue; + + if (reply_q->is_iouring_poll_q) { + _base_process_reply_queue(reply_q); + continue; + } + synchronize_irq(pci_irq_vector(ioc->pdev, reply_q->msix_index)); + if (reply_q->irq_poll_scheduled) { + /* Calling irq_poll_disable will wait for any pending + * callbacks to have completed. + */ + irq_poll_disable(&reply_q->irqpoll); + irq_poll_enable(&reply_q->irqpoll); + /* check how the scheduled poll has ended, + * clean up only if necessary + */ + if (reply_q->irq_poll_scheduled) { + reply_q->irq_poll_scheduled = false; + reply_q->irq_line_enable = true; + enable_irq(reply_q->os_irq); + } + } + + if (poll) + _base_process_reply_queue(reply_q); } } @@ -1783,7 +2227,16 @@ _base_build_sg(struct MPT3SAS_ADAPTER *ioc, void *psge, /** * _base_build_nvme_prp - This function is called for NVMe end devices to build - * a native SGL (NVMe PRP). The native SGL is built starting in the first PRP + * a native SGL (NVMe PRP). + * @ioc: per adapter object + * @smid: system request message index for getting asscociated SGL + * @nvme_encap_request: the NVMe request msg frame pointer + * @data_out_dma: physical address for WRITES + * @data_out_sz: data xfer size for WRITES + * @data_in_dma: physical address for READS + * @data_in_sz: data xfer size for READS + * + * The native SGL is built starting in the first PRP * entry of the NVMe message (PRP1). If the data buffer is small enough to be * described entirely using PRP1, then PRP2 is not used. If needed, PRP2 is * used to describe a larger data buffer. If the data buffer is too large to @@ -1812,7 +2265,7 @@ _base_build_sg(struct MPT3SAS_ADAPTER *ioc, void *psge, * Each 64-bit PRP entry comprises an address and an offset field. The address * always points at the beginning of a 4KB physical memory page, and the offset * describes where within that 4KB page the memory segment begins. Only the - * first element in a PRP list may contain a non-zero offest, implying that all + * first element in a PRP list may contain a non-zero offset, implying that all * memory segments following the first begin at the start of a 4KB page. * * Each PRP element normally describes 4KB of physical memory, with exceptions @@ -1826,14 +2279,6 @@ _base_build_sg(struct MPT3SAS_ADAPTER *ioc, void *psge, * Since PRP entries lack any indication of size, the overall data buffer length * is used to determine where the end of the data memory buffer is located, and * how many PRP entries are required to describe it. - * - * @ioc: per adapter object - * @smid: system request message index for getting asscociated SGL - * @nvme_encap_request: the NVMe request msg frame pointer - * @data_out_dma: physical address for WRITES - * @data_out_sz: data xfer size for WRITES - * @data_in_dma: physical address for READS - * @data_in_sz: data xfer size for READS */ static void _base_build_nvme_prp(struct MPT3SAS_ADAPTER *ioc, u16 smid, @@ -1990,8 +2435,8 @@ _base_build_nvme_prp(struct MPT3SAS_ADAPTER *ioc, u16 smid, } /** - * base_make_prp_nvme - - * Prepare PRPs(Physical Region Page)- SGLs specific to NVMe drives only + * base_make_prp_nvme - Prepare PRPs (Physical Region Page) - + * SGLs specific to NVMe drives only * * @ioc: per adapter object * @scmd: SCSI command from the mid-layer @@ -2137,6 +2582,11 @@ base_is_prp_possible(struct MPT3SAS_ADAPTER *ioc, bool build_prp = true; data_length = scsi_bufflen(scmd); + if (pcie_device && + (mpt3sas_scsih_is_pcie_scsi_device(pcie_device->device_info))) { + build_prp = false; + return build_prp; + } /* If Datalenth is <= 16K and number of SGE’s entries are <= 2 * we built IEEE SGL @@ -2171,16 +2621,12 @@ _base_check_pcie_native_sgl(struct MPT3SAS_ADAPTER *ioc, /* Get the SG list pointer and info. */ sges_left = scsi_dma_map(scmd); - if (sges_left < 0) { - sdev_printk(KERN_ERR, scmd->device, - "scsi_dma_map failed: request for %d bytes!\n", - scsi_bufflen(scmd)); + if (sges_left < 0) return 1; - } /* Check if we need to build a native SG list. */ - if (base_is_prp_possible(ioc, pcie_device, - scmd, sges_left) == 0) { + if (!base_is_prp_possible(ioc, pcie_device, + scmd, sges_left)) { /* We built a native SG list, just return. */ goto out; } @@ -2236,6 +2682,22 @@ _base_build_zero_len_sge_ieee(struct MPT3SAS_ADAPTER *ioc, void *paddr) _base_add_sg_single_ieee(paddr, sgl_flags, 0, 0, -1); } +static inline int _base_scsi_dma_map(struct scsi_cmnd *cmd) +{ + /* + * Some firmware versions byte-swap the REPORT ZONES command reply from + * ATA-ZAC devices by directly accessing in the host buffer. This does + * not respect the default command DMA direction and causes IOMMU page + * faults on some architectures with an IOMMU enforcing write mappings + * (e.g. AMD hosts). Avoid such issue by making the report zones buffer + * mapping bi-directional. + */ + if (cmd->cmnd[0] == ZBC_IN && cmd->cmnd[1] == ZI_REPORT_ZONES) + cmd->sc_data_direction = DMA_BIDIRECTIONAL; + + return scsi_dma_map(cmd); +} + /** * _base_build_sg_scmd - main sg creation routine * pcie_device is unused here! @@ -2282,13 +2744,9 @@ _base_build_sg_scmd(struct MPT3SAS_ADAPTER *ioc, sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; sg_scmd = scsi_sglist(scmd); - sges_left = scsi_dma_map(scmd); - if (sges_left < 0) { - sdev_printk(KERN_ERR, scmd->device, - "scsi_dma_map failed: request for %d bytes!\n", - scsi_bufflen(scmd)); + sges_left = _base_scsi_dma_map(scmd); + if (sges_left < 0) return -ENOMEM; - } sg_local = &mpi_request->SGL; sges_in_segment = ioc->max_sges_in_main_message; @@ -2430,13 +2888,9 @@ _base_build_sg_scmd_ieee(struct MPT3SAS_ADAPTER *ioc, } sg_scmd = scsi_sglist(scmd); - sges_left = scsi_dma_map(scmd); - if (sges_left < 0) { - sdev_printk(KERN_ERR, scmd->device, - "scsi_dma_map failed: request for %d bytes!\n", - scsi_bufflen(scmd)); + sges_left = _base_scsi_dma_map(scmd); + if (sges_left < 0) return -ENOMEM; - } sg_local = &mpi_request->SGL; sges_in_segment = (ioc->request_sz - @@ -2578,56 +3032,43 @@ _base_build_sg_ieee(struct MPT3SAS_ADAPTER *ioc, void *psge, static int _base_config_dma_addressing(struct MPT3SAS_ADAPTER *ioc, struct pci_dev *pdev) { - u64 required_mask, coherent_mask; struct sysinfo s; + u64 coherent_dma_mask, dma_mask; + + if (ioc->is_mcpu_endpoint || sizeof(dma_addr_t) == 4) { + ioc->dma_mask = 32; + coherent_dma_mask = dma_mask = DMA_BIT_MASK(32); + /* Set 63 bit DMA mask for all SAS3 and SAS35 controllers */ + } else if (ioc->hba_mpi_version_belonged > MPI2_VERSION) { + ioc->dma_mask = 63; + coherent_dma_mask = dma_mask = DMA_BIT_MASK(63); + } else { + ioc->dma_mask = 64; + coherent_dma_mask = dma_mask = DMA_BIT_MASK(64); + } - if (ioc->is_mcpu_endpoint) - goto try_32bit; - - required_mask = dma_get_required_mask(&pdev->dev); - if (sizeof(dma_addr_t) == 4 || required_mask == 32) - goto try_32bit; - - if (ioc->dma_mask) - coherent_mask = DMA_BIT_MASK(64); - else - coherent_mask = DMA_BIT_MASK(32); - - if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)) || - dma_set_coherent_mask(&pdev->dev, coherent_mask)) - goto try_32bit; - - ioc->base_add_sg_single = &_base_add_sg_single_64; - ioc->sge_size = sizeof(Mpi2SGESimple64_t); - ioc->dma_mask = 64; - goto out; + if (ioc->use_32bit_dma) + coherent_dma_mask = DMA_BIT_MASK(32); - try_32bit: - if (dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32))) + if (dma_set_mask(&pdev->dev, dma_mask) || + dma_set_coherent_mask(&pdev->dev, coherent_dma_mask)) return -ENODEV; - ioc->base_add_sg_single = &_base_add_sg_single_32; - ioc->sge_size = sizeof(Mpi2SGESimple32_t); - ioc->dma_mask = 32; - out: + if (ioc->dma_mask > 32) { + ioc->base_add_sg_single = &_base_add_sg_single_64; + ioc->sge_size = sizeof(Mpi2SGESimple64_t); + } else { + ioc->base_add_sg_single = &_base_add_sg_single_32; + ioc->sge_size = sizeof(Mpi2SGESimple32_t); + } + si_meminfo(&s); ioc_info(ioc, "%d BIT PCI BUS DMA ADDRESSING SUPPORTED, total mem (%ld kB)\n", - ioc->dma_mask, convert_to_kb(s.totalram)); + ioc->dma_mask, convert_to_kb(s.totalram)); return 0; } -static int -_base_change_consistent_dma_mask(struct MPT3SAS_ADAPTER *ioc, - struct pci_dev *pdev) -{ - if (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64))) { - if (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) - return -ENODEV; - } - return 0; -} - /** * _base_check_enable_msix - checks MSIX capabable. * @ioc: per adapter object @@ -2675,14 +3116,15 @@ _base_check_enable_msix(struct MPT3SAS_ADAPTER *ioc) } /** - * _base_free_irq - free irq + * mpt3sas_base_free_irq - free irq * @ioc: per adapter object * * Freeing respective reply_queue from the list. */ -static void -_base_free_irq(struct MPT3SAS_ADAPTER *ioc) +void +mpt3sas_base_free_irq(struct MPT3SAS_ADAPTER *ioc) { + unsigned int irq; struct adapter_reply_queue *reply_q, *next; if (list_empty(&ioc->reply_queue_list)) @@ -2690,6 +3132,15 @@ _base_free_irq(struct MPT3SAS_ADAPTER *ioc) list_for_each_entry_safe(reply_q, next, &ioc->reply_queue_list, list) { list_del(&reply_q->list); + if (reply_q->is_iouring_poll_q) { + kfree(reply_q); + continue; + } + + if (ioc->smp_affinity_enable) { + irq = pci_irq_vector(ioc->pdev, reply_q->msix_index); + irq_update_affinity_hint(irq, NULL); + } free_irq(pci_irq_vector(ioc->pdev, reply_q->msix_index), reply_q); kfree(reply_q); @@ -2708,7 +3159,7 @@ _base_request_irq(struct MPT3SAS_ADAPTER *ioc, u8 index) { struct pci_dev *pdev = ioc->pdev; struct adapter_reply_queue *reply_q; - int r; + int r, qid; reply_q = kzalloc(sizeof(struct adapter_reply_queue), GFP_KERNEL); if (!reply_q) { @@ -2720,6 +3171,17 @@ _base_request_irq(struct MPT3SAS_ADAPTER *ioc, u8 index) reply_q->msix_index = index; atomic_set(&reply_q->busy, 0); + + if (index >= ioc->iopoll_q_start_index) { + qid = index - ioc->iopoll_q_start_index; + snprintf(reply_q->name, MPT_NAME_LENGTH, "%s%d-mq-poll%d", + ioc->driver_name, ioc->id, qid); + reply_q->is_iouring_poll_q = 1; + ioc->io_uring_poll_queues[qid].reply_q = reply_q; + goto out; + } + + if (ioc->msix_enable) snprintf(reply_q->name, MPT_NAME_LENGTH, "%s%d-msix%d", ioc->driver_name, ioc->id, index); @@ -2734,7 +3196,7 @@ _base_request_irq(struct MPT3SAS_ADAPTER *ioc, u8 index) kfree(reply_q); return -EBUSY; } - +out: INIT_LIST_HEAD(&reply_q->list); list_add_tail(&reply_q->list, &ioc->reply_queue_list); return 0; @@ -2745,19 +3207,22 @@ _base_request_irq(struct MPT3SAS_ADAPTER *ioc, u8 index) * @ioc: per adapter object * * The enduser would need to set the affinity via /proc/irq/#/smp_affinity - * - * It would nice if we could call irq_set_affinity, however it is not - * an exported symbol */ static void _base_assign_reply_queues(struct MPT3SAS_ADAPTER *ioc) { - unsigned int cpu, nr_cpus, nr_msix, index = 0; + unsigned int cpu, nr_cpus, nr_msix, index = 0, irq; struct adapter_reply_queue *reply_q; + int iopoll_q_count = ioc->reply_queue_count - + ioc->iopoll_q_start_index; + const struct cpumask *mask; if (!_base_is_controller_msix_enabled(ioc)) return; + if (ioc->msix_load_balance) + return; + memset(ioc->cpu_msix_table, 0, ioc->cpu_msix_table_sz); nr_cpus = num_online_cpus(); @@ -2766,14 +3231,34 @@ _base_assign_reply_queues(struct MPT3SAS_ADAPTER *ioc) if (!nr_msix) return; - if (smp_affinity_enable) { + if (ioc->smp_affinity_enable) { + + /* + * set irq affinity to local numa node for those irqs + * corresponding to high iops queues. + */ + if (ioc->high_iops_queues) { + mask = cpumask_of_node(dev_to_node(&ioc->pdev->dev)); + for (index = 0; index < ioc->high_iops_queues; + index++) { + irq = pci_irq_vector(ioc->pdev, index); + irq_set_affinity_and_hint(irq, mask); + } + } + list_for_each_entry(reply_q, &ioc->reply_queue_list, list) { - const cpumask_t *mask = pci_irq_get_affinity(ioc->pdev, - reply_q->msix_index); + const cpumask_t *mask; + + if (reply_q->msix_index < ioc->high_iops_queues || + reply_q->msix_index >= ioc->iopoll_q_start_index) + continue; + + mask = pci_irq_get_affinity(ioc->pdev, + reply_q->msix_index); if (!mask) { ioc_warn(ioc, "no affinity for msi %x\n", reply_q->msix_index); - continue; + goto fall_back; } for_each_cpu_and(cpu, mask, cpu_online_mask) { @@ -2784,12 +3269,19 @@ _base_assign_reply_queues(struct MPT3SAS_ADAPTER *ioc) } return; } + +fall_back: cpu = cpumask_first(cpu_online_mask); + nr_msix -= (ioc->high_iops_queues - iopoll_q_count); + index = 0; list_for_each_entry(reply_q, &ioc->reply_queue_list, list) { - unsigned int i, group = nr_cpus / nr_msix; + if (reply_q->msix_index < ioc->high_iops_queues || + reply_q->msix_index >= ioc->iopoll_q_start_index) + continue; + if (cpu >= nr_cpus) break; @@ -2805,17 +3297,101 @@ _base_assign_reply_queues(struct MPT3SAS_ADAPTER *ioc) } /** - * _base_disable_msix - disables msix + * _base_check_and_enable_high_iops_queues - enable high iops mode * @ioc: per adapter object + * @hba_msix_vector_count: msix vectors supported by HBA + * + * Enable high iops queues only if + * - HBA is a SEA/AERO controller and + * - MSI-Xs vector supported by the HBA is 128 and + * - total CPU count in the system >=16 and + * - loaded driver with default max_msix_vectors module parameter and + * - system booted in non kdump mode * + * Return: nothing. */ static void -_base_disable_msix(struct MPT3SAS_ADAPTER *ioc) +_base_check_and_enable_high_iops_queues(struct MPT3SAS_ADAPTER *ioc, + int hba_msix_vector_count) +{ + u16 lnksta, speed; + + /* + * Disable high iops queues if io uring poll queues are enabled. + */ + if (perf_mode == MPT_PERF_MODE_IOPS || + perf_mode == MPT_PERF_MODE_LATENCY || + ioc->io_uring_poll_queues) { + ioc->high_iops_queues = 0; + return; + } + + if (perf_mode == MPT_PERF_MODE_DEFAULT) { + + pcie_capability_read_word(ioc->pdev, PCI_EXP_LNKSTA, &lnksta); + speed = lnksta & PCI_EXP_LNKSTA_CLS; + + if (speed < 0x4) { + ioc->high_iops_queues = 0; + return; + } + } + + if (!reset_devices && ioc->is_aero_ioc && + hba_msix_vector_count == MPT3SAS_GEN35_MAX_MSIX_QUEUES && + num_online_cpus() >= MPT3SAS_HIGH_IOPS_REPLY_QUEUES && + max_msix_vectors == -1) + ioc->high_iops_queues = MPT3SAS_HIGH_IOPS_REPLY_QUEUES; + else + ioc->high_iops_queues = 0; +} + +/** + * mpt3sas_base_disable_msix - disables msix + * @ioc: per adapter object + * + */ +void +mpt3sas_base_disable_msix(struct MPT3SAS_ADAPTER *ioc) { if (!ioc->msix_enable) return; - pci_disable_msix(ioc->pdev); + pci_free_irq_vectors(ioc->pdev); ioc->msix_enable = 0; + kfree(ioc->io_uring_poll_queues); +} + +/** + * _base_alloc_irq_vectors - allocate msix vectors + * @ioc: per adapter object + * + */ +static int +_base_alloc_irq_vectors(struct MPT3SAS_ADAPTER *ioc) +{ + int i, irq_flags = PCI_IRQ_MSIX; + struct irq_affinity desc = { .pre_vectors = ioc->high_iops_queues }; + struct irq_affinity *descp = &desc; + /* + * Don't allocate msix vectors for poll_queues. + * msix_vectors is always within a range of FW supported reply queue. + */ + int nr_msix_vectors = ioc->iopoll_q_start_index; + + + if (ioc->smp_affinity_enable) + irq_flags |= PCI_IRQ_AFFINITY | PCI_IRQ_ALL_TYPES; + else + descp = NULL; + + ioc_info(ioc, " %d %d %d\n", ioc->high_iops_queues, + ioc->reply_queue_count, nr_msix_vectors); + + i = pci_alloc_irq_vectors_affinity(ioc->pdev, + ioc->high_iops_queues, + nr_msix_vectors, irq_flags, descp); + + return i; } /** @@ -2829,7 +3405,9 @@ _base_enable_msix(struct MPT3SAS_ADAPTER *ioc) int r; int i, local_max_msix_vectors; u8 try_msix = 0; - unsigned int irq_flags = PCI_IRQ_MSIX; + int iopoll_q_count = 0; + + ioc->msix_load_balance = false; if (msix_disable == -1 || msix_disable == 0) try_msix = 1; @@ -2840,56 +3418,131 @@ _base_enable_msix(struct MPT3SAS_ADAPTER *ioc) if (_base_check_enable_msix(ioc) != 0) goto try_ioapic; - ioc->reply_queue_count = min_t(int, ioc->cpu_count, - ioc->msix_vector_count); + ioc_info(ioc, "MSI-X vectors supported: %d\n", ioc->msix_vector_count); + pr_info("\t no of cores: %d, max_msix_vectors: %d\n", + ioc->cpu_count, max_msix_vectors); - ioc_info(ioc, "MSI-X vectors supported: %d, no of cores: %d, max_msix_vectors: %d\n", - ioc->msix_vector_count, ioc->cpu_count, max_msix_vectors); + ioc->reply_queue_count = + min_t(int, ioc->cpu_count, ioc->msix_vector_count); if (!ioc->rdpq_array_enable && max_msix_vectors == -1) local_max_msix_vectors = (reset_devices) ? 1 : 8; else local_max_msix_vectors = max_msix_vectors; - if (local_max_msix_vectors > 0) - ioc->reply_queue_count = min_t(int, local_max_msix_vectors, - ioc->reply_queue_count); - else if (local_max_msix_vectors == 0) + if (local_max_msix_vectors == 0) goto try_ioapic; - if (ioc->msix_vector_count < ioc->cpu_count) - smp_affinity_enable = 0; + /* + * Enable msix_load_balance only if combined reply queue mode is + * disabled on SAS3 & above generation HBA devices. + */ + if (!ioc->combined_reply_queue && + ioc->hba_mpi_version_belonged != MPI2_VERSION) { + ioc_info(ioc, + "combined ReplyQueue is off, Enabling msix load balance\n"); + ioc->msix_load_balance = true; + } + + /* + * smp affinity setting is not need when msix load balance + * is enabled. + */ + if (ioc->msix_load_balance) + ioc->smp_affinity_enable = 0; + + if (!ioc->smp_affinity_enable || ioc->reply_queue_count <= 1) + ioc->shost->host_tagset = 0; + + /* + * Enable io uring poll queues only if host_tagset is enabled. + */ + if (ioc->shost->host_tagset) + iopoll_q_count = poll_queues; + + if (iopoll_q_count) { + ioc->io_uring_poll_queues = kcalloc(iopoll_q_count, + sizeof(struct io_uring_poll_queue), GFP_KERNEL); + if (!ioc->io_uring_poll_queues) + iopoll_q_count = 0; + } + + if (ioc->is_aero_ioc) + _base_check_and_enable_high_iops_queues(ioc, + ioc->msix_vector_count); + + /* + * Add high iops queues count to reply queue count if high iops queues + * are enabled. + */ + ioc->reply_queue_count = min_t(int, + ioc->reply_queue_count + ioc->high_iops_queues, + ioc->msix_vector_count); + + /* + * Adjust the reply queue count incase reply queue count + * exceeds the user provided MSIx vectors count. + */ + if (local_max_msix_vectors > 0) + ioc->reply_queue_count = min_t(int, local_max_msix_vectors, + ioc->reply_queue_count); + /* + * Add io uring poll queues count to reply queues count + * if io uring is enabled in driver. + */ + if (iopoll_q_count) { + if (ioc->reply_queue_count < (iopoll_q_count + MPT3_MIN_IRQS)) + iopoll_q_count = 0; + ioc->reply_queue_count = min_t(int, + ioc->reply_queue_count + iopoll_q_count, + ioc->msix_vector_count); + } - if (smp_affinity_enable) - irq_flags |= PCI_IRQ_AFFINITY; + /* + * Starting index of io uring poll queues in reply queue list. + */ + ioc->iopoll_q_start_index = + ioc->reply_queue_count - iopoll_q_count; - r = pci_alloc_irq_vectors(ioc->pdev, 1, ioc->reply_queue_count, - irq_flags); + r = _base_alloc_irq_vectors(ioc); if (r < 0) { - dfailprintk(ioc, - ioc_info(ioc, "pci_alloc_irq_vectors failed (r=%d) !!!\n", - r)); + ioc_info(ioc, "pci_alloc_irq_vectors failed (r=%d) !!!\n", r); goto try_ioapic; } + /* + * Adjust the reply queue count if the allocated + * MSIx vectors is less then the requested number + * of MSIx vectors. + */ + if (r < ioc->iopoll_q_start_index) { + ioc->reply_queue_count = r + iopoll_q_count; + ioc->iopoll_q_start_index = + ioc->reply_queue_count - iopoll_q_count; + } + ioc->msix_enable = 1; - ioc->reply_queue_count = r; for (i = 0; i < ioc->reply_queue_count; i++) { r = _base_request_irq(ioc, i); if (r) { - _base_free_irq(ioc); - _base_disable_msix(ioc); + mpt3sas_base_free_irq(ioc); + mpt3sas_base_disable_msix(ioc); goto try_ioapic; } } + ioc_info(ioc, "High IOPs queues : %s\n", + ioc->high_iops_queues ? "enabled" : "disabled"); + return 0; /* failback to io_apic interrupt routing */ try_ioapic: - + ioc->high_iops_queues = 0; + ioc_info(ioc, "High IOPs queues : disabled\n"); ioc->reply_queue_count = 1; - r = pci_alloc_irq_vectors(ioc->pdev, 1, 1, PCI_IRQ_LEGACY); + ioc->iopoll_q_start_index = ioc->reply_queue_count - 0; + r = pci_alloc_irq_vectors(ioc->pdev, 1, 1, PCI_IRQ_INTX); if (r < 0) { dfailprintk(ioc, ioc_info(ioc, "pci_alloc_irq_vector(legacy) failed (r=%d) !!!\n", @@ -2911,8 +3564,8 @@ mpt3sas_base_unmap_resources(struct MPT3SAS_ADAPTER *ioc) dexitprintk(ioc, ioc_info(ioc, "%s\n", __func__)); - _base_free_irq(ioc); - _base_disable_msix(ioc); + mpt3sas_base_free_irq(ioc); + mpt3sas_base_disable_msix(ioc); kfree(ioc->replyPostRegisterIndex); ioc->replyPostRegisterIndex = NULL; @@ -2925,11 +3578,49 @@ mpt3sas_base_unmap_resources(struct MPT3SAS_ADAPTER *ioc) if (pci_is_enabled(pdev)) { pci_release_selected_regions(ioc->pdev, ioc->bars); - pci_disable_pcie_error_reporting(pdev); pci_disable_device(pdev); } } +static int +_base_diag_reset(struct MPT3SAS_ADAPTER *ioc); + +/** + * mpt3sas_base_check_for_fault_and_issue_reset - check if IOC is in fault state + * and if it is in fault state then issue diag reset. + * @ioc: per adapter object + * + * Return: 0 for success, non-zero for failure. + */ +int +mpt3sas_base_check_for_fault_and_issue_reset(struct MPT3SAS_ADAPTER *ioc) +{ + u32 ioc_state; + int rc = -EFAULT; + + dinitprintk(ioc, pr_info("%s\n", __func__)); + if (ioc->pci_error_recovery) + return 0; + ioc_state = mpt3sas_base_get_iocstate(ioc, 0); + dhsprintk(ioc, pr_info("%s: ioc_state(0x%08x)\n", __func__, ioc_state)); + + if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) { + mpt3sas_print_fault_code(ioc, ioc_state & + MPI2_DOORBELL_DATA_MASK); + mpt3sas_base_mask_interrupts(ioc); + rc = _base_diag_reset(ioc); + } else if ((ioc_state & MPI2_IOC_STATE_MASK) == + MPI2_IOC_STATE_COREDUMP) { + mpt3sas_print_coredump_info(ioc, ioc_state & + MPI2_DOORBELL_DATA_MASK); + mpt3sas_base_wait_for_coredump_completion(ioc, __func__); + mpt3sas_base_mask_interrupts(ioc); + rc = _base_diag_reset(ioc); + } + + return rc; +} + /** * mpt3sas_base_map_resources - map in controller resources (io/irq/memap) * @ioc: per adapter object @@ -2942,10 +3633,11 @@ mpt3sas_base_map_resources(struct MPT3SAS_ADAPTER *ioc) struct pci_dev *pdev = ioc->pdev; u32 memap_sz; u32 pio_sz; - int i, r = 0; + int i, r = 0, rc; u64 pio_chip = 0; phys_addr_t chip_phys = 0; struct adapter_reply_queue *reply_q; + int iopoll_q_count = 0; dinitprintk(ioc, ioc_info(ioc, "%s\n", __func__)); @@ -2965,9 +3657,6 @@ mpt3sas_base_map_resources(struct MPT3SAS_ADAPTER *ioc) goto out_fail; } -/* AER (Advanced Error Reporting) hooks */ - pci_enable_pcie_error_reporting(pdev); - pci_set_master(pdev); @@ -2995,16 +3684,20 @@ mpt3sas_base_map_resources(struct MPT3SAS_ADAPTER *ioc) } if (ioc->chip == NULL) { - ioc_err(ioc, "unable to map adapter memory! or resource not found\n"); + ioc_err(ioc, + "unable to map adapter memory! or resource not found\n"); r = -EINVAL; goto out_fail; } - _base_mask_interrupts(ioc); + mpt3sas_base_mask_interrupts(ioc); r = _base_get_ioc_facts(ioc); - if (r) - goto out_fail; + if (r) { + rc = mpt3sas_base_check_for_fault_and_issue_reset(ioc); + if (rc || (_base_get_ioc_facts(ioc))) + goto out_fail; + } if (!ioc->rdpq_array_enable_assigned) { ioc->rdpq_array_enable = ioc->rdpq_array_capable; @@ -3015,6 +3708,14 @@ mpt3sas_base_map_resources(struct MPT3SAS_ADAPTER *ioc) if (r) goto out_fail; + iopoll_q_count = ioc->reply_queue_count - ioc->iopoll_q_start_index; + for (i = 0; i < iopoll_q_count; i++) { + atomic_set(&ioc->io_uring_poll_queues[i].busy, 0); + atomic_set(&ioc->io_uring_poll_queues[i].pause, 0); + } + + if (!ioc->is_driver_loading) + _base_init_irqpolls(ioc); /* Use the Combined reply queue feature only for SAS3 C0 & higher * revision HBAs and also only when reply queue count is greater than 8 */ @@ -3029,17 +3730,18 @@ mpt3sas_base_map_resources(struct MPT3SAS_ADAPTER *ioc) ioc->combined_reply_index_count, sizeof(resource_size_t *), GFP_KERNEL); if (!ioc->replyPostRegisterIndex) { - dfailprintk(ioc, - ioc_warn(ioc, "allocation for reply Post Register Index failed!!!\n")); + ioc_err(ioc, + "allocation for replyPostRegisterIndex failed!\n"); r = -ENOMEM; goto out_fail; } for (i = 0; i < ioc->combined_reply_index_count; i++) { - ioc->replyPostRegisterIndex[i] = (resource_size_t *) - ((u8 __force *)&ioc->chip->Doorbell + - MPI25_SUP_REPLY_POST_HOST_INDEX_OFFSET + - (i * MPT3_SUP_REPLY_POST_HOST_INDEX_REG_OFFSET)); + ioc->replyPostRegisterIndex[i] = + (resource_size_t __iomem *) + ((u8 __force *)&ioc->chip->Doorbell + + MPI25_SUP_REPLY_POST_HOST_INDEX_OFFSET + + (i * MPT3_SUP_REPLY_POST_HOST_INDEX_REG_OFFSET)); } } @@ -3054,11 +3756,18 @@ mpt3sas_base_map_resources(struct MPT3SAS_ADAPTER *ioc) * 4))); } - list_for_each_entry(reply_q, &ioc->reply_queue_list, list) + list_for_each_entry(reply_q, &ioc->reply_queue_list, list) { + if (reply_q->msix_index >= ioc->iopoll_q_start_index) { + pr_info("%s: enabled: index: %d\n", + reply_q->name, reply_q->msix_index); + continue; + } + pr_info("%s: %s enabled: IRQ %d\n", reply_q->name, ioc->msix_enable ? "PCI-MSI-X" : "IO-APIC", pci_irq_vector(ioc->pdev, reply_q->msix_index)); + } ioc_info(ioc, "iomem(%pap), mapped(0x%p), size(%d)\n", &chip_phys, ioc->chip, memap_sz); @@ -3155,13 +3864,65 @@ mpt3sas_base_get_reply_virt_addr(struct MPT3SAS_ADAPTER *ioc, u32 phys_addr) return ioc->reply + (phys_addr - (u32)ioc->reply_dma); } +/** + * _base_get_msix_index - get the msix index + * @ioc: per adapter object + * @scmd: scsi_cmnd object + * + * Return: msix index of general reply queues, + * i.e. reply queue on which IO request's reply + * should be posted by the HBA firmware. + */ static inline u8 -_base_get_msix_index(struct MPT3SAS_ADAPTER *ioc) +_base_get_msix_index(struct MPT3SAS_ADAPTER *ioc, + struct scsi_cmnd *scmd) { + /* Enables reply_queue load balancing */ + if (ioc->msix_load_balance) + return ioc->reply_queue_count ? + base_mod64(atomic64_add_return(1, + &ioc->total_io_cnt), ioc->reply_queue_count) : 0; + + if (scmd && ioc->shost->nr_hw_queues > 1) { + u32 tag = blk_mq_unique_tag(scsi_cmd_to_rq(scmd)); + + return blk_mq_unique_tag_to_hwq(tag) + + ioc->high_iops_queues; + } + return ioc->cpu_msix_table[raw_smp_processor_id()]; } /** + * _base_get_high_iops_msix_index - get the msix index of + * high iops queues + * @ioc: per adapter object + * @scmd: scsi_cmnd object + * + * Return: msix index of high iops reply queues. + * i.e. high iops reply queue on which IO request's + * reply should be posted by the HBA firmware. + */ +static inline u8 +_base_get_high_iops_msix_index(struct MPT3SAS_ADAPTER *ioc, + struct scsi_cmnd *scmd) +{ + /** + * Round robin the IO interrupts among the high iops + * reply queues in terms of batch count 16 when outstanding + * IOs on the target device is >=8. + */ + + if (scsi_device_busy(scmd->device) > MPT3SAS_DEVICE_HIGH_IOPS_DEPTH) + return base_mod64(( + atomic64_add_return(1, &ioc->high_iops_outstanding) / + MPT3SAS_HIGH_IOPS_BATCH_COUNT), + MPT3SAS_HIGH_IOPS_REPLY_QUEUES); + + return _base_get_msix_index(ioc, scmd); +} + +/** * mpt3sas_base_get_smid - obtain a free smid from internal queue * @ioc: per adapter object * @cb_idx: callback index @@ -3204,13 +3965,28 @@ mpt3sas_base_get_smid_scsiio(struct MPT3SAS_ADAPTER *ioc, u8 cb_idx, struct scsi_cmnd *scmd) { struct scsiio_tracker *request = scsi_cmd_priv(scmd); - unsigned int tag = scmd->request->tag; u16 smid; + u32 tag, unique_tag; + + unique_tag = blk_mq_unique_tag(scsi_cmd_to_rq(scmd)); + tag = blk_mq_unique_tag_to_tag(unique_tag); + + /* + * Store hw queue number corresponding to the tag. + * This hw queue number is used later to determine + * the unique_tag using the logic below. This unique_tag + * is used to retrieve the scmd pointer corresponding + * to tag using scsi_host_find_tag() API. + * + * tag = smid - 1; + * unique_tag = ioc->io_queue_num[tag] << BLK_MQ_UNIQUE_TAG_BITS | tag; + */ + ioc->io_queue_num[tag] = blk_mq_unique_tag_to_hwq(unique_tag); smid = tag + 1; request->cb_idx = cb_idx; - request->msix_io = _base_get_msix_index(ioc); request->smid = smid; + request->scmd = scmd; INIT_LIST_HEAD(&request->chain_list); return smid; } @@ -3264,6 +4040,7 @@ void mpt3sas_base_clear_st(struct MPT3SAS_ADAPTER *ioc, return; st->cb_idx = 0xFF; st->direct_io = 0; + st->scmd = NULL; atomic_set(&ioc->chain_lookup[st->smid - 1].chain_offset, 0); st->smid = 0; } @@ -3281,14 +4058,21 @@ mpt3sas_base_free_smid(struct MPT3SAS_ADAPTER *ioc, u16 smid) if (smid < ioc->hi_priority_smid) { struct scsiio_tracker *st; + void *request; st = _get_st_from_smid(ioc, smid); if (!st) { _base_recovery_check(ioc); return; } + + /* Clear MPI request frame */ + request = mpt3sas_base_get_msg_frame(ioc, smid); + memset(request, 0, ioc->request_sz); + mpt3sas_base_clear_st(ioc, st); _base_recovery_check(ioc); + ioc->io_queue_num[smid - 1] = 0; return; } @@ -3327,7 +4111,6 @@ _base_mpi_ep_writeq(__u64 b, volatile void __iomem *addr, spin_lock_irqsave(writeq_lock, flags); __raw_writel((u32)(b), addr); __raw_writel((u32)(b >> 32), (addr + 4)); - mmiowb(); spin_unlock_irqrestore(writeq_lock, flags); } @@ -3358,13 +4141,37 @@ _base_writeq(__u64 b, volatile void __iomem *addr, spinlock_t *writeq_lock) #endif /** + * _base_set_and_get_msix_index - get the msix index and assign to msix_io + * variable of scsi tracker + * @ioc: per adapter object + * @smid: system request message index + * + * Return: msix index. + */ +static u8 +_base_set_and_get_msix_index(struct MPT3SAS_ADAPTER *ioc, u16 smid) +{ + struct scsiio_tracker *st = NULL; + + if (smid < ioc->hi_priority_smid) + st = _get_st_from_smid(ioc, smid); + + if (st == NULL) + return _base_get_msix_index(ioc, NULL); + + st->msix_io = ioc->get_msix_index_for_smlio(ioc, st->scmd); + return st->msix_io; +} + +/** * _base_put_smid_mpi_ep_scsi_io - send SCSI_IO request to firmware * @ioc: per adapter object * @smid: system request message index * @handle: device handle */ static void -_base_put_smid_mpi_ep_scsi_io(struct MPT3SAS_ADAPTER *ioc, u16 smid, u16 handle) +_base_put_smid_mpi_ep_scsi_io(struct MPT3SAS_ADAPTER *ioc, + u16 smid, u16 handle) { Mpi2RequestDescriptorUnion_t descriptor; u64 *request = (u64 *)&descriptor; @@ -3377,7 +4184,7 @@ _base_put_smid_mpi_ep_scsi_io(struct MPT3SAS_ADAPTER *ioc, u16 smid, u16 handle) _base_clone_mpi_to_sys_mem(mpi_req_iomem, (void *)mfp, ioc->request_sz); descriptor.SCSIIO.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO; - descriptor.SCSIIO.MSIxIndex = _base_get_msix_index(ioc); + descriptor.SCSIIO.MSIxIndex = _base_set_and_get_msix_index(ioc, smid); descriptor.SCSIIO.SMID = cpu_to_le16(smid); descriptor.SCSIIO.DevHandle = cpu_to_le16(handle); descriptor.SCSIIO.LMID = 0; @@ -3399,7 +4206,7 @@ _base_put_smid_scsi_io(struct MPT3SAS_ADAPTER *ioc, u16 smid, u16 handle) descriptor.SCSIIO.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO; - descriptor.SCSIIO.MSIxIndex = _base_get_msix_index(ioc); + descriptor.SCSIIO.MSIxIndex = _base_set_and_get_msix_index(ioc, smid); descriptor.SCSIIO.SMID = cpu_to_le16(smid); descriptor.SCSIIO.DevHandle = cpu_to_le16(handle); descriptor.SCSIIO.LMID = 0; @@ -3408,13 +4215,13 @@ _base_put_smid_scsi_io(struct MPT3SAS_ADAPTER *ioc, u16 smid, u16 handle) } /** - * mpt3sas_base_put_smid_fast_path - send fast path request to firmware + * _base_put_smid_fast_path - send fast path request to firmware * @ioc: per adapter object * @smid: system request message index * @handle: device handle */ -void -mpt3sas_base_put_smid_fast_path(struct MPT3SAS_ADAPTER *ioc, u16 smid, +static void +_base_put_smid_fast_path(struct MPT3SAS_ADAPTER *ioc, u16 smid, u16 handle) { Mpi2RequestDescriptorUnion_t descriptor; @@ -3422,7 +4229,7 @@ mpt3sas_base_put_smid_fast_path(struct MPT3SAS_ADAPTER *ioc, u16 smid, descriptor.SCSIIO.RequestFlags = MPI25_REQ_DESCRIPT_FLAGS_FAST_PATH_SCSI_IO; - descriptor.SCSIIO.MSIxIndex = _base_get_msix_index(ioc); + descriptor.SCSIIO.MSIxIndex = _base_set_and_get_msix_index(ioc, smid); descriptor.SCSIIO.SMID = cpu_to_le16(smid); descriptor.SCSIIO.DevHandle = cpu_to_le16(handle); descriptor.SCSIIO.LMID = 0; @@ -3431,13 +4238,13 @@ mpt3sas_base_put_smid_fast_path(struct MPT3SAS_ADAPTER *ioc, u16 smid, } /** - * mpt3sas_base_put_smid_hi_priority - send Task Management request to firmware + * _base_put_smid_hi_priority - send Task Management request to firmware * @ioc: per adapter object * @smid: system request message index - * @msix_task: msix_task will be same as msix of IO incase of task abort else 0. + * @msix_task: msix_task will be same as msix of IO in case of task abort else 0 */ -void -mpt3sas_base_put_smid_hi_priority(struct MPT3SAS_ADAPTER *ioc, u16 smid, +static void +_base_put_smid_hi_priority(struct MPT3SAS_ADAPTER *ioc, u16 smid, u16 msix_task) { Mpi2RequestDescriptorUnion_t descriptor; @@ -3486,7 +4293,7 @@ mpt3sas_base_put_smid_nvme_encap(struct MPT3SAS_ADAPTER *ioc, u16 smid) descriptor.Default.RequestFlags = MPI26_REQ_DESCRIPT_FLAGS_PCIE_ENCAPSULATED; - descriptor.Default.MSIxIndex = _base_get_msix_index(ioc); + descriptor.Default.MSIxIndex = _base_set_and_get_msix_index(ioc, smid); descriptor.Default.SMID = cpu_to_le16(smid); descriptor.Default.LMID = 0; descriptor.Default.DescriptorTypeDependent = 0; @@ -3495,12 +4302,12 @@ mpt3sas_base_put_smid_nvme_encap(struct MPT3SAS_ADAPTER *ioc, u16 smid) } /** - * mpt3sas_base_put_smid_default - Default, primarily used for config pages + * _base_put_smid_default - Default, primarily used for config pages * @ioc: per adapter object * @smid: system request message index */ -void -mpt3sas_base_put_smid_default(struct MPT3SAS_ADAPTER *ioc, u16 smid) +static void +_base_put_smid_default(struct MPT3SAS_ADAPTER *ioc, u16 smid) { Mpi2RequestDescriptorUnion_t descriptor; void *mpi_req_iomem; @@ -3518,7 +4325,7 @@ mpt3sas_base_put_smid_default(struct MPT3SAS_ADAPTER *ioc, u16 smid) } request = (u64 *)&descriptor; descriptor.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; - descriptor.Default.MSIxIndex = _base_get_msix_index(ioc); + descriptor.Default.MSIxIndex = _base_set_and_get_msix_index(ioc, smid); descriptor.Default.SMID = cpu_to_le16(smid); descriptor.Default.LMID = 0; descriptor.Default.DescriptorTypeDependent = 0; @@ -3532,6 +4339,95 @@ mpt3sas_base_put_smid_default(struct MPT3SAS_ADAPTER *ioc, u16 smid) } /** + * _base_put_smid_scsi_io_atomic - send SCSI_IO request to firmware using + * Atomic Request Descriptor + * @ioc: per adapter object + * @smid: system request message index + * @handle: device handle, unused in this function, for function type match + * + * Return: nothing. + */ +static void +_base_put_smid_scsi_io_atomic(struct MPT3SAS_ADAPTER *ioc, u16 smid, + u16 handle) +{ + Mpi26AtomicRequestDescriptor_t descriptor; + u32 *request = (u32 *)&descriptor; + + descriptor.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO; + descriptor.MSIxIndex = _base_set_and_get_msix_index(ioc, smid); + descriptor.SMID = cpu_to_le16(smid); + + writel(cpu_to_le32(*request), &ioc->chip->AtomicRequestDescriptorPost); +} + +/** + * _base_put_smid_fast_path_atomic - send fast path request to firmware + * using Atomic Request Descriptor + * @ioc: per adapter object + * @smid: system request message index + * @handle: device handle, unused in this function, for function type match + * Return: nothing + */ +static void +_base_put_smid_fast_path_atomic(struct MPT3SAS_ADAPTER *ioc, u16 smid, + u16 handle) +{ + Mpi26AtomicRequestDescriptor_t descriptor; + u32 *request = (u32 *)&descriptor; + + descriptor.RequestFlags = MPI25_REQ_DESCRIPT_FLAGS_FAST_PATH_SCSI_IO; + descriptor.MSIxIndex = _base_set_and_get_msix_index(ioc, smid); + descriptor.SMID = cpu_to_le16(smid); + + writel(cpu_to_le32(*request), &ioc->chip->AtomicRequestDescriptorPost); +} + +/** + * _base_put_smid_hi_priority_atomic - send Task Management request to + * firmware using Atomic Request Descriptor + * @ioc: per adapter object + * @smid: system request message index + * @msix_task: msix_task will be same as msix of IO in case of task abort else 0 + * + * Return: nothing. + */ +static void +_base_put_smid_hi_priority_atomic(struct MPT3SAS_ADAPTER *ioc, u16 smid, + u16 msix_task) +{ + Mpi26AtomicRequestDescriptor_t descriptor; + u32 *request = (u32 *)&descriptor; + + descriptor.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY; + descriptor.MSIxIndex = msix_task; + descriptor.SMID = cpu_to_le16(smid); + + writel(cpu_to_le32(*request), &ioc->chip->AtomicRequestDescriptorPost); +} + +/** + * _base_put_smid_default_atomic - Default, primarily used for config pages + * use Atomic Request Descriptor + * @ioc: per adapter object + * @smid: system request message index + * + * Return: nothing. + */ +static void +_base_put_smid_default_atomic(struct MPT3SAS_ADAPTER *ioc, u16 smid) +{ + Mpi26AtomicRequestDescriptor_t descriptor; + u32 *request = (u32 *)&descriptor; + + descriptor.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; + descriptor.MSIxIndex = _base_set_and_get_msix_index(ioc, smid); + descriptor.SMID = cpu_to_le16(smid); + + writel(cpu_to_le32(*request), &ioc->chip->AtomicRequestDescriptorPost); +} + +/** * _base_display_OEMs_branding - Display branding string * @ioc: per adapter object */ @@ -3563,6 +4459,7 @@ _base_display_OEMs_branding(struct MPT3SAS_ADAPTER *ioc) ioc->pdev->subsystem_device); break; } + break; case MPI2_MFGPAGE_DEVID_SAS2308_2: switch (ioc->pdev->subsystem_device) { case MPT2SAS_INTEL_RS25GB008_SSDID: @@ -3598,6 +4495,7 @@ _base_display_OEMs_branding(struct MPT3SAS_ADAPTER *ioc) ioc->pdev->subsystem_device); break; } + break; case MPI25_MFGPAGE_DEVID_SAS3008: switch (ioc->pdev->subsystem_device) { case MPT3SAS_INTEL_RMS3JC080_SSDID: @@ -3742,6 +4640,7 @@ _base_display_OEMs_branding(struct MPT3SAS_ADAPTER *ioc) ioc->pdev->subsystem_device); break; } + break; case MPI2_MFGPAGE_DEVID_SAS2308_2: switch (ioc->pdev->subsystem_device) { case MPT2SAS_HP_2_4_INTERNAL_SSDID: @@ -3765,11 +4664,13 @@ _base_display_OEMs_branding(struct MPT3SAS_ADAPTER *ioc) ioc->pdev->subsystem_device); break; } + break; default: ioc_info(ioc, "HP SAS HBA: Subsystem ID: 0x%X\n", ioc->pdev->subsystem_device); break; } + break; default: break; } @@ -3785,10 +4686,12 @@ _base_display_OEMs_branding(struct MPT3SAS_ADAPTER *ioc) static int _base_display_fwpkg_version(struct MPT3SAS_ADAPTER *ioc) { - Mpi2FWImageHeader_t *FWImgHdr; + Mpi2FWImageHeader_t *fw_img_hdr; + Mpi26ComponentImageHeader_t *cmp_img_hdr; Mpi25FWUploadRequest_t *mpi_request; Mpi2FWUploadReply_t mpi_reply; - int r = 0; + int r = 0, issue_diag_reset = 0; + u32 package_version = 0; void *fwpkg_data = NULL; dma_addr_t fwpkg_data_dma; u16 smid, ioc_status; @@ -3805,7 +4708,8 @@ _base_display_fwpkg_version(struct MPT3SAS_ADAPTER *ioc) fwpkg_data = dma_alloc_coherent(&ioc->pdev->dev, data_length, &fwpkg_data_dma, GFP_KERNEL); if (!fwpkg_data) { - ioc_err(ioc, "failure at %s:%d/%s()!\n", + ioc_err(ioc, + "Memory allocation for fwpkg data failed at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); return -ENOMEM; } @@ -3827,7 +4731,7 @@ _base_display_fwpkg_version(struct MPT3SAS_ADAPTER *ioc) ioc->build_sg(ioc, &mpi_request->SGL, 0, 0, fwpkg_data_dma, data_length); init_completion(&ioc->base_cmds.done); - mpt3sas_base_put_smid_default(ioc, smid); + ioc->put_smid_default(ioc, smid); /* Wait for 15 seconds */ wait_for_completion_timeout(&ioc->base_cmds.done, FW_IMG_HDR_READ_TIMEOUT*HZ); @@ -3836,7 +4740,7 @@ _base_display_fwpkg_version(struct MPT3SAS_ADAPTER *ioc) ioc_err(ioc, "%s: timeout\n", __func__); _debug_dump_mf(mpi_request, sizeof(Mpi25FWUploadRequest_t)/4); - r = -ETIME; + issue_diag_reset = 1; } else { memset(&mpi_reply, 0, sizeof(Mpi2FWUploadReply_t)); if (ioc->base_cmds.status & MPT3_CMD_REPLY_VALID) { @@ -3845,14 +4749,26 @@ _base_display_fwpkg_version(struct MPT3SAS_ADAPTER *ioc) ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; if (ioc_status == MPI2_IOCSTATUS_SUCCESS) { - FWImgHdr = (Mpi2FWImageHeader_t *)fwpkg_data; - if (FWImgHdr->PackageVersion.Word) { - ioc_info(ioc, "FW Package Version (%02d.%02d.%02d.%02d)\n", - FWImgHdr->PackageVersion.Struct.Major, - FWImgHdr->PackageVersion.Struct.Minor, - FWImgHdr->PackageVersion.Struct.Unit, - FWImgHdr->PackageVersion.Struct.Dev); - } + fw_img_hdr = (Mpi2FWImageHeader_t *)fwpkg_data; + if (le32_to_cpu(fw_img_hdr->Signature) == + MPI26_IMAGE_HEADER_SIGNATURE0_MPI26) { + cmp_img_hdr = + (Mpi26ComponentImageHeader_t *) + (fwpkg_data); + package_version = + le32_to_cpu( + cmp_img_hdr->ApplicationSpecific); + } else + package_version = + le32_to_cpu( + fw_img_hdr->PackageVersion.Word); + if (package_version) + ioc_info(ioc, + "FW Package Ver(%02d.%02d.%02d.%02d)\n", + ((package_version) & 0xFF000000) >> 24, + ((package_version) & 0x00FF0000) >> 16, + ((package_version) & 0x0000FF00) >> 8, + (package_version) & 0x000000FF); } else { _debug_dump_mf(&mpi_reply, sizeof(Mpi2FWUploadReply_t)/4); @@ -3864,34 +4780,35 @@ out: if (fwpkg_data) dma_free_coherent(&ioc->pdev->dev, data_length, fwpkg_data, fwpkg_data_dma); + if (issue_diag_reset) { + if (ioc->drv_internal_flags & MPT_DRV_INTERNAL_FIRST_PE_ISSUED) + return -EFAULT; + if (mpt3sas_base_check_for_fault_and_issue_reset(ioc)) + return -EFAULT; + r = -EAGAIN; + } return r; } /** - * _base_display_ioc_capabilities - Disply IOC's capabilities. + * _base_display_ioc_capabilities - Display IOC's capabilities. * @ioc: per adapter object */ static void _base_display_ioc_capabilities(struct MPT3SAS_ADAPTER *ioc) { int i = 0; - char desc[16]; + char desc[17] = {0}; u32 iounit_pg1_flags; - u32 bios_version; - bios_version = le32_to_cpu(ioc->bios_pg3.BiosVersion); - strncpy(desc, ioc->manu_pg0.ChipName, 16); - ioc_info(ioc, "%s: FWVersion(%02d.%02d.%02d.%02d), ChipRevision(0x%02x), BiosVersion(%02d.%02d.%02d.%02d)\n", + memtostr(desc, ioc->manu_pg0.ChipName); + ioc_info(ioc, "%s: FWVersion(%02d.%02d.%02d.%02d), ChipRevision(0x%02x)\n", desc, (ioc->facts.FWVersion.Word & 0xFF000000) >> 24, (ioc->facts.FWVersion.Word & 0x00FF0000) >> 16, (ioc->facts.FWVersion.Word & 0x0000FF00) >> 8, ioc->facts.FWVersion.Word & 0x000000FF, - ioc->pdev->revision, - (bios_version & 0xFF000000) >> 24, - (bios_version & 0x00FF0000) >> 16, - (bios_version & 0x0000FF00) >> 8, - bios_version & 0x000000FF); + ioc->pdev->revision); _base_display_OEMs_branding(ioc); @@ -3968,6 +4885,12 @@ _base_display_ioc_capabilities(struct MPT3SAS_ADAPTER *ioc) i++; } + if (ioc->facts.IOCCapabilities & + MPI26_IOCFACTS_CAPABILITY_MCTP_PASSTHRU) { + pr_cont("%sMCTP Passthru", i ? "," : ""); + i++; + } + iounit_pg1_flags = le32_to_cpu(ioc->iounit_pg1.Flags); if (!(iounit_pg1_flags & MPI2_IOUNITPAGE1_NATIVE_COMMAND_Q_DISABLE)) { pr_cont("%sNCQ", i ? "," : ""); @@ -4003,8 +4926,7 @@ mpt3sas_base_update_missing_delay(struct MPT3SAS_ADAPTER *ioc, if (!num_phys) return; - sz = offsetof(Mpi2SasIOUnitPage1_t, PhyData) + (num_phys * - sizeof(Mpi2SasIOUnit1PhyData_t)); + sz = struct_size(sas_iounit_pg1, PhyData, num_phys); sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL); if (!sas_iounit_pg1) { ioc_err(ioc, "failure at %s:%d/%s()!\n", @@ -4067,31 +4989,664 @@ out: } /** + * _base_update_ioc_page1_inlinewith_perf_mode - Update IOC Page1 fields + * according to performance mode. + * @ioc : per adapter object + * + * Return: zero on success; otherwise return EAGAIN error code asking the + * caller to retry. + */ +static int +_base_update_ioc_page1_inlinewith_perf_mode(struct MPT3SAS_ADAPTER *ioc) +{ + Mpi2IOCPage1_t ioc_pg1; + Mpi2ConfigReply_t mpi_reply; + int rc; + + rc = mpt3sas_config_get_ioc_pg1(ioc, &mpi_reply, &ioc->ioc_pg1_copy); + if (rc) + return rc; + memcpy(&ioc_pg1, &ioc->ioc_pg1_copy, sizeof(Mpi2IOCPage1_t)); + + switch (perf_mode) { + case MPT_PERF_MODE_DEFAULT: + case MPT_PERF_MODE_BALANCED: + if (ioc->high_iops_queues) { + ioc_info(ioc, + "Enable interrupt coalescing only for first\t" + "%d reply queues\n", + MPT3SAS_HIGH_IOPS_REPLY_QUEUES); + /* + * If 31st bit is zero then interrupt coalescing is + * enabled for all reply descriptor post queues. + * If 31st bit is set to one then user can + * enable/disable interrupt coalescing on per reply + * descriptor post queue group(8) basis. So to enable + * interrupt coalescing only on first reply descriptor + * post queue group 31st bit and zero th bit is enabled. + */ + ioc_pg1.ProductSpecific = cpu_to_le32(0x80000000 | + ((1 << MPT3SAS_HIGH_IOPS_REPLY_QUEUES/8) - 1)); + rc = mpt3sas_config_set_ioc_pg1(ioc, &mpi_reply, &ioc_pg1); + if (rc) + return rc; + ioc_info(ioc, "performance mode: balanced\n"); + return 0; + } + fallthrough; + case MPT_PERF_MODE_LATENCY: + /* + * Enable interrupt coalescing on all reply queues + * with timeout value 0xA + */ + ioc_pg1.CoalescingTimeout = cpu_to_le32(0xa); + ioc_pg1.Flags |= cpu_to_le32(MPI2_IOCPAGE1_REPLY_COALESCING); + ioc_pg1.ProductSpecific = 0; + rc = mpt3sas_config_set_ioc_pg1(ioc, &mpi_reply, &ioc_pg1); + if (rc) + return rc; + ioc_info(ioc, "performance mode: latency\n"); + break; + case MPT_PERF_MODE_IOPS: + /* + * Enable interrupt coalescing on all reply queues. + */ + ioc_info(ioc, + "performance mode: iops with coalescing timeout: 0x%x\n", + le32_to_cpu(ioc_pg1.CoalescingTimeout)); + ioc_pg1.Flags |= cpu_to_le32(MPI2_IOCPAGE1_REPLY_COALESCING); + ioc_pg1.ProductSpecific = 0; + rc = mpt3sas_config_set_ioc_pg1(ioc, &mpi_reply, &ioc_pg1); + if (rc) + return rc; + break; + } + return 0; +} + +/** + * _base_get_event_diag_triggers - get event diag trigger values from + * persistent pages + * @ioc : per adapter object + * + * Return: nothing. + */ +static int +_base_get_event_diag_triggers(struct MPT3SAS_ADAPTER *ioc) +{ + Mpi26DriverTriggerPage2_t trigger_pg2; + struct SL_WH_EVENT_TRIGGER_T *event_tg; + MPI26_DRIVER_MPI_EVENT_TRIGGER_ENTRY *mpi_event_tg; + Mpi2ConfigReply_t mpi_reply; + int r = 0, i = 0; + u16 count = 0; + u16 ioc_status; + + r = mpt3sas_config_get_driver_trigger_pg2(ioc, &mpi_reply, + &trigger_pg2); + if (r) + return r; + + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + dinitprintk(ioc, + ioc_err(ioc, + "%s: Failed to get trigger pg2, ioc_status(0x%04x)\n", + __func__, ioc_status)); + return 0; + } + + if (le16_to_cpu(trigger_pg2.NumMPIEventTrigger)) { + count = le16_to_cpu(trigger_pg2.NumMPIEventTrigger); + count = min_t(u16, NUM_VALID_ENTRIES, count); + ioc->diag_trigger_event.ValidEntries = count; + + event_tg = &ioc->diag_trigger_event.EventTriggerEntry[0]; + mpi_event_tg = &trigger_pg2.MPIEventTriggers[0]; + for (i = 0; i < count; i++) { + event_tg->EventValue = le16_to_cpu( + mpi_event_tg->MPIEventCode); + event_tg->LogEntryQualifier = le16_to_cpu( + mpi_event_tg->MPIEventCodeSpecific); + event_tg++; + mpi_event_tg++; + } + } + return 0; +} + +/** + * _base_get_scsi_diag_triggers - get scsi diag trigger values from + * persistent pages + * @ioc : per adapter object + * + * Return: 0 on success; otherwise return failure status. + */ +static int +_base_get_scsi_diag_triggers(struct MPT3SAS_ADAPTER *ioc) +{ + Mpi26DriverTriggerPage3_t trigger_pg3; + struct SL_WH_SCSI_TRIGGER_T *scsi_tg; + MPI26_DRIVER_SCSI_SENSE_TRIGGER_ENTRY *mpi_scsi_tg; + Mpi2ConfigReply_t mpi_reply; + int r = 0, i = 0; + u16 count = 0; + u16 ioc_status; + + r = mpt3sas_config_get_driver_trigger_pg3(ioc, &mpi_reply, + &trigger_pg3); + if (r) + return r; + + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + dinitprintk(ioc, + ioc_err(ioc, + "%s: Failed to get trigger pg3, ioc_status(0x%04x)\n", + __func__, ioc_status)); + return 0; + } + + if (le16_to_cpu(trigger_pg3.NumSCSISenseTrigger)) { + count = le16_to_cpu(trigger_pg3.NumSCSISenseTrigger); + count = min_t(u16, NUM_VALID_ENTRIES, count); + ioc->diag_trigger_scsi.ValidEntries = count; + + scsi_tg = &ioc->diag_trigger_scsi.SCSITriggerEntry[0]; + mpi_scsi_tg = &trigger_pg3.SCSISenseTriggers[0]; + for (i = 0; i < count; i++) { + scsi_tg->ASCQ = mpi_scsi_tg->ASCQ; + scsi_tg->ASC = mpi_scsi_tg->ASC; + scsi_tg->SenseKey = mpi_scsi_tg->SenseKey; + + scsi_tg++; + mpi_scsi_tg++; + } + } + return 0; +} + +/** + * _base_get_mpi_diag_triggers - get mpi diag trigger values from + * persistent pages + * @ioc : per adapter object + * + * Return: 0 on success; otherwise return failure status. + */ +static int +_base_get_mpi_diag_triggers(struct MPT3SAS_ADAPTER *ioc) +{ + Mpi26DriverTriggerPage4_t trigger_pg4; + struct SL_WH_MPI_TRIGGER_T *status_tg; + MPI26_DRIVER_IOCSTATUS_LOGINFO_TRIGGER_ENTRY *mpi_status_tg; + Mpi2ConfigReply_t mpi_reply; + int r = 0, i = 0; + u16 count = 0; + u16 ioc_status; + + r = mpt3sas_config_get_driver_trigger_pg4(ioc, &mpi_reply, + &trigger_pg4); + if (r) + return r; + + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + dinitprintk(ioc, + ioc_err(ioc, + "%s: Failed to get trigger pg4, ioc_status(0x%04x)\n", + __func__, ioc_status)); + return 0; + } + + if (le16_to_cpu(trigger_pg4.NumIOCStatusLogInfoTrigger)) { + count = le16_to_cpu(trigger_pg4.NumIOCStatusLogInfoTrigger); + count = min_t(u16, NUM_VALID_ENTRIES, count); + ioc->diag_trigger_mpi.ValidEntries = count; + + status_tg = &ioc->diag_trigger_mpi.MPITriggerEntry[0]; + mpi_status_tg = &trigger_pg4.IOCStatusLoginfoTriggers[0]; + + for (i = 0; i < count; i++) { + status_tg->IOCStatus = le16_to_cpu( + mpi_status_tg->IOCStatus); + status_tg->IocLogInfo = le32_to_cpu( + mpi_status_tg->LogInfo); + + status_tg++; + mpi_status_tg++; + } + } + return 0; +} + +/** + * _base_get_master_diag_triggers - get master diag trigger values from + * persistent pages + * @ioc : per adapter object + * + * Return: nothing. + */ +static int +_base_get_master_diag_triggers(struct MPT3SAS_ADAPTER *ioc) +{ + Mpi26DriverTriggerPage1_t trigger_pg1; + Mpi2ConfigReply_t mpi_reply; + int r; + u16 ioc_status; + + r = mpt3sas_config_get_driver_trigger_pg1(ioc, &mpi_reply, + &trigger_pg1); + if (r) + return r; + + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + dinitprintk(ioc, + ioc_err(ioc, + "%s: Failed to get trigger pg1, ioc_status(0x%04x)\n", + __func__, ioc_status)); + return 0; + } + + if (le16_to_cpu(trigger_pg1.NumMasterTrigger)) + ioc->diag_trigger_master.MasterData |= + le32_to_cpu( + trigger_pg1.MasterTriggers[0].MasterTriggerFlags); + return 0; +} + +/** + * _base_check_for_trigger_pages_support - checks whether HBA FW supports + * driver trigger pages or not + * @ioc : per adapter object + * @trigger_flags : address where trigger page0's TriggerFlags value is copied + * + * Return: trigger flags mask if HBA FW supports driver trigger pages; + * otherwise returns %-EFAULT if driver trigger pages are not supported by FW or + * return EAGAIN if diag reset occurred due to FW fault and asking the + * caller to retry the command. + * + */ +static int +_base_check_for_trigger_pages_support(struct MPT3SAS_ADAPTER *ioc, u32 *trigger_flags) +{ + Mpi26DriverTriggerPage0_t trigger_pg0; + int r = 0; + Mpi2ConfigReply_t mpi_reply; + u16 ioc_status; + + r = mpt3sas_config_get_driver_trigger_pg0(ioc, &mpi_reply, + &trigger_pg0); + if (r) + return r; + + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) + return -EFAULT; + + *trigger_flags = le16_to_cpu(trigger_pg0.TriggerFlags); + return 0; +} + +/** + * _base_get_diag_triggers - Retrieve diag trigger values from + * persistent pages. + * @ioc : per adapter object + * + * Return: zero on success; otherwise return EAGAIN error codes + * asking the caller to retry. + */ +static int +_base_get_diag_triggers(struct MPT3SAS_ADAPTER *ioc) +{ + int trigger_flags; + int r; + + /* + * Default setting of master trigger. + */ + ioc->diag_trigger_master.MasterData = + (MASTER_TRIGGER_FW_FAULT + MASTER_TRIGGER_ADAPTER_RESET); + + r = _base_check_for_trigger_pages_support(ioc, &trigger_flags); + if (r) { + if (r == -EAGAIN) + return r; + /* + * Don't go for error handling when FW doesn't support + * driver trigger pages. + */ + return 0; + } + + ioc->supports_trigger_pages = 1; + + /* + * Retrieve master diag trigger values from driver trigger pg1 + * if master trigger bit enabled in TriggerFlags. + */ + if ((u16)trigger_flags & + MPI26_DRIVER_TRIGGER0_FLAG_MASTER_TRIGGER_VALID) { + r = _base_get_master_diag_triggers(ioc); + if (r) + return r; + } + + /* + * Retrieve event diag trigger values from driver trigger pg2 + * if event trigger bit enabled in TriggerFlags. + */ + if ((u16)trigger_flags & + MPI26_DRIVER_TRIGGER0_FLAG_MPI_EVENT_TRIGGER_VALID) { + r = _base_get_event_diag_triggers(ioc); + if (r) + return r; + } + + /* + * Retrieve scsi diag trigger values from driver trigger pg3 + * if scsi trigger bit enabled in TriggerFlags. + */ + if ((u16)trigger_flags & + MPI26_DRIVER_TRIGGER0_FLAG_SCSI_SENSE_TRIGGER_VALID) { + r = _base_get_scsi_diag_triggers(ioc); + if (r) + return r; + } + /* + * Retrieve mpi error diag trigger values from driver trigger pg4 + * if loginfo trigger bit enabled in TriggerFlags. + */ + if ((u16)trigger_flags & + MPI26_DRIVER_TRIGGER0_FLAG_LOGINFO_TRIGGER_VALID) { + r = _base_get_mpi_diag_triggers(ioc); + if (r) + return r; + } + return 0; +} + +/** + * _base_update_diag_trigger_pages - Update the driver trigger pages after + * online FW update, in case updated FW supports driver + * trigger pages. + * @ioc : per adapter object + * + * Return: nothing. + */ +static void +_base_update_diag_trigger_pages(struct MPT3SAS_ADAPTER *ioc) +{ + + if (ioc->diag_trigger_master.MasterData) + mpt3sas_config_update_driver_trigger_pg1(ioc, + &ioc->diag_trigger_master, 1); + + if (ioc->diag_trigger_event.ValidEntries) + mpt3sas_config_update_driver_trigger_pg2(ioc, + &ioc->diag_trigger_event, 1); + + if (ioc->diag_trigger_scsi.ValidEntries) + mpt3sas_config_update_driver_trigger_pg3(ioc, + &ioc->diag_trigger_scsi, 1); + + if (ioc->diag_trigger_mpi.ValidEntries) + mpt3sas_config_update_driver_trigger_pg4(ioc, + &ioc->diag_trigger_mpi, 1); +} + +/** + * _base_assign_fw_reported_qd - Get FW reported QD for SAS/SATA devices. + * - On failure set default QD values. + * @ioc : per adapter object + * + * Returns 0 for success, non-zero for failure. + * + */ +static int _base_assign_fw_reported_qd(struct MPT3SAS_ADAPTER *ioc) +{ + Mpi2ConfigReply_t mpi_reply; + Mpi2SasIOUnitPage1_t sas_iounit_pg1; + Mpi26PCIeIOUnitPage1_t pcie_iounit_pg1; + u16 depth; + int rc = 0; + + ioc->max_wideport_qd = MPT3SAS_SAS_QUEUE_DEPTH; + ioc->max_narrowport_qd = MPT3SAS_SAS_QUEUE_DEPTH; + ioc->max_sata_qd = MPT3SAS_SATA_QUEUE_DEPTH; + ioc->max_nvme_qd = MPT3SAS_NVME_QUEUE_DEPTH; + if (!ioc->is_gen35_ioc) + goto out; + /* sas iounit page 1 */ + rc = mpt3sas_config_get_sas_iounit_pg1(ioc, &mpi_reply, + &sas_iounit_pg1, sizeof(Mpi2SasIOUnitPage1_t)); + if (rc) { + pr_err("%s: failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + goto out; + } + + depth = le16_to_cpu(sas_iounit_pg1.SASWideMaxQueueDepth); + ioc->max_wideport_qd = (depth ? depth : MPT3SAS_SAS_QUEUE_DEPTH); + + depth = le16_to_cpu(sas_iounit_pg1.SASNarrowMaxQueueDepth); + ioc->max_narrowport_qd = (depth ? depth : MPT3SAS_SAS_QUEUE_DEPTH); + + depth = sas_iounit_pg1.SATAMaxQDepth; + ioc->max_sata_qd = (depth ? depth : MPT3SAS_SATA_QUEUE_DEPTH); + + /* pcie iounit page 1 */ + rc = mpt3sas_config_get_pcie_iounit_pg1(ioc, &mpi_reply, + &pcie_iounit_pg1, sizeof(Mpi26PCIeIOUnitPage1_t)); + if (rc) { + pr_err("%s: failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + goto out; + } + ioc->max_nvme_qd = (le16_to_cpu(pcie_iounit_pg1.NVMeMaxQueueDepth)) ? + (le16_to_cpu(pcie_iounit_pg1.NVMeMaxQueueDepth)) : + MPT3SAS_NVME_QUEUE_DEPTH; +out: + dinitprintk(ioc, pr_err( + "MaxWidePortQD: 0x%x MaxNarrowPortQD: 0x%x MaxSataQD: 0x%x MaxNvmeQD: 0x%x\n", + ioc->max_wideport_qd, ioc->max_narrowport_qd, + ioc->max_sata_qd, ioc->max_nvme_qd)); + return rc; +} + +/** + * mpt3sas_atto_validate_nvram - validate the ATTO nvram read from mfg pg1 + * + * @ioc : per adapter object + * @n : ptr to the ATTO nvram structure + * Return: 0 for success, non-zero for failure. + */ +static int +mpt3sas_atto_validate_nvram(struct MPT3SAS_ADAPTER *ioc, + struct ATTO_SAS_NVRAM *n) +{ + int r = -EINVAL; + union ATTO_SAS_ADDRESS *s1; + u32 len; + u8 *pb; + u8 ckSum; + + /* validate nvram checksum */ + pb = (u8 *) n; + ckSum = ATTO_SASNVR_CKSUM_SEED; + len = sizeof(struct ATTO_SAS_NVRAM); + + while (len--) + ckSum = ckSum + pb[len]; + + if (ckSum) { + ioc_err(ioc, "Invalid ATTO NVRAM checksum\n"); + return r; + } + + s1 = (union ATTO_SAS_ADDRESS *) n->SasAddr; + + if (n->Signature[0] != 'E' + || n->Signature[1] != 'S' + || n->Signature[2] != 'A' + || n->Signature[3] != 'S') + ioc_err(ioc, "Invalid ATTO NVRAM signature\n"); + else if (n->Version > ATTO_SASNVR_VERSION) + ioc_info(ioc, "Invalid ATTO NVRAM version"); + else if ((n->SasAddr[7] & (ATTO_SAS_ADDR_ALIGN - 1)) + || s1->b[0] != 0x50 + || s1->b[1] != 0x01 + || s1->b[2] != 0x08 + || (s1->b[3] & 0xF0) != 0x60 + || ((s1->b[3] & 0x0F) | le32_to_cpu(s1->d[1])) == 0) { + ioc_err(ioc, "Invalid ATTO SAS address\n"); + } else + r = 0; + return r; +} + +/** + * mpt3sas_atto_get_sas_addr - get the ATTO SAS address from mfg page 1 + * + * @ioc : per adapter object + * @sas_addr : return sas address + * Return: 0 for success, non-zero for failure. + */ +static int +mpt3sas_atto_get_sas_addr(struct MPT3SAS_ADAPTER *ioc, union ATTO_SAS_ADDRESS *sas_addr) +{ + Mpi2ManufacturingPage1_t mfg_pg1; + Mpi2ConfigReply_t mpi_reply; + struct ATTO_SAS_NVRAM *nvram; + int r; + __be64 addr; + + r = mpt3sas_config_get_manufacturing_pg1(ioc, &mpi_reply, &mfg_pg1); + if (r) { + ioc_err(ioc, "Failed to read manufacturing page 1\n"); + return r; + } + + /* validate nvram */ + nvram = (struct ATTO_SAS_NVRAM *) mfg_pg1.VPD; + r = mpt3sas_atto_validate_nvram(ioc, nvram); + if (r) + return r; + + addr = *((__be64 *) nvram->SasAddr); + sas_addr->q = cpu_to_le64(be64_to_cpu(addr)); + return r; +} + +/** + * mpt3sas_atto_init - perform initializaion for ATTO branded + * adapter. + * @ioc : per adapter object + *5 + * Return: 0 for success, non-zero for failure. + */ +static int +mpt3sas_atto_init(struct MPT3SAS_ADAPTER *ioc) +{ + int sz = 0; + Mpi2BiosPage4_t *bios_pg4 = NULL; + Mpi2ConfigReply_t mpi_reply; + int r; + int ix; + union ATTO_SAS_ADDRESS sas_addr; + union ATTO_SAS_ADDRESS temp; + union ATTO_SAS_ADDRESS bias; + + r = mpt3sas_atto_get_sas_addr(ioc, &sas_addr); + if (r) + return r; + + /* get header first to get size */ + r = mpt3sas_config_get_bios_pg4(ioc, &mpi_reply, NULL, 0); + if (r) { + ioc_err(ioc, "Failed to read ATTO bios page 4 header.\n"); + return r; + } + + sz = mpi_reply.Header.PageLength * sizeof(u32); + bios_pg4 = kzalloc(sz, GFP_KERNEL); + if (!bios_pg4) { + ioc_err(ioc, "Failed to allocate memory for ATTO bios page.\n"); + return -ENOMEM; + } + + /* read bios page 4 */ + r = mpt3sas_config_get_bios_pg4(ioc, &mpi_reply, bios_pg4, sz); + if (r) { + ioc_err(ioc, "Failed to read ATTO bios page 4\n"); + goto out; + } + + /* Update bios page 4 with the ATTO WWID */ + bias.q = sas_addr.q; + bias.b[7] += ATTO_SAS_ADDR_DEVNAME_BIAS; + + for (ix = 0; ix < bios_pg4->NumPhys; ix++) { + temp.q = sas_addr.q; + temp.b[7] += ix; + bios_pg4->Phy[ix].ReassignmentWWID = temp.q; + bios_pg4->Phy[ix].ReassignmentDeviceName = bias.q; + } + r = mpt3sas_config_set_bios_pg4(ioc, &mpi_reply, bios_pg4, sz); + +out: + kfree(bios_pg4); + return r; +} + +/** * _base_static_config_pages - static start of day config pages * @ioc: per adapter object */ -static void +static int _base_static_config_pages(struct MPT3SAS_ADAPTER *ioc) { + Mpi2IOUnitPage8_t iounit_pg8; Mpi2ConfigReply_t mpi_reply; u32 iounit_pg1_flags; - + int tg_flags = 0; + int rc; ioc->nvme_abort_timeout = 30; - mpt3sas_config_get_manufacturing_pg0(ioc, &mpi_reply, &ioc->manu_pg0); - if (ioc->ir_firmware) - mpt3sas_config_get_manufacturing_pg10(ioc, &mpi_reply, + + rc = mpt3sas_config_get_manufacturing_pg0(ioc, &mpi_reply, + &ioc->manu_pg0); + if (rc) + return rc; + if (ioc->ir_firmware) { + rc = mpt3sas_config_get_manufacturing_pg10(ioc, &mpi_reply, &ioc->manu_pg10); + if (rc) + return rc; + } + + if (ioc->pdev->vendor == MPI2_MFGPAGE_VENDORID_ATTO) { + rc = mpt3sas_atto_init(ioc); + if (rc) + return rc; + } /* * Ensure correct T10 PI operation if vendor left EEDPTagMode * flag unset in NVDATA. */ - mpt3sas_config_get_manufacturing_pg11(ioc, &mpi_reply, &ioc->manu_pg11); + rc = mpt3sas_config_get_manufacturing_pg11(ioc, &mpi_reply, + &ioc->manu_pg11); + if (rc) + return rc; if (!ioc->is_gen35_ioc && ioc->manu_pg11.EEDPTagMode == 0) { - pr_err("%s: overriding NVDATA EEDPTagMode setting\n", + pr_err("%s: overriding NVDATA EEDPTagMode setting from 0 to 1\n", ioc->name); - ioc->manu_pg11.EEDPTagMode &= ~0x3; - ioc->manu_pg11.EEDPTagMode |= 0x1; + ioc->manu_pg11.EEDPTagMode = 0x1; mpt3sas_config_set_manufacturing_pg11(ioc, &mpi_reply, &ioc->manu_pg11); } @@ -4107,13 +5662,54 @@ _base_static_config_pages(struct MPT3SAS_ADAPTER *ioc) else ioc->nvme_abort_timeout = ioc->manu_pg11.NVMeAbortTO; } + ioc->time_sync_interval = + ioc->manu_pg11.TimeSyncInterval & MPT3SAS_TIMESYNC_MASK; + if (ioc->time_sync_interval) { + if (ioc->manu_pg11.TimeSyncInterval & MPT3SAS_TIMESYNC_UNIT_MASK) + ioc->time_sync_interval = + ioc->time_sync_interval * SECONDS_PER_HOUR; + else + ioc->time_sync_interval = + ioc->time_sync_interval * SECONDS_PER_MIN; + dinitprintk(ioc, ioc_info(ioc, + "Driver-FW TimeSync interval is %d seconds. ManuPg11 TimeSync Unit is in %s\n", + ioc->time_sync_interval, (ioc->manu_pg11.TimeSyncInterval & + MPT3SAS_TIMESYNC_UNIT_MASK) ? "Hour" : "Minute")); + } else { + if (ioc->is_gen35_ioc) + ioc_warn(ioc, + "TimeSync Interval in Manuf page-11 is not enabled. Periodic Time-Sync will be disabled\n"); + } + rc = _base_assign_fw_reported_qd(ioc); + if (rc) + return rc; + + /* + * ATTO doesn't use bios page 2 and 3 for bios settings. + */ + if (ioc->pdev->vendor == MPI2_MFGPAGE_VENDORID_ATTO) + ioc->bios_pg3.BiosVersion = 0; + else { + rc = mpt3sas_config_get_bios_pg2(ioc, &mpi_reply, &ioc->bios_pg2); + if (rc) + return rc; + rc = mpt3sas_config_get_bios_pg3(ioc, &mpi_reply, &ioc->bios_pg3); + if (rc) + return rc; + } - mpt3sas_config_get_bios_pg2(ioc, &mpi_reply, &ioc->bios_pg2); - mpt3sas_config_get_bios_pg3(ioc, &mpi_reply, &ioc->bios_pg3); - mpt3sas_config_get_ioc_pg8(ioc, &mpi_reply, &ioc->ioc_pg8); - mpt3sas_config_get_iounit_pg0(ioc, &mpi_reply, &ioc->iounit_pg0); - mpt3sas_config_get_iounit_pg1(ioc, &mpi_reply, &ioc->iounit_pg1); - mpt3sas_config_get_iounit_pg8(ioc, &mpi_reply, &ioc->iounit_pg8); + rc = mpt3sas_config_get_ioc_pg8(ioc, &mpi_reply, &ioc->ioc_pg8); + if (rc) + return rc; + rc = mpt3sas_config_get_iounit_pg0(ioc, &mpi_reply, &ioc->iounit_pg0); + if (rc) + return rc; + rc = mpt3sas_config_get_iounit_pg1(ioc, &mpi_reply, &ioc->iounit_pg1); + if (rc) + return rc; + rc = mpt3sas_config_get_iounit_pg8(ioc, &mpi_reply, &iounit_pg8); + if (rc) + return rc; _base_display_ioc_capabilities(ioc); /* @@ -4129,17 +5725,50 @@ _base_static_config_pages(struct MPT3SAS_ADAPTER *ioc) iounit_pg1_flags |= MPI2_IOUNITPAGE1_DISABLE_TASK_SET_FULL_HANDLING; ioc->iounit_pg1.Flags = cpu_to_le32(iounit_pg1_flags); - mpt3sas_config_set_iounit_pg1(ioc, &mpi_reply, &ioc->iounit_pg1); + rc = mpt3sas_config_set_iounit_pg1(ioc, &mpi_reply, &ioc->iounit_pg1); + if (rc) + return rc; - if (ioc->iounit_pg8.NumSensors) - ioc->temp_sensors_count = ioc->iounit_pg8.NumSensors; + if (iounit_pg8.NumSensors) + ioc->temp_sensors_count = iounit_pg8.NumSensors; + if (ioc->is_aero_ioc) { + rc = _base_update_ioc_page1_inlinewith_perf_mode(ioc); + if (rc) + return rc; + } + if (ioc->is_gen35_ioc) { + if (ioc->is_driver_loading) { + rc = _base_get_diag_triggers(ioc); + if (rc) + return rc; + } else { + /* + * In case of online HBA FW update operation, + * check whether updated FW supports the driver trigger + * pages or not. + * - If previous FW has not supported driver trigger + * pages and newer FW supports them then update these + * pages with current diag trigger values. + * - If previous FW has supported driver trigger pages + * and new FW doesn't support them then disable + * support_trigger_pages flag. + */ + _base_check_for_trigger_pages_support(ioc, &tg_flags); + if (!ioc->supports_trigger_pages && tg_flags != -EFAULT) + _base_update_diag_trigger_pages(ioc); + else if (ioc->supports_trigger_pages && + tg_flags == -EFAULT) + ioc->supports_trigger_pages = 0; + } + } + return 0; } /** * mpt3sas_free_enclosure_list - release memory * @ioc: per adapter object * - * Free memory allocated during encloure add. + * Free memory allocated during enclosure add. */ void mpt3sas_free_enclosure_list(struct MPT3SAS_ADAPTER *ioc) @@ -4165,8 +5794,9 @@ _base_release_memory_pools(struct MPT3SAS_ADAPTER *ioc) { int i = 0; int j = 0; + int dma_alloc_count = 0; struct chain_tracker *ct; - struct reply_post_struct *rps; + int count = ioc->rdpq_array_enable ? ioc->reply_queue_count : 1; dexitprintk(ioc, ioc_info(ioc, "%s\n", __func__)); @@ -4208,29 +5838,34 @@ _base_release_memory_pools(struct MPT3SAS_ADAPTER *ioc) } if (ioc->reply_post) { - do { - rps = &ioc->reply_post[i]; - if (rps->reply_post_free) { - dma_pool_free( - ioc->reply_post_free_dma_pool, - rps->reply_post_free, - rps->reply_post_free_dma); - dexitprintk(ioc, - ioc_info(ioc, "reply_post_free_pool(0x%p): free\n", - rps->reply_post_free)); - rps->reply_post_free = NULL; + dma_alloc_count = DIV_ROUND_UP(count, + RDPQ_MAX_INDEX_IN_ONE_CHUNK); + for (i = 0; i < count; i++) { + if (i % RDPQ_MAX_INDEX_IN_ONE_CHUNK == 0 + && dma_alloc_count) { + if (ioc->reply_post[i].reply_post_free) { + dma_pool_free( + ioc->reply_post_free_dma_pool, + ioc->reply_post[i].reply_post_free, + ioc->reply_post[i].reply_post_free_dma); + dexitprintk(ioc, ioc_info(ioc, + "reply_post_free_pool(0x%p): free\n", + ioc->reply_post[i].reply_post_free)); + ioc->reply_post[i].reply_post_free = + NULL; + } + --dma_alloc_count; } - } while (ioc->rdpq_array_enable && - (++i < ioc->reply_queue_count)); + } + dma_pool_destroy(ioc->reply_post_free_dma_pool); if (ioc->reply_post_free_array && ioc->rdpq_array_enable) { dma_pool_free(ioc->reply_post_free_array_dma_pool, - ioc->reply_post_free_array, - ioc->reply_post_free_array_dma); + ioc->reply_post_free_array, + ioc->reply_post_free_array_dma); ioc->reply_post_free_array = NULL; } dma_pool_destroy(ioc->reply_post_free_array_dma_pool); - dma_pool_destroy(ioc->reply_post_free_dma_pool); kfree(ioc->reply_post); } @@ -4239,10 +5874,12 @@ _base_release_memory_pools(struct MPT3SAS_ADAPTER *ioc) dma_pool_free(ioc->pcie_sgl_dma_pool, ioc->pcie_sg_lookup[i].pcie_sgl, ioc->pcie_sg_lookup[i].pcie_sgl_dma); + ioc->pcie_sg_lookup[i].pcie_sgl = NULL; } - if (ioc->pcie_sgl_dma_pool) - dma_pool_destroy(ioc->pcie_sgl_dma_pool); + dma_pool_destroy(ioc->pcie_sgl_dma_pool); } + kfree(ioc->pcie_sg_lookup); + ioc->pcie_sg_lookup = NULL; if (ioc->config_page) { dexitprintk(ioc, @@ -4253,7 +5890,9 @@ _base_release_memory_pools(struct MPT3SAS_ADAPTER *ioc) } kfree(ioc->hpr_lookup); + ioc->hpr_lookup = NULL; kfree(ioc->internal_lookup); + ioc->internal_lookup = NULL; if (ioc->chain_lookup) { for (i = 0; i < ioc->scsiio_depth; i++) { for (j = ioc->chains_per_prp_buffer; @@ -4270,33 +5909,378 @@ _base_release_memory_pools(struct MPT3SAS_ADAPTER *ioc) kfree(ioc->chain_lookup); ioc->chain_lookup = NULL; } + + kfree(ioc->io_queue_num); + ioc->io_queue_num = NULL; } /** - * is_MSB_are_same - checks whether all reply queues in a set are + * mpt3sas_check_same_4gb_region - checks whether all reply queues in a set are * having same upper 32bits in their base memory address. - * @reply_pool_start_address: Base address of a reply queue set + * @start_address: Base address of a reply queue set * @pool_sz: Size of single Reply Descriptor Post Queues pool size * * Return: 1 if reply queues in a set have a same upper 32bits in their base * memory address, else 0. */ - static int -is_MSB_are_same(long reply_pool_start_address, u32 pool_sz) +mpt3sas_check_same_4gb_region(dma_addr_t start_address, u32 pool_sz) { - long reply_pool_end_address; + dma_addr_t end_address; - reply_pool_end_address = reply_pool_start_address + pool_sz; + end_address = start_address + pool_sz - 1; - if (upper_32_bits(reply_pool_start_address) == - upper_32_bits(reply_pool_end_address)) + if (upper_32_bits(start_address) == upper_32_bits(end_address)) return 1; else return 0; } /** + * _base_reduce_hba_queue_depth- Retry with reduced queue depth + * @ioc: Adapter object + * + * Return: 0 for success, non-zero for failure. + **/ +static inline int +_base_reduce_hba_queue_depth(struct MPT3SAS_ADAPTER *ioc) +{ + int reduce_sz = 64; + + if ((ioc->hba_queue_depth - reduce_sz) > + (ioc->internal_depth + INTERNAL_SCSIIO_CMDS_COUNT)) { + ioc->hba_queue_depth -= reduce_sz; + return 0; + } else + return -ENOMEM; +} + +/** + * _base_allocate_pcie_sgl_pool - Allocating DMA'able memory + * for pcie sgl pools. + * @ioc: Adapter object + * @sz: DMA Pool size + * + * Return: 0 for success, non-zero for failure. + */ + +static int +_base_allocate_pcie_sgl_pool(struct MPT3SAS_ADAPTER *ioc, u32 sz) +{ + int i = 0, j = 0; + struct chain_tracker *ct; + + ioc->pcie_sgl_dma_pool = + dma_pool_create("PCIe SGL pool", &ioc->pdev->dev, sz, + ioc->page_size, 0); + if (!ioc->pcie_sgl_dma_pool) { + ioc_err(ioc, "PCIe SGL pool: dma_pool_create failed\n"); + return -ENOMEM; + } + + ioc->chains_per_prp_buffer = sz/ioc->chain_segment_sz; + ioc->chains_per_prp_buffer = + min(ioc->chains_per_prp_buffer, ioc->chains_needed_per_io); + for (i = 0; i < ioc->scsiio_depth; i++) { + ioc->pcie_sg_lookup[i].pcie_sgl = + dma_pool_alloc(ioc->pcie_sgl_dma_pool, GFP_KERNEL, + &ioc->pcie_sg_lookup[i].pcie_sgl_dma); + if (!ioc->pcie_sg_lookup[i].pcie_sgl) { + ioc_err(ioc, "PCIe SGL pool: dma_pool_alloc failed\n"); + return -EAGAIN; + } + + if (!mpt3sas_check_same_4gb_region( + ioc->pcie_sg_lookup[i].pcie_sgl_dma, sz)) { + ioc_err(ioc, "PCIE SGLs are not in same 4G !! pcie sgl (0x%p) dma = (0x%llx)\n", + ioc->pcie_sg_lookup[i].pcie_sgl, + (unsigned long long) + ioc->pcie_sg_lookup[i].pcie_sgl_dma); + ioc->use_32bit_dma = true; + return -EAGAIN; + } + + for (j = 0; j < ioc->chains_per_prp_buffer; j++) { + ct = &ioc->chain_lookup[i].chains_per_smid[j]; + ct->chain_buffer = + ioc->pcie_sg_lookup[i].pcie_sgl + + (j * ioc->chain_segment_sz); + ct->chain_buffer_dma = + ioc->pcie_sg_lookup[i].pcie_sgl_dma + + (j * ioc->chain_segment_sz); + } + } + dinitprintk(ioc, ioc_info(ioc, + "PCIe sgl pool depth(%d), element_size(%d), pool_size(%d kB)\n", + ioc->scsiio_depth, sz, (sz * ioc->scsiio_depth)/1024)); + dinitprintk(ioc, ioc_info(ioc, + "Number of chains can fit in a PRP page(%d)\n", + ioc->chains_per_prp_buffer)); + return 0; +} + +/** + * _base_allocate_chain_dma_pool - Allocating DMA'able memory + * for chain dma pool. + * @ioc: Adapter object + * @sz: DMA Pool size + * + * Return: 0 for success, non-zero for failure. + */ +static int +_base_allocate_chain_dma_pool(struct MPT3SAS_ADAPTER *ioc, u32 sz) +{ + int i = 0, j = 0; + struct chain_tracker *ctr; + + ioc->chain_dma_pool = dma_pool_create("chain pool", &ioc->pdev->dev, + ioc->chain_segment_sz, 16, 0); + if (!ioc->chain_dma_pool) + return -ENOMEM; + + for (i = 0; i < ioc->scsiio_depth; i++) { + for (j = ioc->chains_per_prp_buffer; + j < ioc->chains_needed_per_io; j++) { + ctr = &ioc->chain_lookup[i].chains_per_smid[j]; + ctr->chain_buffer = dma_pool_alloc(ioc->chain_dma_pool, + GFP_KERNEL, &ctr->chain_buffer_dma); + if (!ctr->chain_buffer) + return -EAGAIN; + if (!mpt3sas_check_same_4gb_region( + ctr->chain_buffer_dma, ioc->chain_segment_sz)) { + ioc_err(ioc, + "Chain buffers are not in same 4G !!! Chain buff (0x%p) dma = (0x%llx)\n", + ctr->chain_buffer, + (unsigned long long)ctr->chain_buffer_dma); + ioc->use_32bit_dma = true; + return -EAGAIN; + } + } + } + dinitprintk(ioc, ioc_info(ioc, + "chain_lookup depth (%d), frame_size(%d), pool_size(%d kB)\n", + ioc->scsiio_depth, ioc->chain_segment_sz, ((ioc->scsiio_depth * + (ioc->chains_needed_per_io - ioc->chains_per_prp_buffer) * + ioc->chain_segment_sz))/1024)); + return 0; +} + +/** + * _base_allocate_sense_dma_pool - Allocating DMA'able memory + * for sense dma pool. + * @ioc: Adapter object + * @sz: DMA Pool size + * Return: 0 for success, non-zero for failure. + */ +static int +_base_allocate_sense_dma_pool(struct MPT3SAS_ADAPTER *ioc, u32 sz) +{ + ioc->sense_dma_pool = + dma_pool_create("sense pool", &ioc->pdev->dev, sz, 4, 0); + if (!ioc->sense_dma_pool) + return -ENOMEM; + ioc->sense = dma_pool_alloc(ioc->sense_dma_pool, + GFP_KERNEL, &ioc->sense_dma); + if (!ioc->sense) + return -EAGAIN; + if (!mpt3sas_check_same_4gb_region(ioc->sense_dma, sz)) { + dinitprintk(ioc, pr_err( + "Bad Sense Pool! sense (0x%p) sense_dma = (0x%llx)\n", + ioc->sense, (unsigned long long) ioc->sense_dma)); + ioc->use_32bit_dma = true; + return -EAGAIN; + } + ioc_info(ioc, + "sense pool(0x%p) - dma(0x%llx): depth(%d), element_size(%d), pool_size (%d kB)\n", + ioc->sense, (unsigned long long)ioc->sense_dma, + ioc->scsiio_depth, SCSI_SENSE_BUFFERSIZE, sz/1024); + return 0; +} + +/** + * _base_allocate_reply_pool - Allocating DMA'able memory + * for reply pool. + * @ioc: Adapter object + * @sz: DMA Pool size + * Return: 0 for success, non-zero for failure. + */ +static int +_base_allocate_reply_pool(struct MPT3SAS_ADAPTER *ioc, u32 sz) +{ + /* reply pool, 4 byte align */ + ioc->reply_dma_pool = dma_pool_create("reply pool", + &ioc->pdev->dev, sz, 4, 0); + if (!ioc->reply_dma_pool) + return -ENOMEM; + ioc->reply = dma_pool_alloc(ioc->reply_dma_pool, GFP_KERNEL, + &ioc->reply_dma); + if (!ioc->reply) + return -EAGAIN; + if (!mpt3sas_check_same_4gb_region(ioc->reply_dma, sz)) { + dinitprintk(ioc, pr_err( + "Bad Reply Pool! Reply (0x%p) Reply dma = (0x%llx)\n", + ioc->reply, (unsigned long long) ioc->reply_dma)); + ioc->use_32bit_dma = true; + return -EAGAIN; + } + ioc->reply_dma_min_address = (u32)(ioc->reply_dma); + ioc->reply_dma_max_address = (u32)(ioc->reply_dma) + sz; + ioc_info(ioc, + "reply pool(0x%p) - dma(0x%llx): depth(%d), frame_size(%d), pool_size(%d kB)\n", + ioc->reply, (unsigned long long)ioc->reply_dma, + ioc->reply_free_queue_depth, ioc->reply_sz, sz/1024); + return 0; +} + +/** + * _base_allocate_reply_free_dma_pool - Allocating DMA'able memory + * for reply free dma pool. + * @ioc: Adapter object + * @sz: DMA Pool size + * Return: 0 for success, non-zero for failure. + */ +static int +_base_allocate_reply_free_dma_pool(struct MPT3SAS_ADAPTER *ioc, u32 sz) +{ + /* reply free queue, 16 byte align */ + ioc->reply_free_dma_pool = dma_pool_create( + "reply_free pool", &ioc->pdev->dev, sz, 16, 0); + if (!ioc->reply_free_dma_pool) + return -ENOMEM; + ioc->reply_free = dma_pool_alloc(ioc->reply_free_dma_pool, + GFP_KERNEL, &ioc->reply_free_dma); + if (!ioc->reply_free) + return -EAGAIN; + if (!mpt3sas_check_same_4gb_region(ioc->reply_free_dma, sz)) { + dinitprintk(ioc, + pr_err("Bad Reply Free Pool! Reply Free (0x%p) Reply Free dma = (0x%llx)\n", + ioc->reply_free, (unsigned long long) ioc->reply_free_dma)); + ioc->use_32bit_dma = true; + return -EAGAIN; + } + memset(ioc->reply_free, 0, sz); + dinitprintk(ioc, ioc_info(ioc, + "reply_free pool(0x%p): depth(%d), element_size(%d), pool_size(%d kB)\n", + ioc->reply_free, ioc->reply_free_queue_depth, 4, sz/1024)); + dinitprintk(ioc, ioc_info(ioc, + "reply_free_dma (0x%llx)\n", + (unsigned long long)ioc->reply_free_dma)); + return 0; +} + +/** + * _base_allocate_reply_post_free_array - Allocating DMA'able memory + * for reply post free array. + * @ioc: Adapter object + * @reply_post_free_array_sz: DMA Pool size + * Return: 0 for success, non-zero for failure. + */ + +static int +_base_allocate_reply_post_free_array(struct MPT3SAS_ADAPTER *ioc, + u32 reply_post_free_array_sz) +{ + ioc->reply_post_free_array_dma_pool = + dma_pool_create("reply_post_free_array pool", + &ioc->pdev->dev, reply_post_free_array_sz, 16, 0); + if (!ioc->reply_post_free_array_dma_pool) + return -ENOMEM; + ioc->reply_post_free_array = + dma_pool_alloc(ioc->reply_post_free_array_dma_pool, + GFP_KERNEL, &ioc->reply_post_free_array_dma); + if (!ioc->reply_post_free_array) + return -EAGAIN; + if (!mpt3sas_check_same_4gb_region(ioc->reply_post_free_array_dma, + reply_post_free_array_sz)) { + dinitprintk(ioc, pr_err( + "Bad Reply Free Pool! Reply Free (0x%p) Reply Free dma = (0x%llx)\n", + ioc->reply_free, + (unsigned long long) ioc->reply_free_dma)); + ioc->use_32bit_dma = true; + return -EAGAIN; + } + return 0; +} +/** + * base_alloc_rdpq_dma_pool - Allocating DMA'able memory + * for reply queues. + * @ioc: per adapter object + * @sz: DMA Pool size + * Return: 0 for success, non-zero for failure. + */ +static int +base_alloc_rdpq_dma_pool(struct MPT3SAS_ADAPTER *ioc, int sz) +{ + int i = 0; + u32 dma_alloc_count = 0; + int reply_post_free_sz = ioc->reply_post_queue_depth * + sizeof(Mpi2DefaultReplyDescriptor_t); + int count = ioc->rdpq_array_enable ? ioc->reply_queue_count : 1; + + ioc->reply_post = kcalloc(count, sizeof(struct reply_post_struct), + GFP_KERNEL); + if (!ioc->reply_post) + return -ENOMEM; + /* + * For INVADER_SERIES each set of 8 reply queues(0-7, 8-15, ..) and + * VENTURA_SERIES each set of 16 reply queues(0-15, 16-31, ..) should + * be within 4GB boundary i.e reply queues in a set must have same + * upper 32-bits in their memory address. so here driver is allocating + * the DMA'able memory for reply queues according. + * Driver uses limitation of + * VENTURA_SERIES to manage INVADER_SERIES as well. + */ + dma_alloc_count = DIV_ROUND_UP(count, + RDPQ_MAX_INDEX_IN_ONE_CHUNK); + ioc->reply_post_free_dma_pool = + dma_pool_create("reply_post_free pool", + &ioc->pdev->dev, sz, 16, 0); + if (!ioc->reply_post_free_dma_pool) + return -ENOMEM; + for (i = 0; i < count; i++) { + if ((i % RDPQ_MAX_INDEX_IN_ONE_CHUNK == 0) && dma_alloc_count) { + ioc->reply_post[i].reply_post_free = + dma_pool_zalloc(ioc->reply_post_free_dma_pool, + GFP_KERNEL, + &ioc->reply_post[i].reply_post_free_dma); + if (!ioc->reply_post[i].reply_post_free) + return -ENOMEM; + /* + * Each set of RDPQ pool must satisfy 4gb boundary + * restriction. + * 1) Check if allocated resources for RDPQ pool are in + * the same 4GB range. + * 2) If #1 is true, continue with 64 bit DMA. + * 3) If #1 is false, return 1. which means free all the + * resources and set DMA mask to 32 and allocate. + */ + if (!mpt3sas_check_same_4gb_region( + ioc->reply_post[i].reply_post_free_dma, sz)) { + dinitprintk(ioc, + ioc_err(ioc, "bad Replypost free pool(0x%p)" + "reply_post_free_dma = (0x%llx)\n", + ioc->reply_post[i].reply_post_free, + (unsigned long long) + ioc->reply_post[i].reply_post_free_dma)); + return -EAGAIN; + } + dma_alloc_count--; + + } else { + ioc->reply_post[i].reply_post_free = + (Mpi2ReplyDescriptorsUnion_t *) + ((long)ioc->reply_post[i-1].reply_post_free + + reply_post_free_sz); + ioc->reply_post[i].reply_post_free_dma = + (dma_addr_t) + (ioc->reply_post[i-1].reply_post_free_dma + + reply_post_free_sz); + } + } + return 0; +} + +/** * _base_allocate_memory_pools - allocate start of day memory pools * @ioc: per adapter object * @@ -4310,11 +6294,12 @@ _base_allocate_memory_pools(struct MPT3SAS_ADAPTER *ioc) u16 chains_needed_per_io; u32 sz, total_sz, reply_post_free_sz, reply_post_free_array_sz; u32 retry_sz; + u32 rdpq_sz = 0, sense_sz = 0; u16 max_request_credit, nvme_blocks_needed; unsigned short sg_tablesize; u16 sge_size; - int i, j; - struct chain_tracker *ct; + int i; + int ret = 0, rc = 0; dinitprintk(ioc, ioc_info(ioc, "%s\n", __func__)); @@ -4456,64 +6441,40 @@ _base_allocate_memory_pools(struct MPT3SAS_ADAPTER *ioc) ioc->reply_free_queue_depth = ioc->hba_queue_depth + 64; } - dinitprintk(ioc, - ioc_info(ioc, "scatter gather: sge_in_main_msg(%d), sge_per_chain(%d), sge_per_io(%d), chains_per_io(%d)\n", - ioc->max_sges_in_main_message, - ioc->max_sges_in_chain_message, - ioc->shost->sg_tablesize, - ioc->chains_needed_per_io)); + ioc_info(ioc, + "scatter gather: sge_in_main_msg(%d), sge_per_chain(%d), " + "sge_per_io(%d), chains_per_io(%d)\n", + ioc->max_sges_in_main_message, + ioc->max_sges_in_chain_message, + ioc->shost->sg_tablesize, + ioc->chains_needed_per_io); /* reply post queue, 16 byte align */ reply_post_free_sz = ioc->reply_post_queue_depth * sizeof(Mpi2DefaultReplyDescriptor_t); - - sz = reply_post_free_sz; - if (_base_is_controller_msix_enabled(ioc) && !ioc->rdpq_array_enable) - sz *= ioc->reply_queue_count; - - ioc->reply_post = kcalloc((ioc->rdpq_array_enable) ? - (ioc->reply_queue_count):1, - sizeof(struct reply_post_struct), GFP_KERNEL); - - if (!ioc->reply_post) { - ioc_err(ioc, "reply_post_free pool: kcalloc failed\n"); - goto out; - } - ioc->reply_post_free_dma_pool = dma_pool_create("reply_post_free pool", - &ioc->pdev->dev, sz, 16, 0); - if (!ioc->reply_post_free_dma_pool) { - ioc_err(ioc, "reply_post_free pool: dma_pool_create failed\n"); - goto out; - } - i = 0; - do { - ioc->reply_post[i].reply_post_free = - dma_pool_zalloc(ioc->reply_post_free_dma_pool, - GFP_KERNEL, - &ioc->reply_post[i].reply_post_free_dma); - if (!ioc->reply_post[i].reply_post_free) { - ioc_err(ioc, "reply_post_free pool: dma_pool_alloc failed\n"); - goto out; - } - dinitprintk(ioc, - ioc_info(ioc, "reply post free pool (0x%p): depth(%d), element_size(%d), pool_size(%d kB)\n", - ioc->reply_post[i].reply_post_free, - ioc->reply_post_queue_depth, - 8, sz / 1024)); - dinitprintk(ioc, - ioc_info(ioc, "reply_post_free_dma = (0x%llx)\n", - (u64)ioc->reply_post[i].reply_post_free_dma)); - total_sz += sz; - } while (ioc->rdpq_array_enable && (++i < ioc->reply_queue_count)); - - if (ioc->dma_mask == 64) { - if (_base_change_consistent_dma_mask(ioc, ioc->pdev) != 0) { - ioc_warn(ioc, "no suitable consistent DMA mask for %s\n", - pci_name(ioc->pdev)); - goto out; + rdpq_sz = reply_post_free_sz * RDPQ_MAX_INDEX_IN_ONE_CHUNK; + if ((_base_is_controller_msix_enabled(ioc) && !ioc->rdpq_array_enable) + || (ioc->reply_queue_count < RDPQ_MAX_INDEX_IN_ONE_CHUNK)) + rdpq_sz = reply_post_free_sz * ioc->reply_queue_count; + ret = base_alloc_rdpq_dma_pool(ioc, rdpq_sz); + if (ret == -EAGAIN) { + /* + * Free allocated bad RDPQ memory pools. + * Change dma coherent mask to 32 bit and reallocate RDPQ + */ + _base_release_memory_pools(ioc); + ioc->use_32bit_dma = true; + if (_base_config_dma_addressing(ioc, ioc->pdev) != 0) { + ioc_err(ioc, + "32 DMA mask failed %s\n", pci_name(ioc->pdev)); + return -ENODEV; } - } - + if (base_alloc_rdpq_dma_pool(ioc, rdpq_sz)) + return -ENOMEM; + } else if (ret == -ENOMEM) + return -ENOMEM; + total_sz = rdpq_sz * (!ioc->rdpq_array_enable ? 1 : + DIV_ROUND_UP(ioc->reply_queue_count, RDPQ_MAX_INDEX_IN_ONE_CHUNK)); ioc->scsiio_depth = ioc->hba_queue_depth - ioc->hi_priority_depth - ioc->internal_depth; @@ -4525,7 +6486,6 @@ _base_allocate_memory_pools(struct MPT3SAS_ADAPTER *ioc) ioc_info(ioc, "scsi host: can_queue depth (%d)\n", ioc->shost->can_queue)); - /* contiguous pool for request and chains, 16 byte align, one extra " * "frame for smid=0 */ @@ -4570,15 +6530,13 @@ _base_allocate_memory_pools(struct MPT3SAS_ADAPTER *ioc) ioc->internal_dma = ioc->hi_priority_dma + (ioc->hi_priority_depth * ioc->request_sz); - dinitprintk(ioc, - ioc_info(ioc, "request pool(0x%p): depth(%d), frame_size(%d), pool_size(%d kB)\n", - ioc->request, ioc->hba_queue_depth, - ioc->request_sz, - (ioc->hba_queue_depth * ioc->request_sz) / 1024)); + ioc_info(ioc, + "request pool(0x%p) - dma(0x%llx): " + "depth(%d), frame_size(%d), pool_size(%d kB)\n", + ioc->request, (unsigned long long) ioc->request_dma, + ioc->hba_queue_depth, ioc->request_sz, + (ioc->hba_queue_depth * ioc->request_sz) / 1024); - dinitprintk(ioc, - ioc_info(ioc, "request pool: dma(0x%llx)\n", - (unsigned long long)ioc->request_dma)); total_sz += sz; dinitprintk(ioc, @@ -4627,6 +6585,11 @@ _base_allocate_memory_pools(struct MPT3SAS_ADAPTER *ioc) ioc_info(ioc, "internal(0x%p): depth(%d), start smid(%d)\n", ioc->internal, ioc->internal_depth, ioc->internal_smid)); + + ioc->io_queue_num = kcalloc(ioc->scsiio_depth, + sizeof(u16), GFP_KERNEL); + if (!ioc->io_queue_num) + goto out; /* * The number of NVMe page sized blocks needed is: * (((sg_tablesize * 8) - 1) / (page_size - 8)) + 1 @@ -4640,6 +6603,7 @@ _base_allocate_memory_pools(struct MPT3SAS_ADAPTER *ioc) * be required for NVMe PRP's, only each set of NVMe blocks will be * contiguous, so a new set is allocated for each possible I/O. */ + ioc->chains_per_prp_buffer = 0; if (ioc->facts.ProtocolFlags & MPI2_IOCFACTS_PROTOCOL_NVME_DEVICES) { nvme_blocks_needed = @@ -4654,192 +6618,62 @@ _base_allocate_memory_pools(struct MPT3SAS_ADAPTER *ioc) goto out; } sz = nvme_blocks_needed * ioc->page_size; - ioc->pcie_sgl_dma_pool = - dma_pool_create("PCIe SGL pool", &ioc->pdev->dev, sz, 16, 0); - if (!ioc->pcie_sgl_dma_pool) { - ioc_info(ioc, "PCIe SGL pool: dma_pool_create failed\n"); - goto out; - } - - ioc->chains_per_prp_buffer = sz/ioc->chain_segment_sz; - ioc->chains_per_prp_buffer = min(ioc->chains_per_prp_buffer, - ioc->chains_needed_per_io); - - for (i = 0; i < ioc->scsiio_depth; i++) { - ioc->pcie_sg_lookup[i].pcie_sgl = dma_pool_alloc( - ioc->pcie_sgl_dma_pool, GFP_KERNEL, - &ioc->pcie_sg_lookup[i].pcie_sgl_dma); - if (!ioc->pcie_sg_lookup[i].pcie_sgl) { - ioc_info(ioc, "PCIe SGL pool: dma_pool_alloc failed\n"); - goto out; - } - for (j = 0; j < ioc->chains_per_prp_buffer; j++) { - ct = &ioc->chain_lookup[i].chains_per_smid[j]; - ct->chain_buffer = - ioc->pcie_sg_lookup[i].pcie_sgl + - (j * ioc->chain_segment_sz); - ct->chain_buffer_dma = - ioc->pcie_sg_lookup[i].pcie_sgl_dma + - (j * ioc->chain_segment_sz); - } - } - - dinitprintk(ioc, - ioc_info(ioc, "PCIe sgl pool depth(%d), element_size(%d), pool_size(%d kB)\n", - ioc->scsiio_depth, sz, - (sz * ioc->scsiio_depth) / 1024)); - dinitprintk(ioc, - ioc_info(ioc, "Number of chains can fit in a PRP page(%d)\n", - ioc->chains_per_prp_buffer)); + rc = _base_allocate_pcie_sgl_pool(ioc, sz); + if (rc == -ENOMEM) + return -ENOMEM; + else if (rc == -EAGAIN) + goto try_32bit_dma; total_sz += sz * ioc->scsiio_depth; } - ioc->chain_dma_pool = dma_pool_create("chain pool", &ioc->pdev->dev, - ioc->chain_segment_sz, 16, 0); - if (!ioc->chain_dma_pool) { - ioc_err(ioc, "chain_dma_pool: dma_pool_create failed\n"); - goto out; - } - for (i = 0; i < ioc->scsiio_depth; i++) { - for (j = ioc->chains_per_prp_buffer; - j < ioc->chains_needed_per_io; j++) { - ct = &ioc->chain_lookup[i].chains_per_smid[j]; - ct->chain_buffer = dma_pool_alloc( - ioc->chain_dma_pool, GFP_KERNEL, - &ct->chain_buffer_dma); - if (!ct->chain_buffer) { - ioc_err(ioc, "chain_lookup: pci_pool_alloc failed\n"); - _base_release_memory_pools(ioc); - goto out; - } - } - total_sz += ioc->chain_segment_sz; - } - + rc = _base_allocate_chain_dma_pool(ioc, ioc->chain_segment_sz); + if (rc == -ENOMEM) + return -ENOMEM; + else if (rc == -EAGAIN) + goto try_32bit_dma; + total_sz += ioc->chain_segment_sz * ((ioc->chains_needed_per_io - + ioc->chains_per_prp_buffer) * ioc->scsiio_depth); dinitprintk(ioc, - ioc_info(ioc, "chain pool depth(%d), frame_size(%d), pool_size(%d kB)\n", - ioc->chain_depth, ioc->chain_segment_sz, - (ioc->chain_depth * ioc->chain_segment_sz) / 1024)); - + ioc_info(ioc, "chain pool depth(%d), frame_size(%d), pool_size(%d kB)\n", + ioc->chain_depth, ioc->chain_segment_sz, + (ioc->chain_depth * ioc->chain_segment_sz) / 1024)); /* sense buffers, 4 byte align */ - sz = ioc->scsiio_depth * SCSI_SENSE_BUFFERSIZE; - ioc->sense_dma_pool = dma_pool_create("sense pool", &ioc->pdev->dev, sz, - 4, 0); - if (!ioc->sense_dma_pool) { - ioc_err(ioc, "sense pool: dma_pool_create failed\n"); - goto out; - } - ioc->sense = dma_pool_alloc(ioc->sense_dma_pool, GFP_KERNEL, - &ioc->sense_dma); - if (!ioc->sense) { - ioc_err(ioc, "sense pool: dma_pool_alloc failed\n"); - goto out; - } - /* sense buffer requires to be in same 4 gb region. - * Below function will check the same. - * In case of failure, new pci pool will be created with updated - * alignment. Older allocation and pool will be destroyed. - * Alignment will be used such a way that next allocation if - * success, will always meet same 4gb region requirement. - * Actual requirement is not alignment, but we need start and end of - * DMA address must have same upper 32 bit address. - */ - if (!is_MSB_are_same((long)ioc->sense, sz)) { - //Release Sense pool & Reallocate - dma_pool_free(ioc->sense_dma_pool, ioc->sense, ioc->sense_dma); - dma_pool_destroy(ioc->sense_dma_pool); - ioc->sense = NULL; - - ioc->sense_dma_pool = - dma_pool_create("sense pool", &ioc->pdev->dev, sz, - roundup_pow_of_two(sz), 0); - if (!ioc->sense_dma_pool) { - ioc_err(ioc, "sense pool: pci_pool_create failed\n"); - goto out; - } - ioc->sense = dma_pool_alloc(ioc->sense_dma_pool, GFP_KERNEL, - &ioc->sense_dma); - if (!ioc->sense) { - ioc_err(ioc, "sense pool: pci_pool_alloc failed\n"); - goto out; - } - } - dinitprintk(ioc, - ioc_info(ioc, "sense pool(0x%p): depth(%d), element_size(%d), pool_size(%d kB)\n", - ioc->sense, ioc->scsiio_depth, - SCSI_SENSE_BUFFERSIZE, sz / 1024)); - dinitprintk(ioc, - ioc_info(ioc, "sense_dma(0x%llx)\n", - (unsigned long long)ioc->sense_dma)); - total_sz += sz; - + sense_sz = ioc->scsiio_depth * SCSI_SENSE_BUFFERSIZE; + rc = _base_allocate_sense_dma_pool(ioc, sense_sz); + if (rc == -ENOMEM) + return -ENOMEM; + else if (rc == -EAGAIN) + goto try_32bit_dma; + total_sz += sense_sz; /* reply pool, 4 byte align */ sz = ioc->reply_free_queue_depth * ioc->reply_sz; - ioc->reply_dma_pool = dma_pool_create("reply pool", &ioc->pdev->dev, sz, - 4, 0); - if (!ioc->reply_dma_pool) { - ioc_err(ioc, "reply pool: dma_pool_create failed\n"); - goto out; - } - ioc->reply = dma_pool_alloc(ioc->reply_dma_pool, GFP_KERNEL, - &ioc->reply_dma); - if (!ioc->reply) { - ioc_err(ioc, "reply pool: dma_pool_alloc failed\n"); - goto out; - } - ioc->reply_dma_min_address = (u32)(ioc->reply_dma); - ioc->reply_dma_max_address = (u32)(ioc->reply_dma) + sz; - dinitprintk(ioc, - ioc_info(ioc, "reply pool(0x%p): depth(%d), frame_size(%d), pool_size(%d kB)\n", - ioc->reply, ioc->reply_free_queue_depth, - ioc->reply_sz, sz / 1024)); - dinitprintk(ioc, - ioc_info(ioc, "reply_dma(0x%llx)\n", - (unsigned long long)ioc->reply_dma)); + rc = _base_allocate_reply_pool(ioc, sz); + if (rc == -ENOMEM) + return -ENOMEM; + else if (rc == -EAGAIN) + goto try_32bit_dma; total_sz += sz; /* reply free queue, 16 byte align */ sz = ioc->reply_free_queue_depth * 4; - ioc->reply_free_dma_pool = dma_pool_create("reply_free pool", - &ioc->pdev->dev, sz, 16, 0); - if (!ioc->reply_free_dma_pool) { - ioc_err(ioc, "reply_free pool: dma_pool_create failed\n"); - goto out; - } - ioc->reply_free = dma_pool_zalloc(ioc->reply_free_dma_pool, GFP_KERNEL, - &ioc->reply_free_dma); - if (!ioc->reply_free) { - ioc_err(ioc, "reply_free pool: dma_pool_alloc failed\n"); - goto out; - } - dinitprintk(ioc, - ioc_info(ioc, "reply_free pool(0x%p): depth(%d), element_size(%d), pool_size(%d kB)\n", - ioc->reply_free, ioc->reply_free_queue_depth, - 4, sz / 1024)); + rc = _base_allocate_reply_free_dma_pool(ioc, sz); + if (rc == -ENOMEM) + return -ENOMEM; + else if (rc == -EAGAIN) + goto try_32bit_dma; dinitprintk(ioc, ioc_info(ioc, "reply_free_dma (0x%llx)\n", (unsigned long long)ioc->reply_free_dma)); total_sz += sz; - if (ioc->rdpq_array_enable) { reply_post_free_array_sz = ioc->reply_queue_count * sizeof(Mpi2IOCInitRDPQArrayEntry); - ioc->reply_post_free_array_dma_pool = - dma_pool_create("reply_post_free_array pool", - &ioc->pdev->dev, reply_post_free_array_sz, 16, 0); - if (!ioc->reply_post_free_array_dma_pool) { - dinitprintk(ioc, - ioc_info(ioc, "reply_post_free_array pool: dma_pool_create failed\n")); - goto out; - } - ioc->reply_post_free_array = - dma_pool_alloc(ioc->reply_post_free_array_dma_pool, - GFP_KERNEL, &ioc->reply_post_free_array_dma); - if (!ioc->reply_post_free_array) { - dinitprintk(ioc, - ioc_info(ioc, "reply_post_free_array pool: dma_pool_alloc failed\n")); - goto out; - } + rc = _base_allocate_reply_post_free_array(ioc, + reply_post_free_array_sz); + if (rc == -ENOMEM) + return -ENOMEM; + else if (rc == -EAGAIN) + goto try_32bit_dma; } ioc->config_page_sz = 512; ioc->config_page = dma_alloc_coherent(&ioc->pdev->dev, @@ -4848,12 +6682,10 @@ _base_allocate_memory_pools(struct MPT3SAS_ADAPTER *ioc) ioc_err(ioc, "config page: dma_pool_alloc failed\n"); goto out; } - dinitprintk(ioc, - ioc_info(ioc, "config page(0x%p): size(%d)\n", - ioc->config_page, ioc->config_page_sz)); - dinitprintk(ioc, - ioc_info(ioc, "config_page_dma(0x%llx)\n", - (unsigned long long)ioc->config_page_dma)); + + ioc_info(ioc, "config page(0x%p) - dma(0x%llx): size(%d)\n", + ioc->config_page, (unsigned long long)ioc->config_page_dma, + ioc->config_page_sz); total_sz += ioc->config_page_sz; ioc_info(ioc, "Allocated physical memory: size(%d kB)\n", @@ -4864,6 +6696,19 @@ _base_allocate_memory_pools(struct MPT3SAS_ADAPTER *ioc) ioc->shost->sg_tablesize); return 0; +try_32bit_dma: + _base_release_memory_pools(ioc); + if (ioc->use_32bit_dma && (ioc->dma_mask > 32)) { + /* Change dma coherent mask to 32 bit and reallocate */ + if (_base_config_dma_addressing(ioc, ioc->pdev) != 0) { + pr_err("Setting 32 bit coherent DMA mask Failed %s\n", + pci_name(ioc->pdev)); + return -ENODEV; + } + } else if (_base_reduce_hba_queue_depth(ioc) != 0) + return -ENOMEM; + goto retry_allocation; + out: return -ENOMEM; } @@ -4881,7 +6726,7 @@ mpt3sas_base_get_iocstate(struct MPT3SAS_ADAPTER *ioc, int cooked) { u32 s, sc; - s = ioc->base_readl(&ioc->chip->Doorbell); + s = ioc->base_readl_ext_retry(&ioc->chip->Doorbell); sc = s & MPI2_IOC_STATE_MASK; return cooked ? sc : s; } @@ -4908,6 +6753,8 @@ _base_wait_on_iocstate(struct MPT3SAS_ADAPTER *ioc, u32 ioc_state, int timeout) return 0; if (count && current_state == MPI2_IOC_STATE_FAULT) break; + if (count && current_state == MPI2_IOC_STATE_COREDUMP) + break; usleep_range(1000, 1500); count++; @@ -4917,16 +6764,32 @@ _base_wait_on_iocstate(struct MPT3SAS_ADAPTER *ioc, u32 ioc_state, int timeout) } /** + * _base_dump_reg_set - This function will print hexdump of register set. + * @ioc: per adapter object + * + * Return: nothing. + */ +static inline void +_base_dump_reg_set(struct MPT3SAS_ADAPTER *ioc) +{ + unsigned int i, sz = 256; + u32 __iomem *reg = (u32 __iomem *)ioc->chip; + + ioc_info(ioc, "System Register set:\n"); + for (i = 0; i < (sz / sizeof(u32)); i++) + pr_info("%08x: %08x\n", (i * 4), readl(®[i])); +} + +/** * _base_wait_for_doorbell_int - waiting for controller interrupt(generated by * a write to the doorbell) * @ioc: per adapter object + * @timeout: timeout in seconds * * Return: 0 for success, non-zero for failure. * * Notes: MPI2_HIS_IOC2SYS_DB_STATUS - set to one when IOC writes to doorbell. */ -static int -_base_diag_reset(struct MPT3SAS_ADAPTER *ioc); static int _base_wait_for_doorbell_int(struct MPT3SAS_ADAPTER *ioc, int timeout) @@ -5008,10 +6871,15 @@ _base_wait_for_doorbell_ack(struct MPT3SAS_ADAPTER *ioc, int timeout) __func__, count, timeout)); return 0; } else if (int_status & MPI2_HIS_IOC2SYS_DB_STATUS) { - doorbell = ioc->base_readl(&ioc->chip->Doorbell); + doorbell = ioc->base_readl_ext_retry(&ioc->chip->Doorbell); if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) { - mpt3sas_base_fault_info(ioc , doorbell); + mpt3sas_print_fault_code(ioc, doorbell); + return -EFAULT; + } + if ((doorbell & MPI2_IOC_STATE_MASK) == + MPI2_IOC_STATE_COREDUMP) { + mpt3sas_print_coredump_info(ioc, doorbell); return -EFAULT; } } else if (int_status == 0xFFFFFFFF) @@ -5043,7 +6911,7 @@ _base_wait_for_doorbell_not_used(struct MPT3SAS_ADAPTER *ioc, int timeout) count = 0; cntdn = 1000 * timeout; do { - doorbell_reg = ioc->base_readl(&ioc->chip->Doorbell); + doorbell_reg = ioc->base_readl_ext_retry(&ioc->chip->Doorbell); if (!(doorbell_reg & MPI2_DOORBELL_USED)) { dhsprintk(ioc, ioc_info(ioc, "%s: successful count(%d), timeout(%d)\n", @@ -5073,6 +6941,7 @@ _base_send_ioc_reset(struct MPT3SAS_ADAPTER *ioc, u8 reset_type, int timeout) { u32 ioc_state; int r = 0; + unsigned long flags; if (reset_type != MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET) { ioc_err(ioc, "%s: unknown reset_type\n", __func__); @@ -5091,6 +6960,7 @@ _base_send_ioc_reset(struct MPT3SAS_ADAPTER *ioc, u8 reset_type, int timeout) r = -EFAULT; goto out; } + ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_READY, timeout); if (ioc_state) { ioc_err(ioc, "%s: failed going to ready state (ioc_state=0x%x)\n", @@ -5099,6 +6969,26 @@ _base_send_ioc_reset(struct MPT3SAS_ADAPTER *ioc, u8 reset_type, int timeout) goto out; } out: + if (r != 0) { + ioc_state = mpt3sas_base_get_iocstate(ioc, 0); + spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); + /* + * Wait for IOC state CoreDump to clear only during + * HBA initialization & release time. + */ + if ((ioc_state & MPI2_IOC_STATE_MASK) == + MPI2_IOC_STATE_COREDUMP && (ioc->is_driver_loading == 1 || + ioc->fault_reset_work_q == NULL)) { + spin_unlock_irqrestore( + &ioc->ioc_reset_in_progress_lock, flags); + mpt3sas_print_coredump_info(ioc, ioc_state); + mpt3sas_base_wait_for_coredump_completion(ioc, + __func__); + spin_lock_irqsave( + &ioc->ioc_reset_in_progress_lock, flags); + } + spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); + } ioc_info(ioc, "message unit reset: %s\n", r == 0 ? "SUCCESS" : "FAILED"); return r; @@ -5107,11 +6997,11 @@ _base_send_ioc_reset(struct MPT3SAS_ADAPTER *ioc, u8 reset_type, int timeout) /** * mpt3sas_wait_for_ioc - IOC's operational state is checked here. * @ioc: per adapter object - * @wait_count: timeout in seconds + * @timeout: timeout in seconds * * Return: Waits up to timeout seconds for the IOC to * become operational. Returns 0 if IOC is present - * and operational; otherwise returns -EFAULT. + * and operational; otherwise returns %-EFAULT. */ int @@ -5124,6 +7014,17 @@ mpt3sas_wait_for_ioc(struct MPT3SAS_ADAPTER *ioc, int timeout) ioc_state = mpt3sas_base_get_iocstate(ioc, 1); if (ioc_state == MPI2_IOC_STATE_OPERATIONAL) break; + + /* + * Watchdog thread will be started after IOC Initialization, so + * no need to wait here for IOC state to become operational + * when IOC Initialization is on. Instead the driver will + * return ETIME status, so that calling function can issue + * diag reset operation and retry the command. + */ + if (ioc->is_driver_loading) + return -ETIME; + ssleep(1); ioc_info(ioc, "%s: waiting for operational state(count=%d)\n", __func__, ++wait_state_count); @@ -5156,11 +7057,12 @@ _base_handshake_req_reply_wait(struct MPT3SAS_ADAPTER *ioc, int request_bytes, int i; u8 failed; __le32 *mfp; + int ret_val; /* make sure doorbell is not in use */ - if ((ioc->base_readl(&ioc->chip->Doorbell) & MPI2_DOORBELL_USED)) { + if ((ioc->base_readl_ext_retry(&ioc->chip->Doorbell) & MPI2_DOORBELL_USED)) { ioc_err(ioc, "doorbell is in use (line=%d)\n", __LINE__); - return -EFAULT; + goto doorbell_diag_reset; } /* clear pending doorbell interrupts from previous state changes */ @@ -5207,7 +7109,7 @@ _base_handshake_req_reply_wait(struct MPT3SAS_ADAPTER *ioc, int request_bytes, } /* read the first two 16-bits, it gives the total length of the reply */ - reply[0] = le16_to_cpu(ioc->base_readl(&ioc->chip->Doorbell) + reply[0] = le16_to_cpu(ioc->base_readl_ext_retry(&ioc->chip->Doorbell) & MPI2_DOORBELL_DATA_MASK); writel(0, &ioc->chip->HostInterruptStatus); if ((_base_wait_for_doorbell_int(ioc, 5))) { @@ -5215,7 +7117,7 @@ _base_handshake_req_reply_wait(struct MPT3SAS_ADAPTER *ioc, int request_bytes, __LINE__); return -EFAULT; } - reply[1] = le16_to_cpu(ioc->base_readl(&ioc->chip->Doorbell) + reply[1] = le16_to_cpu(ioc->base_readl_ext_retry(&ioc->chip->Doorbell) & MPI2_DOORBELL_DATA_MASK); writel(0, &ioc->chip->HostInterruptStatus); @@ -5226,10 +7128,10 @@ _base_handshake_req_reply_wait(struct MPT3SAS_ADAPTER *ioc, int request_bytes, return -EFAULT; } if (i >= reply_bytes/2) /* overflow case */ - ioc->base_readl(&ioc->chip->Doorbell); + ioc->base_readl_ext_retry(&ioc->chip->Doorbell); else reply[i] = le16_to_cpu( - ioc->base_readl(&ioc->chip->Doorbell) + ioc->base_readl_ext_retry(&ioc->chip->Doorbell) & MPI2_DOORBELL_DATA_MASK); writel(0, &ioc->chip->HostInterruptStatus); } @@ -5246,10 +7148,14 @@ _base_handshake_req_reply_wait(struct MPT3SAS_ADAPTER *ioc, int request_bytes, mfp = (__le32 *)reply; pr_info("\toffset:data\n"); for (i = 0; i < reply_bytes/4; i++) - pr_info("\t[0x%02x]:%08x\n", i*4, + ioc_info(ioc, "\t[0x%02x]:%08x\n", i*4, le32_to_cpu(mfp[i])); } return 0; + +doorbell_diag_reset: + ret_val = _base_diag_reset(ioc); + return ret_val; } /** @@ -5306,7 +7212,7 @@ mpt3sas_base_sas_iounit_control(struct MPT3SAS_ADAPTER *ioc, mpi_request->Operation == MPI2_SAS_OP_PHY_LINK_RESET) ioc->ioc_link_reset_in_progress = 1; init_completion(&ioc->base_cmds.done); - mpt3sas_base_put_smid_default(ioc, smid); + ioc->put_smid_default(ioc, smid); wait_for_completion_timeout(&ioc->base_cmds.done, msecs_to_jiffies(10000)); if ((mpi_request->Operation == MPI2_SAS_OP_PHY_HARD_RESET || @@ -5314,10 +7220,9 @@ mpt3sas_base_sas_iounit_control(struct MPT3SAS_ADAPTER *ioc, ioc->ioc_link_reset_in_progress) ioc->ioc_link_reset_in_progress = 0; if (!(ioc->base_cmds.status & MPT3_CMD_COMPLETE)) { - issue_reset = - mpt3sas_base_check_cmd_timeout(ioc, - ioc->base_cmds.status, mpi_request, - sizeof(Mpi2SasIoUnitControlRequest_t)/4); + mpt3sas_check_cmd_timeout(ioc, ioc->base_cmds.status, + mpi_request, sizeof(Mpi2SasIoUnitControlRequest_t)/4, + issue_reset); goto issue_host_reset; } if (ioc->base_cmds.status & MPT3_CMD_REPLY_VALID) @@ -5383,16 +7288,16 @@ mpt3sas_base_scsi_enclosure_processor(struct MPT3SAS_ADAPTER *ioc, ioc->base_cmds.status = MPT3_CMD_PENDING; request = mpt3sas_base_get_msg_frame(ioc, smid); ioc->base_cmds.smid = smid; + memset(request, 0, ioc->request_sz); memcpy(request, mpi_request, sizeof(Mpi2SepReply_t)); init_completion(&ioc->base_cmds.done); - mpt3sas_base_put_smid_default(ioc, smid); + ioc->put_smid_default(ioc, smid); wait_for_completion_timeout(&ioc->base_cmds.done, msecs_to_jiffies(10000)); if (!(ioc->base_cmds.status & MPT3_CMD_COMPLETE)) { - issue_reset = - mpt3sas_base_check_cmd_timeout(ioc, - ioc->base_cmds.status, mpi_request, - sizeof(Mpi2SepRequest_t)/4); + mpt3sas_check_cmd_timeout(ioc, + ioc->base_cmds.status, mpi_request, + sizeof(Mpi2SepRequest_t)/4, issue_reset); goto issue_host_reset; } if (ioc->base_cmds.status & MPT3_CMD_REPLY_VALID) @@ -5491,9 +7396,15 @@ _base_wait_for_iocstate(struct MPT3SAS_ADAPTER *ioc, int timeout) } if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) { - mpt3sas_base_fault_info(ioc, ioc_state & + mpt3sas_print_fault_code(ioc, ioc_state & MPI2_DOORBELL_DATA_MASK); goto issue_diag_reset; + } else if ((ioc_state & MPI2_IOC_STATE_MASK) == + MPI2_IOC_STATE_COREDUMP) { + ioc_info(ioc, + "%s: Skipping the diag reset here. (ioc_state=0x%x)\n", + __func__, ioc_state); + return -EFAULT; } ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_READY, timeout); @@ -5504,7 +7415,9 @@ _base_wait_for_iocstate(struct MPT3SAS_ADAPTER *ioc, int timeout) return -EFAULT; } - issue_diag_reset: + return 0; + +issue_diag_reset: rc = _base_diag_reset(ioc); return rc; } @@ -5568,6 +7481,9 @@ _base_get_ioc_facts(struct MPT3SAS_ADAPTER *ioc) if ((facts->IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_RDPQ_ARRAY_CAPABLE) && (!reset_devices)) ioc->rdpq_array_capable = 1; + if ((facts->IOCCapabilities & MPI26_IOCFACTS_CAPABILITY_ATOMIC_REQ) + && ioc->is_aero_ioc) + ioc->atomic_desc_capable = 1; facts->FWVersion.Word = le32_to_cpu(mpi_reply.FWVersion.Word); facts->IOCRequestFrameSize = le16_to_cpu(mpi_reply.IOCRequestFrameSize); @@ -5669,6 +7585,12 @@ _base_send_ioc_init(struct MPT3SAS_ADAPTER *ioc) cpu_to_le64((u64)ioc->reply_post[0].reply_post_free_dma); } + /* + * Set the flag to enable CoreDump state feature in IOC firmware. + */ + mpi_request.ConfigurationFlags |= + cpu_to_le16(MPI26_IOCINIT_CFGFLAGS_COREDUMP_ENABLE); + /* This time stamp specifies number of milliseconds * since epoch ~ midnight January 1, 1970. */ @@ -5680,15 +7602,15 @@ _base_send_ioc_init(struct MPT3SAS_ADAPTER *ioc) int i; mfp = (__le32 *)&mpi_request; - pr_info("\toffset:data\n"); + ioc_info(ioc, "\toffset:data\n"); for (i = 0; i < sizeof(Mpi2IOCInitRequest_t)/4; i++) - pr_info("\t[0x%02x]:%08x\n", i*4, + ioc_info(ioc, "\t[0x%02x]:%08x\n", i*4, le32_to_cpu(mfp[i])); } r = _base_handshake_req_reply_wait(ioc, sizeof(Mpi2IOCInitRequest_t), (u32 *)&mpi_request, - sizeof(Mpi2IOCInitReply_t), (u16 *)&mpi_reply, 10); + sizeof(Mpi2IOCInitReply_t), (u16 *)&mpi_reply, 30); if (r != 0) { ioc_err(ioc, "%s: handshake failed (r=%d)\n", __func__, r); @@ -5702,6 +7624,8 @@ _base_send_ioc_init(struct MPT3SAS_ADAPTER *ioc) r = -EIO; } + /* Reset TimeSync Counter*/ + ioc->timestamp_update_count = 0; return r; } @@ -5740,7 +7664,8 @@ mpt3sas_port_enable_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, if (ioc_status != MPI2_IOCSTATUS_SUCCESS) ioc->port_enable_failed = 1; - if (ioc->is_driver_loading) { + if (ioc->port_enable_cmds.status & MPT3_CMD_COMPLETE_ASYNC) { + ioc->port_enable_cmds.status &= ~MPT3_CMD_COMPLETE_ASYNC; if (ioc_status == MPI2_IOCSTATUS_SUCCESS) { mpt3sas_port_enable_complete(ioc); return 1; @@ -5789,7 +7714,7 @@ _base_send_port_enable(struct MPT3SAS_ADAPTER *ioc) mpi_request->Function = MPI2_FUNCTION_PORT_ENABLE; init_completion(&ioc->port_enable_cmds.done); - mpt3sas_base_put_smid_default(ioc, smid); + ioc->put_smid_default(ioc, smid); wait_for_completion_timeout(&ioc->port_enable_cmds.done, 300*HZ); if (!(ioc->port_enable_cmds.status & MPT3_CMD_COMPLETE)) { ioc_err(ioc, "%s: timeout\n", __func__); @@ -5841,14 +7766,15 @@ mpt3sas_port_enable(struct MPT3SAS_ADAPTER *ioc) ioc_err(ioc, "%s: failed obtaining a smid\n", __func__); return -EAGAIN; } - + ioc->drv_internal_flags |= MPT_DRV_INTERNAL_FIRST_PE_ISSUED; ioc->port_enable_cmds.status = MPT3_CMD_PENDING; + ioc->port_enable_cmds.status |= MPT3_CMD_COMPLETE_ASYNC; mpi_request = mpt3sas_base_get_msg_frame(ioc, smid); ioc->port_enable_cmds.smid = smid; memset(mpi_request, 0, sizeof(Mpi2PortEnableRequest_t)); mpi_request->Function = MPI2_FUNCTION_PORT_ENABLE; - mpt3sas_base_put_smid_default(ioc, smid); + ioc->put_smid_default(ioc, smid); return 0; } @@ -5939,7 +7865,7 @@ _base_event_notification(struct MPT3SAS_ADAPTER *ioc) Mpi2EventNotificationRequest_t *mpi_request; u16 smid; int r = 0; - int i; + int i, issue_diag_reset = 0; dinitprintk(ioc, ioc_info(ioc, "%s\n", __func__)); @@ -5964,7 +7890,7 @@ _base_event_notification(struct MPT3SAS_ADAPTER *ioc) mpi_request->EventMasks[i] = cpu_to_le32(ioc->event_masks[i]); init_completion(&ioc->base_cmds.done); - mpt3sas_base_put_smid_default(ioc, smid); + ioc->put_smid_default(ioc, smid); wait_for_completion_timeout(&ioc->base_cmds.done, 30*HZ); if (!(ioc->base_cmds.status & MPT3_CMD_COMPLETE)) { ioc_err(ioc, "%s: timeout\n", __func__); @@ -5973,10 +7899,19 @@ _base_event_notification(struct MPT3SAS_ADAPTER *ioc) if (ioc->base_cmds.status & MPT3_CMD_RESET) r = -EFAULT; else - r = -ETIME; + issue_diag_reset = 1; + } else dinitprintk(ioc, ioc_info(ioc, "%s: complete\n", __func__)); ioc->base_cmds.status = MPT3_CMD_NOT_USED; + + if (issue_diag_reset) { + if (ioc->drv_internal_flags & MPT_DRV_INTERNAL_FIRST_PE_ISSUED) + return -EFAULT; + if (mpt3sas_base_check_for_fault_and_issue_reset(ioc)) + return -EFAULT; + r = -EAGAIN; + } return r; } @@ -6018,24 +7953,22 @@ mpt3sas_base_validate_event_type(struct MPT3SAS_ADAPTER *ioc, u32 *event_type) } /** - * _base_diag_reset - the "big hammer" start of day reset - * @ioc: per adapter object - * - * Return: 0 for success, non-zero for failure. - */ -static int -_base_diag_reset(struct MPT3SAS_ADAPTER *ioc) -{ - u32 host_diagnostic; - u32 ioc_state; - u32 count; - u32 hcb_size; +* mpt3sas_base_unlock_and_get_host_diagnostic- enable Host Diagnostic Register writes +* @ioc: per adapter object +* @host_diagnostic: host diagnostic register content +* +* Return: 0 for success, non-zero for failure. +*/ - ioc_info(ioc, "sending diag reset !!\n"); - - drsprintk(ioc, ioc_info(ioc, "clear interrupts\n")); +int +mpt3sas_base_unlock_and_get_host_diagnostic(struct MPT3SAS_ADAPTER *ioc, + u32 *host_diagnostic) +{ + u32 count; + *host_diagnostic = 0; count = 0; + do { /* Write magic sequence to WriteSequence register * Loop until in diagnostic mode @@ -6052,43 +7985,90 @@ _base_diag_reset(struct MPT3SAS_ADAPTER *ioc) /* wait 100 msec */ msleep(100); - if (count++ > 20) - goto out; + if (count++ > 20) { + ioc_info(ioc, + "Stop writing magic sequence after 20 retries\n"); + _base_dump_reg_set(ioc); + return -EFAULT; + } - host_diagnostic = ioc->base_readl(&ioc->chip->HostDiagnostic); + *host_diagnostic = ioc->base_readl_ext_retry(&ioc->chip->HostDiagnostic); drsprintk(ioc, - ioc_info(ioc, "wrote magic sequence: count(%d), host_diagnostic(0x%08x)\n", - count, host_diagnostic)); + ioc_info(ioc, "wrote magic sequence: count(%d), host_diagnostic(0x%08x)\n", + count, *host_diagnostic)); - } while ((host_diagnostic & MPI2_DIAG_DIAG_WRITE_ENABLE) == 0); + } while ((*host_diagnostic & MPI2_DIAG_DIAG_WRITE_ENABLE) == 0); + return 0; +} - hcb_size = ioc->base_readl(&ioc->chip->HCBSize); +/** + * mpt3sas_base_lock_host_diagnostic: Disable Host Diagnostic Register writes + * @ioc: per adapter object + */ + +void +mpt3sas_base_lock_host_diagnostic(struct MPT3SAS_ADAPTER *ioc) +{ + drsprintk(ioc, ioc_info(ioc, "disable writes to the diagnostic register\n")); + writel(MPI2_WRSEQ_FLUSH_KEY_VALUE, &ioc->chip->WriteSequence); +} + +/** + * _base_diag_reset - the "big hammer" start of day reset + * @ioc: per adapter object + * + * Return: 0 for success, non-zero for failure. + */ +static int +_base_diag_reset(struct MPT3SAS_ADAPTER *ioc) +{ + u32 host_diagnostic; + u32 ioc_state; + u32 count; + u32 hcb_size; + + ioc_info(ioc, "sending diag reset !!\n"); + + pci_cfg_access_lock(ioc->pdev); + drsprintk(ioc, ioc_info(ioc, "clear interrupts\n")); + + mutex_lock(&ioc->hostdiag_unlock_mutex); + if (mpt3sas_base_unlock_and_get_host_diagnostic(ioc, &host_diagnostic)) + goto unlock; + + hcb_size = ioc->base_readl(&ioc->chip->HCBSize); drsprintk(ioc, ioc_info(ioc, "diag reset: issued\n")); writel(host_diagnostic | MPI2_DIAG_RESET_ADAPTER, &ioc->chip->HostDiagnostic); - /*This delay allows the chip PCIe hardware time to finish reset tasks*/ + /* This delay allows the chip PCIe hardware time to finish reset tasks */ msleep(MPI2_HARD_RESET_PCIE_FIRST_READ_DELAY_MICRO_SEC/1000); /* Approximately 300 second max wait */ for (count = 0; count < (300000000 / - MPI2_HARD_RESET_PCIE_SECOND_READ_DELAY_MICRO_SEC); count++) { + MPI2_HARD_RESET_PCIE_SECOND_READ_DELAY_MICRO_SEC); count++) { - host_diagnostic = ioc->base_readl(&ioc->chip->HostDiagnostic); + host_diagnostic = ioc->base_readl_ext_retry(&ioc->chip->HostDiagnostic); - if (host_diagnostic == 0xFFFFFFFF) - goto out; + if (host_diagnostic == 0xFFFFFFFF) { + ioc_info(ioc, + "Invalid host diagnostic register value\n"); + _base_dump_reg_set(ioc); + goto unlock; + } if (!(host_diagnostic & MPI2_DIAG_RESET_ADAPTER)) break; - msleep(MPI2_HARD_RESET_PCIE_SECOND_READ_DELAY_MICRO_SEC / 1000); + /* Wait to pass the second read delay window */ + msleep(MPI2_HARD_RESET_PCIE_SECOND_READ_DELAY_MICRO_SEC/1000); } if (host_diagnostic & MPI2_DIAG_HCB_MODE) { drsprintk(ioc, - ioc_info(ioc, "restart the adapter assuming the HCB Address points to good F/W\n")); + ioc_info(ioc, "restart the adapter assuming the\n" + "HCB Address points to good F/W\n")); host_diagnostic &= ~MPI2_DIAG_BOOT_DEVICE_SELECT_MASK; host_diagnostic |= MPI2_DIAG_BOOT_DEVICE_SELECT_HCDW; writel(host_diagnostic, &ioc->chip->HostDiagnostic); @@ -6102,35 +8082,40 @@ _base_diag_reset(struct MPT3SAS_ADAPTER *ioc) writel(host_diagnostic & ~MPI2_DIAG_HOLD_IOC_RESET, &ioc->chip->HostDiagnostic); - drsprintk(ioc, - ioc_info(ioc, "disable writes to the diagnostic register\n")); - writel(MPI2_WRSEQ_FLUSH_KEY_VALUE, &ioc->chip->WriteSequence); + mpt3sas_base_lock_host_diagnostic(ioc); + mutex_unlock(&ioc->hostdiag_unlock_mutex); drsprintk(ioc, ioc_info(ioc, "Wait for FW to go to the READY state\n")); ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_READY, 20); if (ioc_state) { ioc_err(ioc, "%s: failed going to ready state (ioc_state=0x%x)\n", __func__, ioc_state); - goto out; + _base_dump_reg_set(ioc); + goto fail; } + pci_cfg_access_unlock(ioc->pdev); ioc_info(ioc, "diag reset: SUCCESS\n"); return 0; - out: +unlock: + mutex_unlock(&ioc->hostdiag_unlock_mutex); + +fail: + pci_cfg_access_unlock(ioc->pdev); ioc_err(ioc, "diag reset: FAILED\n"); return -EFAULT; } /** - * _base_make_ioc_ready - put controller in READY state + * mpt3sas_base_make_ioc_ready - put controller in READY state * @ioc: per adapter object * @type: FORCE_BIG_HAMMER or SOFT_RESET * * Return: 0 for success, non-zero for failure. */ -static int -_base_make_ioc_ready(struct MPT3SAS_ADAPTER *ioc, enum reset_type type) +int +mpt3sas_base_make_ioc_ready(struct MPT3SAS_ADAPTER *ioc, enum reset_type type) { u32 ioc_state; int rc; @@ -6165,16 +8150,33 @@ _base_make_ioc_ready(struct MPT3SAS_ADAPTER *ioc, enum reset_type type) return 0; if (ioc_state & MPI2_DOORBELL_USED) { - dhsprintk(ioc, ioc_info(ioc, "unexpected doorbell active!\n")); + ioc_info(ioc, "unexpected doorbell active!\n"); goto issue_diag_reset; } if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) { - mpt3sas_base_fault_info(ioc, ioc_state & + mpt3sas_print_fault_code(ioc, ioc_state & MPI2_DOORBELL_DATA_MASK); goto issue_diag_reset; } + if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_COREDUMP) { + /* + * if host reset is invoked while watch dog thread is waiting + * for IOC state to be changed to Fault state then driver has + * to wait here for CoreDump state to clear otherwise reset + * will be issued to the FW and FW move the IOC state to + * reset state without copying the FW logs to coredump region. + */ + if (ioc->ioc_coredump_loop != MPT3SAS_COREDUMP_LOOP_DONE) { + mpt3sas_print_coredump_info(ioc, ioc_state & + MPI2_DOORBELL_DATA_MASK); + mpt3sas_base_wait_for_coredump_completion(ioc, + __func__); + } + goto issue_diag_reset; + } + if (type == FORCE_BIG_HAMMER) goto issue_diag_reset; @@ -6198,7 +8200,7 @@ _base_make_ioc_ready(struct MPT3SAS_ADAPTER *ioc, enum reset_type type) static int _base_make_ioc_operational(struct MPT3SAS_ADAPTER *ioc) { - int r, i, index; + int r, i, index, rc; unsigned long flags; u32 reply_address; u16 smid; @@ -6301,8 +8303,19 @@ _base_make_ioc_operational(struct MPT3SAS_ADAPTER *ioc) skip_init_reply_post_free_queue: r = _base_send_ioc_init(ioc); - if (r) - return r; + if (r) { + /* + * No need to check IOC state for fault state & issue + * diag reset during host reset. This check is need + * only during driver load time. + */ + if (!ioc->is_driver_loading) + return r; + + rc = mpt3sas_base_check_for_fault_and_issue_reset(ioc); + if (rc || (_base_send_ioc_init(ioc))) + return r; + } /* initialize reply free host index */ ioc->reply_free_host_index = ioc->reply_free_queue_depth - 1; @@ -6325,7 +8338,7 @@ _base_make_ioc_operational(struct MPT3SAS_ADAPTER *ioc) skip_init_reply_post_host_index: - _base_unmask_interrupts(ioc); + mpt3sas_base_unmask_interrupts(ioc); if (ioc->hba_mpi_version_belonged != MPI2_VERSION) { r = _base_display_fwpkg_version(ioc); @@ -6333,12 +8346,15 @@ _base_make_ioc_operational(struct MPT3SAS_ADAPTER *ioc) return r; } - _base_static_config_pages(ioc); + r = _base_static_config_pages(ioc); + if (r) + return r; + r = _base_event_notification(ioc); if (r) return r; - if (ioc->is_driver_loading) { + if (!ioc->shost_recovery) { if (ioc->is_warpdrive && ioc->manu_pg10.OEMIdentifier == 0x80) { @@ -6374,9 +8390,9 @@ mpt3sas_base_free_resources(struct MPT3SAS_ADAPTER *ioc) /* synchronizing freeing resource with pci_access_mutex lock */ mutex_lock(&ioc->pci_access_mutex); if (ioc->chip_phys && ioc->chip) { - _base_mask_interrupts(ioc); + mpt3sas_base_mask_interrupts(ioc); ioc->shost_recovery = 1; - _base_make_ioc_ready(ioc, SOFT_RESET); + mpt3sas_base_make_ioc_ready(ioc, SOFT_RESET); ioc->shost_recovery = 0; } @@ -6394,7 +8410,7 @@ mpt3sas_base_free_resources(struct MPT3SAS_ADAPTER *ioc) int mpt3sas_base_attach(struct MPT3SAS_ADAPTER *ioc) { - int r, i; + int r, i, rc; int cpu_id, last_cpu_id = 0; dinitprintk(ioc, ioc_info(ioc, "%s\n", __func__)); @@ -6407,8 +8423,7 @@ mpt3sas_base_attach(struct MPT3SAS_ADAPTER *ioc) ioc->cpu_msix_table = kzalloc(ioc->cpu_msix_table_sz, GFP_KERNEL); ioc->reply_queue_count = 1; if (!ioc->cpu_msix_table) { - dfailprintk(ioc, - ioc_info(ioc, "allocation for cpu_msix_table failed!!!\n")); + ioc_info(ioc, "Allocation for cpu_msix_table failed!!!\n"); r = -ENOMEM; goto out_free_resources; } @@ -6417,40 +8432,49 @@ mpt3sas_base_attach(struct MPT3SAS_ADAPTER *ioc) ioc->reply_post_host_index = kcalloc(ioc->cpu_msix_table_sz, sizeof(resource_size_t *), GFP_KERNEL); if (!ioc->reply_post_host_index) { - dfailprintk(ioc, - ioc_info(ioc, "allocation for reply_post_host_index failed!!!\n")); + ioc_info(ioc, "Allocation for reply_post_host_index failed!!!\n"); r = -ENOMEM; goto out_free_resources; } } + ioc->smp_affinity_enable = smp_affinity_enable; + ioc->rdpq_array_enable_assigned = 0; - ioc->dma_mask = 0; - if (ioc->is_aero_ioc) + ioc->use_32bit_dma = false; + ioc->dma_mask = 64; + if (ioc->is_aero_ioc) { ioc->base_readl = &_base_readl_aero; - else + ioc->base_readl_ext_retry = &_base_readl_ext_retry; + } else { ioc->base_readl = &_base_readl; + ioc->base_readl_ext_retry = &_base_readl; + } r = mpt3sas_base_map_resources(ioc); if (r) goto out_free_resources; pci_set_drvdata(ioc->pdev, ioc->shost); r = _base_get_ioc_facts(ioc); - if (r) - goto out_free_resources; + if (r) { + rc = mpt3sas_base_check_for_fault_and_issue_reset(ioc); + if (rc || (_base_get_ioc_facts(ioc))) + goto out_free_resources; + } switch (ioc->hba_mpi_version_belonged) { case MPI2_VERSION: ioc->build_sg_scmd = &_base_build_sg_scmd; ioc->build_sg = &_base_build_sg; ioc->build_zero_len_sge = &_base_build_zero_len_sge; + ioc->get_msix_index_for_smlio = &_base_get_msix_index; break; case MPI25_VERSION: case MPI26_VERSION: /* * In SAS3.0, * SCSI_IO, SMP_PASSTHRU, SATA_PASSTHRU, Target Assist, and - * Target Status - all require the IEEE formated scatter gather + * Target Status - all require the IEEE formatted scatter gather * elements. */ ioc->build_sg_scmd = &_base_build_sg_scmd_ieee; @@ -6458,15 +8482,30 @@ mpt3sas_base_attach(struct MPT3SAS_ADAPTER *ioc) ioc->build_nvme_prp = &_base_build_nvme_prp; ioc->build_zero_len_sge = &_base_build_zero_len_sge_ieee; ioc->sge_size_ieee = sizeof(Mpi2IeeeSgeSimple64_t); - + if (ioc->high_iops_queues) + ioc->get_msix_index_for_smlio = + &_base_get_high_iops_msix_index; + else + ioc->get_msix_index_for_smlio = &_base_get_msix_index; break; } - - if (ioc->is_mcpu_endpoint) - ioc->put_smid_scsi_io = &_base_put_smid_mpi_ep_scsi_io; - else - ioc->put_smid_scsi_io = &_base_put_smid_scsi_io; - + if (ioc->atomic_desc_capable) { + ioc->put_smid_default = &_base_put_smid_default_atomic; + ioc->put_smid_scsi_io = &_base_put_smid_scsi_io_atomic; + ioc->put_smid_fast_path = + &_base_put_smid_fast_path_atomic; + ioc->put_smid_hi_priority = + &_base_put_smid_hi_priority_atomic; + } else { + ioc->put_smid_default = &_base_put_smid_default; + ioc->put_smid_fast_path = &_base_put_smid_fast_path; + ioc->put_smid_hi_priority = &_base_put_smid_hi_priority; + if (ioc->is_mcpu_endpoint) + ioc->put_smid_scsi_io = + &_base_put_smid_mpi_ep_scsi_io; + else + ioc->put_smid_scsi_io = &_base_put_smid_scsi_io; + } /* * These function pointers for other requests that don't * the require IEEE scatter gather elements. @@ -6476,7 +8515,7 @@ mpt3sas_base_attach(struct MPT3SAS_ADAPTER *ioc) ioc->build_sg_mpi = &_base_build_sg; ioc->build_zero_len_sge_mpi = &_base_build_zero_len_sge; - r = _base_make_ioc_ready(ioc, SOFT_RESET); + r = mpt3sas_base_make_ioc_ready(ioc, SOFT_RESET); if (r) goto out_free_resources; @@ -6489,20 +8528,35 @@ mpt3sas_base_attach(struct MPT3SAS_ADAPTER *ioc) for (i = 0 ; i < ioc->facts.NumberOfPorts; i++) { r = _base_get_port_facts(ioc, i); - if (r) - goto out_free_resources; + if (r) { + rc = mpt3sas_base_check_for_fault_and_issue_reset(ioc); + if (rc || (_base_get_port_facts(ioc, i))) + goto out_free_resources; + } } r = _base_allocate_memory_pools(ioc); if (r) goto out_free_resources; + if (irqpoll_weight > 0) + ioc->thresh_hold = irqpoll_weight; + else + ioc->thresh_hold = ioc->hba_queue_depth/4; + + _base_init_irqpolls(ioc); init_waitqueue_head(&ioc->reset_wq); /* allocate memory pd handle bitmask list */ ioc->pd_handles_sz = (ioc->facts.MaxDevHandle / 8); if (ioc->facts.MaxDevHandle % 8) ioc->pd_handles_sz++; + /* + * pd_handles_sz should have, at least, the minimal room for + * set_bit()/test_bit(), otherwise out-of-memory touch may occur. + */ + ioc->pd_handles_sz = ALIGN(ioc->pd_handles_sz, sizeof(unsigned long)); + ioc->pd_handles = kzalloc(ioc->pd_handles_sz, GFP_KERNEL); if (!ioc->pd_handles) { @@ -6520,16 +8574,27 @@ mpt3sas_base_attach(struct MPT3SAS_ADAPTER *ioc) ioc->pend_os_device_add_sz = (ioc->facts.MaxDevHandle / 8); if (ioc->facts.MaxDevHandle % 8) ioc->pend_os_device_add_sz++; + + /* + * pend_os_device_add_sz should have, at least, the minimal room for + * set_bit()/test_bit(), otherwise out-of-memory may occur. + */ + ioc->pend_os_device_add_sz = ALIGN(ioc->pend_os_device_add_sz, + sizeof(unsigned long)); ioc->pend_os_device_add = kzalloc(ioc->pend_os_device_add_sz, GFP_KERNEL); - if (!ioc->pend_os_device_add) + if (!ioc->pend_os_device_add) { + r = -ENOMEM; goto out_free_resources; + } ioc->device_remove_in_progress_sz = ioc->pend_os_device_add_sz; ioc->device_remove_in_progress = kzalloc(ioc->device_remove_in_progress_sz, GFP_KERNEL); - if (!ioc->device_remove_in_progress) + if (!ioc->device_remove_in_progress) { + r = -ENOMEM; goto out_free_resources; + } ioc->fwfault_debug = mpt3sas_fwfault_debug; @@ -6603,10 +8668,21 @@ mpt3sas_base_attach(struct MPT3SAS_ADAPTER *ioc) } } r = _base_make_ioc_operational(ioc); - if (r) - goto out_free_resources; + if (r == -EAGAIN) { + r = _base_make_ioc_operational(ioc); + if (r) + goto out_free_resources; + } + + /* + * Copy current copy of IOCFacts in prev_fw_facts + * and it will be used during online firmware upgrade. + */ + memcpy(&ioc->prev_fw_facts, &ioc->facts, + sizeof(struct mpt3sas_facts)); ioc->non_operational_loop = 0; + ioc->ioc_coredump_loop = 0; ioc->got_task_abort_from_ioctl = 0; return 0; @@ -6688,14 +8764,14 @@ static void _base_pre_reset_handler(struct MPT3SAS_ADAPTER *ioc) } /** - * _base_after_reset_handler - after reset handler + * _base_clear_outstanding_mpt_commands - clears outstanding mpt commands * @ioc: per adapter object */ -static void _base_after_reset_handler(struct MPT3SAS_ADAPTER *ioc) +static void +_base_clear_outstanding_mpt_commands(struct MPT3SAS_ADAPTER *ioc) { - mpt3sas_scsih_after_reset_handler(ioc); - mpt3sas_ctl_after_reset_handler(ioc); - dtmprintk(ioc, ioc_info(ioc, "%s: MPT3_IOC_AFTER_RESET\n", __func__)); + dtmprintk(ioc, + ioc_info(ioc, "%s: clear outstanding mpt cmds\n", __func__)); if (ioc->transport_cmds.status & MPT3_CMD_PENDING) { ioc->transport_cmds.status |= MPT3_CMD_RESET; mpt3sas_base_free_smid(ioc, ioc->transport_cmds.smid); @@ -6714,8 +8790,6 @@ static void _base_after_reset_handler(struct MPT3SAS_ADAPTER *ioc) ioc->start_scan_failed = MPI2_IOCSTATUS_INTERNAL_ERROR; ioc->start_scan = 0; - ioc->port_enable_cmds.status = - MPT3_CMD_NOT_USED; } else { complete(&ioc->port_enable_cmds.done); } @@ -6729,6 +8803,17 @@ static void _base_after_reset_handler(struct MPT3SAS_ADAPTER *ioc) } /** + * _base_clear_outstanding_commands - clear all outstanding commands + * @ioc: per adapter object + */ +static void _base_clear_outstanding_commands(struct MPT3SAS_ADAPTER *ioc) +{ + mpt3sas_scsih_clear_outstanding_scsi_tm_commands(ioc); + mpt3sas_ctl_clear_outstanding_ioctls(ioc); + _base_clear_outstanding_mpt_commands(ioc); +} + +/** * _base_reset_done_handler - reset done handler * @ioc: per adapter object */ @@ -6768,6 +8853,90 @@ mpt3sas_wait_for_commands_to_complete(struct MPT3SAS_ADAPTER *ioc) } /** + * _base_check_ioc_facts_changes - Look for increase/decrease of IOCFacts + * attributes during online firmware upgrade and update the corresponding + * IOC variables accordingly. + * + * @ioc: Pointer to MPT_ADAPTER structure + */ +static int +_base_check_ioc_facts_changes(struct MPT3SAS_ADAPTER *ioc) +{ + u16 pd_handles_sz; + void *pd_handles = NULL, *blocking_handles = NULL; + void *pend_os_device_add = NULL, *device_remove_in_progress = NULL; + struct mpt3sas_facts *old_facts = &ioc->prev_fw_facts; + + if (ioc->facts.MaxDevHandle > old_facts->MaxDevHandle) { + pd_handles_sz = (ioc->facts.MaxDevHandle / 8); + if (ioc->facts.MaxDevHandle % 8) + pd_handles_sz++; + + /* + * pd_handles should have, at least, the minimal room for + * set_bit()/test_bit(), otherwise out-of-memory touch may + * occur. + */ + pd_handles_sz = ALIGN(pd_handles_sz, sizeof(unsigned long)); + pd_handles = krealloc(ioc->pd_handles, pd_handles_sz, + GFP_KERNEL); + if (!pd_handles) { + ioc_info(ioc, + "Unable to allocate the memory for pd_handles of sz: %d\n", + pd_handles_sz); + return -ENOMEM; + } + memset(pd_handles + ioc->pd_handles_sz, 0, + (pd_handles_sz - ioc->pd_handles_sz)); + ioc->pd_handles = pd_handles; + + blocking_handles = krealloc(ioc->blocking_handles, + pd_handles_sz, GFP_KERNEL); + if (!blocking_handles) { + ioc_info(ioc, + "Unable to allocate the memory for " + "blocking_handles of sz: %d\n", + pd_handles_sz); + return -ENOMEM; + } + memset(blocking_handles + ioc->pd_handles_sz, 0, + (pd_handles_sz - ioc->pd_handles_sz)); + ioc->blocking_handles = blocking_handles; + ioc->pd_handles_sz = pd_handles_sz; + + pend_os_device_add = krealloc(ioc->pend_os_device_add, + pd_handles_sz, GFP_KERNEL); + if (!pend_os_device_add) { + ioc_info(ioc, + "Unable to allocate the memory for pend_os_device_add of sz: %d\n", + pd_handles_sz); + return -ENOMEM; + } + memset(pend_os_device_add + ioc->pend_os_device_add_sz, 0, + (pd_handles_sz - ioc->pend_os_device_add_sz)); + ioc->pend_os_device_add = pend_os_device_add; + ioc->pend_os_device_add_sz = pd_handles_sz; + + device_remove_in_progress = krealloc( + ioc->device_remove_in_progress, pd_handles_sz, GFP_KERNEL); + if (!device_remove_in_progress) { + ioc_info(ioc, + "Unable to allocate the memory for device_remove_in_progress of sz: %d\n", + pd_handles_sz); + return -ENOMEM; + } + memset(device_remove_in_progress + + ioc->device_remove_in_progress_sz, 0, + (pd_handles_sz - ioc->device_remove_in_progress_sz)); + ioc->device_remove_in_progress = device_remove_in_progress; + ioc->device_remove_in_progress_sz = pd_handles_sz; + } + + memcpy(&ioc->prev_fw_facts, &ioc->facts, sizeof(struct mpt3sas_facts)); + return 0; +} + +/** * mpt3sas_base_hard_reset_handler - reset controller * @ioc: Pointer to MPT_ADAPTER structure * @type: FORCE_BIG_HAMMER or SOFT_RESET @@ -6807,16 +8976,22 @@ mpt3sas_base_hard_reset_handler(struct MPT3SAS_ADAPTER *ioc, MPT3_DIAG_BUFFER_IS_RELEASED))) { is_trigger = 1; ioc_state = mpt3sas_base_get_iocstate(ioc, 0); - if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) + if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT || + (ioc_state & MPI2_IOC_STATE_MASK) == + MPI2_IOC_STATE_COREDUMP) { is_fault = 1; + ioc->htb_rel.trigger_info_dwords[1] = + (ioc_state & MPI2_DOORBELL_DATA_MASK); + } } _base_pre_reset_handler(ioc); mpt3sas_wait_for_commands_to_complete(ioc); - _base_mask_interrupts(ioc); - r = _base_make_ioc_ready(ioc, type); + mpt3sas_base_mask_interrupts(ioc); + mpt3sas_base_pause_mq_polling(ioc); + r = mpt3sas_base_make_ioc_ready(ioc, type); if (r) goto out; - _base_after_reset_handler(ioc); + _base_clear_outstanding_commands(ioc); /* If this hard reset is called while port enable is active, then * there is no reason to call make_ioc_operational @@ -6830,6 +9005,13 @@ mpt3sas_base_hard_reset_handler(struct MPT3SAS_ADAPTER *ioc, if (r) goto out; + r = _base_check_ioc_facts_changes(ioc); + if (r) { + ioc_info(ioc, + "Some of the parameters got changed in this new firmware" + " image and it requires system reboot\n"); + goto out; + } if (ioc->rdpq_array_enable && !ioc->rdpq_array_capable) panic("%s: Issue occurred with flashing controller firmware." "Please reboot the system and ensure that the correct" @@ -6840,15 +9022,14 @@ mpt3sas_base_hard_reset_handler(struct MPT3SAS_ADAPTER *ioc, _base_reset_done_handler(ioc); out: - dtmprintk(ioc, - ioc_info(ioc, "%s: %s\n", - __func__, r == 0 ? "SUCCESS" : "FAILED")); + ioc_info(ioc, "%s: %s\n", __func__, r == 0 ? "SUCCESS" : "FAILED"); spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); ioc->shost_recovery = 0; spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); ioc->ioc_reset_count++; mutex_unlock(&ioc->reset_in_progress_mutex); + mpt3sas_base_resume_mq_polling(ioc); out_unlocked: if ((r == 0) && is_trigger) { diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.h b/drivers/scsi/mpt3sas/mpt3sas_base.h index 800351932cc3..e6a6f21d309b 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.h +++ b/drivers/scsi/mpt3sas/mpt3sas_base.h @@ -67,19 +67,21 @@ #include <scsi/scsi_eh.h> #include <linux/pci.h> #include <linux/poll.h> +#include <linux/irq_poll.h> #include "mpt3sas_debug.h" #include "mpt3sas_trigger_diag.h" +#include "mpt3sas_trigger_pages.h" /* driver versioning info */ #define MPT3SAS_DRIVER_NAME "mpt3sas" #define MPT3SAS_AUTHOR "Avago Technologies <MPT-FusionLinux.pdl@avagotech.com>" #define MPT3SAS_DESCRIPTION "LSI MPT Fusion SAS 3.0 Device Driver" -#define MPT3SAS_DRIVER_VERSION "27.101.00.00" -#define MPT3SAS_MAJOR_VERSION 27 -#define MPT3SAS_MINOR_VERSION 101 -#define MPT3SAS_BUILD_VERSION 0 -#define MPT3SAS_RELEASE_VERSION 00 +#define MPT3SAS_DRIVER_VERSION "54.100.00.00" +#define MPT3SAS_MAJOR_VERSION 54 +#define MPT3SAS_MINOR_VERSION 100 +#define MPT3SAS_BUILD_VERSION 00 +#define MPT3SAS_RELEASE_VERSION 00 #define MPT2SAS_DRIVER_NAME "mpt2sas" #define MPT2SAS_DESCRIPTION "LSI MPT Fusion SAS 2.0 Device Driver" @@ -89,6 +91,18 @@ #define MPT2SAS_BUILD_VERSION 0 #define MPT2SAS_RELEASE_VERSION 00 +/* CoreDump: Default timeout */ +#define MPT3SAS_DEFAULT_COREDUMP_TIMEOUT_SECONDS (15) /*15 seconds*/ +#define MPT3SAS_COREDUMP_LOOP_DONE (0xFF) +#define MPT3SAS_TIMESYNC_TIMEOUT_SECONDS (10) /* 10 seconds */ +#define MPT3SAS_TIMESYNC_UPDATE_INTERVAL (900) /* 15 minutes */ +#define MPT3SAS_TIMESYNC_UNIT_MASK (0x80) /* bit 7 */ +#define MPT3SAS_TIMESYNC_MASK (0x7F) /* 0 - 6 bits */ +#define SECONDS_PER_MIN (60) +#define SECONDS_PER_HOUR (3600) +#define MPT3SAS_COREDUMP_LOOP_DONE (0xFF) +#define MPI26_SET_IOC_PARAMETER_SYNC_TIMESTAMP (0x81) + /* * Set MPT3SAS_SG_DEPTH value based on user input. */ @@ -128,6 +142,8 @@ #define MPT_MAX_CALLBACKS 32 +#define MPT_MAX_HBA_NUM_PHYS 32 + #define INTERNAL_CMDS_COUNT 10 /* reserved cmds */ /* reserved for issuing internally framed scsi io cmds */ #define INTERNAL_SCSIIO_CMDS_COUNT 3 @@ -139,6 +155,7 @@ #define MAX_CHAIN_ELEMT_SZ 16 #define DEFAULT_NUM_FWCHAIN_ELEMTS 8 +#define IO_UNIT_CONTROL_SHUTDOWN_TIMEOUT 6 #define FW_IMG_HDR_READ_TIMEOUT 15 #define IOC_OPERATIONAL_WAIT_COUNT 10 @@ -193,6 +210,9 @@ struct mpt3sas_nvme_cmd { #define SAS2_PCI_DEVICE_B0_REVISION (0x01) #define SAS3_PCI_DEVICE_C0_REVISION (0x02) +/* Atlas PCIe Switch Management Port */ +#define MPI26_ATLAS_PCIe_SWITCH_DEVID (0x00B2) + /* * Intel HBA branding */ @@ -299,6 +319,8 @@ struct mpt3sas_nvme_cmd { #define MPT3_DIAG_BUFFER_IS_REGISTERED (0x01) #define MPT3_DIAG_BUFFER_IS_RELEASED (0x02) #define MPT3_DIAG_BUFFER_IS_DIAG_RESET (0x04) +#define MPT3_DIAG_BUFFER_IS_DRIVER_ALLOCATED (0x08) +#define MPT3_DIAG_BUFFER_IS_APP_OWNED (0x10) /* * HP HBA branding @@ -334,6 +356,7 @@ struct mpt3sas_nvme_cmd { #define MPT3_SUP_REPLY_POST_HOST_INDEX_REG_COUNT_G3 12 #define MPT3_SUP_REPLY_POST_HOST_INDEX_REG_COUNT_G35 16 #define MPT3_SUP_REPLY_POST_HOST_INDEX_REG_OFFSET (0x10) +#define MPT3_MIN_IRQS 1 /* OEM Identifiers */ #define MFG10_OEM_ID_INVALID (0x00000000) @@ -351,6 +374,13 @@ struct mpt3sas_nvme_cmd { #define VIRTUAL_IO_FAILED_RETRY (0x32010081) +/* High IOPs definitions */ +#define MPT3SAS_DEVICE_HIGH_IOPS_DEPTH 8 +#define MPT3SAS_HIGH_IOPS_REPLY_QUEUES 8 +#define MPT3SAS_HIGH_IOPS_BATCH_COUNT 16 +#define MPT3SAS_GEN35_MAX_MSIX_QUEUES 128 +#define RDPQ_MAX_INDEX_IN_ONE_CHUNK 16 + /* OEM Specific Flags will come from OEM specific header files */ struct Mpi2ManufacturingPage10_t { MPI2_CONFIG_PAGE_HEADER Header; /* 00h */ @@ -381,9 +411,15 @@ struct Mpi2ManufacturingPage11_t { u8 Reserved6; /* 2Fh */ __le32 Reserved7[7]; /* 30h - 4Bh */ u8 NVMeAbortTO; /* 4Ch */ - u8 Reserved8; /* 4Dh */ - u16 Reserved9; /* 4Eh */ - __le32 Reserved10[4]; /* 50h - 60h */ + u8 NumPerDevEvents; /* 4Dh */ + u8 HostTraceBufferDecrementSizeKB; /* 4Eh */ + u8 HostTraceBufferFlags; /* 4Fh */ + u16 HostTraceBufferMaxSizeKB; /* 50h */ + u16 HostTraceBufferMinSizeKB; /* 52h */ + u8 CoreDumpTOSec; /* 54h */ + u8 TimeSyncInterval; /* 55h */ + u16 Reserved9; /* 56h */ + __le32 Reserved10; /* 58h */ }; /** @@ -396,6 +432,7 @@ struct Mpi2ManufacturingPage11_t { * @flags: MPT_TARGET_FLAGS_XXX flags * @deleted: target flaged for deletion * @tm_busy: target is busy with TM request. + * @port: hba port entry containing target's port number info * @sas_dev: The sas_device associated with this target * @pcie_dev: The pcie device associated with this target */ @@ -408,6 +445,7 @@ struct MPT3SAS_TARGET { u32 flags; u8 deleted; u8 tm_busy; + struct hba_port *port; struct _sas_device *sas_dev; struct _pcie_device *pcie_dev; }; @@ -465,6 +503,7 @@ struct MPT3SAS_DEVICE { #define MPT3_CMD_PENDING 0x0002 /* pending */ #define MPT3_CMD_REPLY_VALID 0x0004 /* reply is valid */ #define MPT3_CMD_RESET 0x0008 /* host reset dropped the command */ +#define MPT3_CMD_COMPLETE_ASYNC 0x0010 /* tells whether cmd completes in same thread or not */ /** * struct _internal_cmd - internal commands struct @@ -510,6 +549,9 @@ struct _internal_cmd { * addition routine. * @chassis_slot: chassis slot * @is_chassis_slot_valid: chassis slot valid or not + * @port: hba port entry containing device's port number info + * @rphy: device's sas_rphy address used to identify this device structure in + * target_alloc callback function */ struct _sas_device { struct list_head list; @@ -536,6 +578,9 @@ struct _sas_device { u8 is_chassis_slot_valid; u8 connector_name[5]; struct kref refcount; + u8 port_type; + struct hba_port *port; + struct sas_rphy *rphy; }; static inline void sas_device_get(struct _sas_device *s) @@ -573,6 +618,8 @@ static inline void sas_device_put(struct _sas_device *s) * @enclosure_level: The level of device's enclosure from the controller * @connector_name: ASCII value of the Connector's name * @serial_number: pointer of serial number string allocated runtime + * @access_status: Device's Access Status + * @shutdown_latency: NVMe device's RTD3 Entry Latency * @refcount: reference count for deletion */ struct _pcie_device { @@ -594,6 +641,8 @@ struct _pcie_device { u8 connector_name[4]; u8 *serial_number; u8 reset_timeout; + u8 access_status; + u16 shutdown_latency; struct kref refcount; }; /** @@ -702,6 +751,7 @@ struct _boot_device { * @remote_identify: attached device identification * @rphy: sas transport rphy object * @port: sas transport wide/narrow port object + * @hba_port: hba port entry containing port's port number info * @phy_list: _sas_phy list objects belonging to this port */ struct _sas_port { @@ -710,6 +760,7 @@ struct _sas_port { struct sas_identify remote_identify; struct sas_rphy *rphy; struct sas_port *port; + struct hba_port *hba_port; struct list_head phy_list; }; @@ -723,6 +774,7 @@ struct _sas_port { * @handle: device handle for this phy * @attached_handle: device handle for attached device * @phy_belongs_to_port: port has been created for this phy + * @port: hba port entry containing port number info */ struct _sas_phy { struct list_head port_siblings; @@ -733,6 +785,8 @@ struct _sas_phy { u16 handle; u16 attached_handle; u8 phy_belongs_to_port; + u8 hba_vphy; + struct hba_port *port; }; /** @@ -746,8 +800,11 @@ struct _sas_phy { * @enclosure_handle: handle for this a member of an enclosure * @device_info: bitwise defining capabilities of this sas_host/expander * @responding: used in _scsih_expander_device_mark_responding + * @nr_phys_allocated: Allocated memory for this many count phys * @phy: a list of phys that make up this sas_host/expander * @sas_port_list: list of ports attached to this sas_host/expander + * @port: hba port entry containing node's port number info + * @rphy: sas_rphy object of this expander */ struct _sas_node { struct list_head list; @@ -759,11 +816,13 @@ struct _sas_node { u16 enclosure_handle; u64 enclosure_logical_id; u8 responding; + u8 nr_phys_allocated; + struct hba_port *port; struct _sas_phy *phy; struct list_head sas_port_list; + struct sas_rphy *rphy; }; - /** * struct _enclosure_node - enclosure information * @list: list of enclosures @@ -820,6 +879,7 @@ struct chain_lookup { */ struct scsiio_tracker { u16 smid; + struct scsi_cmnd *scmd; u8 cb_idx; u8 direct_io; struct pcie_sg_list pcie_sg_list; @@ -879,6 +939,11 @@ struct _event_ack_list { * @reply_post_free: reply post base virt address * @name: the name registered to request_irq() * @busy: isr is actively processing replies on another cpu + * @os_irq: irq number + * @irqpoll: irq_poll object + * @irq_poll_scheduled: Tells whether irq poll is scheduled or not + * @is_iouring_poll_q: Tells whether reply queues is assigned + * to io uring poll queues or not * @list: this list */ struct adapter_reply_queue { @@ -888,9 +953,26 @@ struct adapter_reply_queue { Mpi2ReplyDescriptorsUnion_t *reply_post_free; char name[MPT_NAME_LENGTH]; atomic_t busy; + u32 os_irq; + struct irq_poll irqpoll; + bool irq_poll_scheduled; + bool irq_line_enable; + bool is_iouring_poll_q; struct list_head list; }; +/** + * struct io_uring_poll_queue - the io uring poll queue structure + * @busy: Tells whether io uring poll queue is busy or not + * @pause: Tells whether IOs are paused on io uring poll queue or not + * @reply_q: reply queue mapped for io uring poll queue + */ +struct io_uring_poll_queue { + atomic_t busy; + atomic_t pause; + struct adapter_reply_queue *reply_q; +}; + typedef void (*MPT_ADD_SGE)(void *paddr, u32 flags_length, dma_addr_t dma_addr); /* SAS3.0 support */ @@ -912,7 +994,13 @@ typedef void (*NVME_BUILD_PRP)(struct MPT3SAS_ADAPTER *ioc, u16 smid, typedef void (*PUT_SMID_IO_FP_HIP) (struct MPT3SAS_ADAPTER *ioc, u16 smid, u16 funcdep); typedef void (*PUT_SMID_DEFAULT) (struct MPT3SAS_ADAPTER *ioc, u16 smid); -typedef u32 (*BASE_READ_REG) (const volatile void __iomem *addr); +typedef u32 (*BASE_READ_REG) (const void __iomem *addr); +/* + * To get high iops reply queue's msix index when high iops mode is enabled + * else get the msix index of general reply queues. + */ +typedef u8 (*GET_MSIX_INDEX) (struct MPT3SAS_ADAPTER *ioc, + struct scsi_cmnd *scmd); /* IOC Facts and Port Facts converted from little endian to cpu */ union mpi3_version_union { @@ -967,6 +1055,90 @@ struct reply_post_struct { dma_addr_t reply_post_free_dma; }; +/** + * struct virtual_phy - vSES phy structure + * sas_address: SAS Address of vSES device + * phy_mask: vSES device's phy number + * flags: flags used to manage this structure + */ +struct virtual_phy { + struct list_head list; + u64 sas_address; + u32 phy_mask; + u8 flags; +}; + +#define MPT_VPHY_FLAG_DIRTY_PHY 0x01 + +/** + * struct hba_port - Saves each HBA's Wide/Narrow port info + * @sas_address: sas address of this wide/narrow port's attached device + * @phy_mask: HBA PHY's belonging to this port + * @port_id: port number + * @flags: hba port flags + * @vphys_mask : mask of vSES devices Phy number + * @vphys_list : list containing vSES device structures + */ +struct hba_port { + struct list_head list; + u64 sas_address; + u32 phy_mask; + u8 port_id; + u8 flags; + u32 vphys_mask; + struct list_head vphys_list; +}; + +/* hba port flags */ +#define HBA_PORT_FLAG_DIRTY_PORT 0x01 +#define HBA_PORT_FLAG_NEW_PORT 0x02 + +#define MULTIPATH_DISABLED_PORT_ID 0xFF + +/** + * struct htb_rel_query - diagnostic buffer release reason + * @unique_id - unique id associated with this buffer. + * @buffer_rel_condition - Release condition ioctl/sysfs/reset + * @reserved + * @trigger_type - Master/Event/scsi/MPI + * @trigger_info_dwords - Data Correspondig to trigger type + */ +struct htb_rel_query { + u16 buffer_rel_condition; + u16 reserved; + u32 trigger_type; + u32 trigger_info_dwords[2]; +}; + +/* Buffer_rel_condition bit fields */ + +/* Bit 0 - Diag Buffer not Released */ +#define MPT3_DIAG_BUFFER_NOT_RELEASED (0x00) +/* Bit 0 - Diag Buffer Released */ +#define MPT3_DIAG_BUFFER_RELEASED (0x01) + +/* + * Bit 1 - Diag Buffer Released by IOCTL, + * This bit is valid only if Bit 0 is one + */ +#define MPT3_DIAG_BUFFER_REL_IOCTL (0x02 | MPT3_DIAG_BUFFER_RELEASED) + +/* + * Bit 2 - Diag Buffer Released by Trigger, + * This bit is valid only if Bit 0 is one + */ +#define MPT3_DIAG_BUFFER_REL_TRIGGER (0x04 | MPT3_DIAG_BUFFER_RELEASED) + +/* + * Bit 3 - Diag Buffer Released by SysFs, + * This bit is valid only if Bit 0 is one + */ +#define MPT3_DIAG_BUFFER_REL_SYSFS (0x08 | MPT3_DIAG_BUFFER_RELEASED) + +/* DIAG RESET Master trigger flags */ +#define MPT_DIAG_RESET_ISSUED_BY_DRIVER 0x00000000 +#define MPT_DIAG_RESET_ISSUED_BY_USER 0x00000001 + typedef void (*MPT3SAS_FLUSH_RUNNING_CMDS)(struct MPT3SAS_ADAPTER *ioc); /** * struct MPT3SAS_ADAPTER - per adapter struct @@ -985,16 +1157,16 @@ typedef void (*MPT3SAS_FLUSH_RUNNING_CMDS)(struct MPT3SAS_ADAPTER *ioc); * @ir_firmware: IR firmware present * @bars: bitmask of BAR's that must be configured * @mask_interrupts: ignore interrupt - * @dma_mask: used to set the consistent dma mask * @pci_access_mutex: Mutex to synchronize ioctl, sysfs show path and * pci resource handling * @fault_reset_work_q_name: fw fault work queue * @fault_reset_work_q: "" * @fault_reset_work: "" - * @firmware_event_name: fw event work queue - * @firmware_event_thread: "" + * @firmware_event_thread: fw event work queue * @fw_event_lock: * @fw_event_list: list of fw events + * @current_evet: current processing firmware event + * @fw_event_cleanup: set to one while cleaning up the fw events * @aen_event_read_flag: event log was read * @broadcast_aen_busy: broadcast aen waiting to be serviced * @shost_recovery: host reset in progress @@ -1013,7 +1185,22 @@ typedef void (*MPT3SAS_FLUSH_RUNNING_CMDS)(struct MPT3SAS_ADAPTER *ioc); * @msix_vector_count: number msix vectors * @cpu_msix_table: table for mapping cpus to msix index * @cpu_msix_table_sz: table size + * @total_io_cnt: Gives total IO count, used to load balance the interrupts + * @ioc_coredump_loop: will have non-zero value when FW is in CoreDump state + * @timestamp_update_count: Counter to fire timeSync command + * time_sync_interval: Time sync interval read from man page 11 + * @high_iops_outstanding: used to load balance the interrupts + * within high iops reply queues + * @msix_load_balance: Enables load balancing of interrupts across + * the multiple MSIXs * @schedule_dead_ioc_flush_running_cmds: callback to flush pending commands + * @thresh_hold: Max number of reply descriptors processed + * before updating Host Index + * @iopoll_q_start_index: starting index of io uring poll queues + * in reply queue list + * @drv_internal_flags: Bit map internal to driver + * @drv_support_bitmap: driver's supported feature bit map + * @use_32bit_dma: Flag to use 32 bit consistent dma mask * @scsi_io_cb_idx: shost generated commands * @tm_cb_idx: task management commands * @scsih_cb_idx: scsih internal commands @@ -1034,7 +1221,12 @@ typedef void (*MPT3SAS_FLUSH_RUNNING_CMDS)(struct MPT3SAS_ADAPTER *ioc); * @event_context: unique id for each logged event * @event_log: event log pointer * @event_masks: events that are masked + * @max_shutdown_latency: timeout value for NVMe shutdown operation, + * which is equal that NVMe drive's RTD3 Entry Latency + * which has reported maximum RTD3 Entry Latency value + * among attached NVMe drives. * @facts: static facts data + * @prev_fw_facts: previous fw facts data * @pfacts: static port facts data * @manu_pg0: static manufacturing page 0 * @manu_pg10: static manufacturing page 10 @@ -1044,7 +1236,6 @@ typedef void (*MPT3SAS_FLUSH_RUNNING_CMDS)(struct MPT3SAS_ADAPTER *ioc); * @ioc_pg8: static ioc page 8 * @iounit_pg0: static iounit page 0 * @iounit_pg1: static iounit page 1 - * @iounit_pg8: static iounit page 8 * @sas_hba: sas host object * @sas_expander_list: expander object list * @enclosure_list: enclosure object list @@ -1131,6 +1322,10 @@ typedef void (*MPT3SAS_FLUSH_RUNNING_CMDS)(struct MPT3SAS_ADAPTER *ioc); * path functions resulting in Null pointer reference followed by kernel * crash. To avoid the above race condition we use mutex syncrhonization * which ensures the syncrhonization between cli/sysfs_show path. + * @atomic_desc_capable: Atomic Request Descriptor support. + * @GET_MSIX_INDEX: Get the msix index of high iops queues. + * @multipath_on_hba: flag to determine multipath on hba is enabled or not + * @port_table_list: list containing HBA's wide/narrow port's info */ struct MPT3SAS_ADAPTER { struct list_head list; @@ -1148,7 +1343,6 @@ struct MPT3SAS_ADAPTER { u8 ir_firmware; int bars; u8 mask_interrupts; - int dma_mask; /* fw fault handler */ char fault_reset_work_q_name[20]; @@ -1156,10 +1350,11 @@ struct MPT3SAS_ADAPTER { struct delayed_work fault_reset_work; /* fw event handler */ - char firmware_event_name[20]; struct workqueue_struct *firmware_event_thread; spinlock_t fw_event_lock; struct list_head fw_event_list; + struct fw_event_work *current_event; + u8 fw_events_cleanup; /* misc flags */ int aen_event_read_flag; @@ -1169,6 +1364,7 @@ struct MPT3SAS_ADAPTER { u8 got_task_abort_from_ioctl; struct mutex reset_in_progress_mutex; + struct mutex hostdiag_unlock_mutex; spinlock_t ioc_reset_in_progress_lock; u8 ioc_link_reset_in_progress; @@ -1189,6 +1385,21 @@ struct MPT3SAS_ADAPTER { u32 ioc_reset_count; MPT3SAS_FLUSH_RUNNING_CMDS schedule_dead_ioc_flush_running_cmds; u32 non_operational_loop; + u8 ioc_coredump_loop; + u32 timestamp_update_count; + u32 time_sync_interval; + atomic64_t total_io_cnt; + atomic64_t high_iops_outstanding; + bool msix_load_balance; + u16 thresh_hold; + u8 high_iops_queues; + u8 iopoll_q_start_index; + u32 drv_internal_flags; + u32 drv_support_bitmap; + u32 dma_mask; + bool enable_sdev_max_qd; + bool use_32bit_dma; + struct io_uring_poll_queue *io_uring_poll_queues; /* internal commands, callback index */ u8 scsi_io_cb_idx; @@ -1234,10 +1445,15 @@ struct MPT3SAS_ADAPTER { u8 tm_custom_handling; u8 nvme_abort_timeout; - + u16 max_shutdown_latency; + u16 max_wideport_qd; + u16 max_narrowport_qd; + u16 max_nvme_qd; + u8 max_sata_qd; /* static config pages */ struct mpt3sas_facts facts; + struct mpt3sas_facts prev_fw_facts; struct mpt3sas_port_facts *pfacts; Mpi2ManufacturingPage0_t manu_pg0; struct Mpi2ManufacturingPage10_t manu_pg10; @@ -1247,7 +1463,7 @@ struct MPT3SAS_ADAPTER { Mpi2IOCPage8_t ioc_pg8; Mpi2IOUnitPage0_t iounit_pg0; Mpi2IOUnitPage1_t iounit_pg1; - Mpi2IOUnitPage8_t iounit_pg8; + Mpi2IOCPage1_t ioc_pg1_copy; struct _boot_device req_boot_device; struct _boot_device req_alt_boot_device; @@ -1297,6 +1513,7 @@ struct MPT3SAS_ADAPTER { spinlock_t scsi_lookup_lock; int pending_io_count; wait_queue_head_t reset_wq; + u16 *io_queue_num; /* PCIe SGL */ struct dma_pool *pcie_sgl_dma_pool; @@ -1366,8 +1583,9 @@ struct MPT3SAS_ADAPTER { u8 combined_reply_queue; u8 combined_reply_index_count; + u8 smp_affinity_enable; /* reply post register index */ - resource_size_t **replyPostRegisterIndex; + resource_size_t __iomem **replyPostRegisterIndex; struct list_head delayed_tr_list; struct list_head delayed_tr_volume_list; @@ -1386,6 +1604,8 @@ struct MPT3SAS_ADAPTER { u32 diagnostic_flags[MPI2_DIAG_BUF_TYPE_COUNT]; u32 ring_buffer_offset; u32 ring_buffer_sz; + struct htb_rel_query htb_rel; + u8 reset_from_user; u8 is_warpdrive; u8 is_mcpu_endpoint; u8 hide_ir_msg; @@ -1393,22 +1613,69 @@ struct MPT3SAS_ADAPTER { u8 hide_drives; spinlock_t diag_trigger_lock; u8 diag_trigger_active; + u8 atomic_desc_capable; BASE_READ_REG base_readl; + BASE_READ_REG base_readl_ext_retry; struct SL_WH_MASTER_TRIGGER_T diag_trigger_master; struct SL_WH_EVENT_TRIGGERS_T diag_trigger_event; struct SL_WH_SCSI_TRIGGERS_T diag_trigger_scsi; struct SL_WH_MPI_TRIGGERS_T diag_trigger_mpi; + u8 supports_trigger_pages; void *device_remove_in_progress; u16 device_remove_in_progress_sz; u8 is_gen35_ioc; u8 is_aero_ioc; + struct dentry *debugfs_root; + struct dentry *ioc_dump; PUT_SMID_IO_FP_HIP put_smid_scsi_io; + PUT_SMID_IO_FP_HIP put_smid_fast_path; + PUT_SMID_IO_FP_HIP put_smid_hi_priority; + PUT_SMID_DEFAULT put_smid_default; + GET_MSIX_INDEX get_msix_index_for_smlio; + u8 multipath_on_hba; + struct list_head port_table_list; }; +struct mpt3sas_debugfs_buffer { + void *buf; + u32 len; +}; + +#define MPT_DRV_SUPPORT_BITMAP_MEMMOVE 0x00000001 +#define MPT_DRV_SUPPORT_BITMAP_ADDNLQUERY 0x00000002 + +#define MPT_DRV_INTERNAL_FIRST_PE_ISSUED 0x00000001 + typedef u8 (*MPT_CALLBACK)(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply); +/* + * struct ATTO_SAS_NVRAM - ATTO NVRAM settings stored + * in Manufacturing page 1 used to get + * ATTO SasAddr. + */ +struct ATTO_SAS_NVRAM { + u8 Signature[4]; + u8 Version; +#define ATTO_SASNVR_VERSION 0 + + u8 Checksum; +#define ATTO_SASNVR_CKSUM_SEED 0x5A + u8 Pad[10]; + u8 SasAddr[8]; +#define ATTO_SAS_ADDR_ALIGN 64 + u8 Reserved[232]; +}; + +#define ATTO_SAS_ADDR_DEVNAME_BIAS 63 + +union ATTO_SAS_ADDRESS { + U8 b[8]; + U16 w[4]; + U32 d[2]; + U64 q; +}; /* base shared API */ extern struct list_head mpt3sas_ioc_list; @@ -1442,7 +1709,9 @@ __le32 mpt3sas_base_get_sense_buffer_dma(struct MPT3SAS_ADAPTER *ioc, u16 smid); void *mpt3sas_base_get_pcie_sgl(struct MPT3SAS_ADAPTER *ioc, u16 smid); dma_addr_t mpt3sas_base_get_pcie_sgl_dma(struct MPT3SAS_ADAPTER *ioc, u16 smid); -void mpt3sas_base_sync_reply_irqs(struct MPT3SAS_ADAPTER *ioc); +void mpt3sas_base_sync_reply_irqs(struct MPT3SAS_ADAPTER *ioc, u8 poll); +void mpt3sas_base_mask_interrupts(struct MPT3SAS_ADAPTER *ioc); +void mpt3sas_base_unmask_interrupts(struct MPT3SAS_ADAPTER *ioc); void mpt3sas_base_put_smid_fast_path(struct MPT3SAS_ADAPTER *ioc, u16 smid, u16 handle); @@ -1473,6 +1742,17 @@ void *mpt3sas_base_get_reply_virt_addr(struct MPT3SAS_ADAPTER *ioc, u32 mpt3sas_base_get_iocstate(struct MPT3SAS_ADAPTER *ioc, int cooked); void mpt3sas_base_fault_info(struct MPT3SAS_ADAPTER *ioc , u16 fault_code); +#define mpt3sas_print_fault_code(ioc, fault_code) \ +do { pr_err("%s fault info from func: %s\n", ioc->name, __func__); \ + mpt3sas_base_fault_info(ioc, fault_code); } while (0) + +void mpt3sas_base_coredump_info(struct MPT3SAS_ADAPTER *ioc, u16 fault_code); +#define mpt3sas_print_coredump_info(ioc, fault_code) \ +do { pr_err("%s fault info from func: %s\n", ioc->name, __func__); \ + mpt3sas_base_coredump_info(ioc, fault_code); } while (0) + +int mpt3sas_base_wait_for_coredump_completion(struct MPT3SAS_ADAPTER *ioc, + const char *caller); int mpt3sas_base_sas_iounit_control(struct MPT3SAS_ADAPTER *ioc, Mpi2SasIoUnitControlReply_t *mpi_reply, Mpi2SasIoUnitControlRequest_t *mpi_request); @@ -1487,6 +1767,9 @@ void mpt3sas_halt_firmware(struct MPT3SAS_ADAPTER *ioc); void mpt3sas_base_update_missing_delay(struct MPT3SAS_ADAPTER *ioc, u16 device_missing_delay, u8 io_missing_delay); +int mpt3sas_base_check_for_fault_and_issue_reset( + struct MPT3SAS_ADAPTER *ioc); + int mpt3sas_port_enable(struct MPT3SAS_ADAPTER *ioc); void @@ -1494,7 +1777,21 @@ mpt3sas_wait_for_commands_to_complete(struct MPT3SAS_ADAPTER *ioc); u8 mpt3sas_base_check_cmd_timeout(struct MPT3SAS_ADAPTER *ioc, u8 status, void *mpi_request, int sz); +#define mpt3sas_check_cmd_timeout(ioc, status, mpi_request, sz, issue_reset) \ +do { ioc_err(ioc, "In func: %s\n", __func__); \ + issue_reset = mpt3sas_base_check_cmd_timeout(ioc, \ + status, mpi_request, sz); } while (0) + int mpt3sas_wait_for_ioc(struct MPT3SAS_ADAPTER *ioc, int wait_count); +int mpt3sas_base_make_ioc_ready(struct MPT3SAS_ADAPTER *ioc, enum reset_type type); +void mpt3sas_base_free_irq(struct MPT3SAS_ADAPTER *ioc); +void mpt3sas_base_disable_msix(struct MPT3SAS_ADAPTER *ioc); +int mpt3sas_blk_mq_poll(struct Scsi_Host *shost, unsigned int queue_num); +void mpt3sas_base_pause_mq_polling(struct MPT3SAS_ADAPTER *ioc); +void mpt3sas_base_resume_mq_polling(struct MPT3SAS_ADAPTER *ioc); +int mpt3sas_base_unlock_and_get_host_diagnostic(struct MPT3SAS_ADAPTER *ioc, + u32 *host_diagnostic); +void mpt3sas_base_lock_host_diagnostic(struct MPT3SAS_ADAPTER *ioc); /* scsih shared API */ struct scsi_cmnd *mpt3sas_scsih_scsi_lookup_get(struct MPT3SAS_ADAPTER *ioc, @@ -1502,31 +1799,40 @@ struct scsi_cmnd *mpt3sas_scsih_scsi_lookup_get(struct MPT3SAS_ADAPTER *ioc, u8 mpt3sas_scsih_event_callback(struct MPT3SAS_ADAPTER *ioc, u8 msix_index, u32 reply); void mpt3sas_scsih_pre_reset_handler(struct MPT3SAS_ADAPTER *ioc); -void mpt3sas_scsih_after_reset_handler(struct MPT3SAS_ADAPTER *ioc); +void mpt3sas_scsih_clear_outstanding_scsi_tm_commands( + struct MPT3SAS_ADAPTER *ioc); void mpt3sas_scsih_reset_done_handler(struct MPT3SAS_ADAPTER *ioc); -int mpt3sas_scsih_issue_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, u64 lun, - u8 type, u16 smid_task, u16 msix_task, u8 timeout, u8 tr_method); +int mpt3sas_scsih_issue_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, + uint channel, uint id, u64 lun, u8 type, u16 smid_task, + u16 msix_task, u8 timeout, u8 tr_method); int mpt3sas_scsih_issue_locked_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, - u64 lun, u8 type, u16 smid_task, u16 msix_task, - u8 timeout, u8 tr_method); + uint channel, uint id, u64 lun, u8 type, u16 smid_task, + u16 msix_task, u8 timeout, u8 tr_method); void mpt3sas_scsih_set_tm_flag(struct MPT3SAS_ADAPTER *ioc, u16 handle); void mpt3sas_scsih_clear_tm_flag(struct MPT3SAS_ADAPTER *ioc, u16 handle); -void mpt3sas_expander_remove(struct MPT3SAS_ADAPTER *ioc, u64 sas_address); +void mpt3sas_expander_remove(struct MPT3SAS_ADAPTER *ioc, u64 sas_address, + struct hba_port *port); void mpt3sas_device_remove_by_sas_address(struct MPT3SAS_ADAPTER *ioc, - u64 sas_address); + u64 sas_address, struct hba_port *port); u8 mpt3sas_check_for_pending_internal_cmds(struct MPT3SAS_ADAPTER *ioc, u16 smid); +struct hba_port * +mpt3sas_get_port_by_id(struct MPT3SAS_ADAPTER *ioc, u8 port, + u8 bypass_dirty_port_flag); struct _sas_node *mpt3sas_scsih_expander_find_by_handle( struct MPT3SAS_ADAPTER *ioc, u16 handle); struct _sas_node *mpt3sas_scsih_expander_find_by_sas_address( - struct MPT3SAS_ADAPTER *ioc, u64 sas_address); + struct MPT3SAS_ADAPTER *ioc, u64 sas_address, + struct hba_port *port); struct _sas_device *mpt3sas_get_sdev_by_addr( - struct MPT3SAS_ADAPTER *ioc, u64 sas_address); + struct MPT3SAS_ADAPTER *ioc, u64 sas_address, + struct hba_port *port); struct _sas_device *__mpt3sas_get_sdev_by_addr( - struct MPT3SAS_ADAPTER *ioc, u64 sas_address); + struct MPT3SAS_ADAPTER *ioc, u64 sas_address, + struct hba_port *port); struct _sas_device *mpt3sas_get_sdev_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle); struct _pcie_device *mpt3sas_get_pdev_by_handle(struct MPT3SAS_ADAPTER *ioc, @@ -1535,6 +1841,12 @@ struct _pcie_device *mpt3sas_get_pdev_by_handle(struct MPT3SAS_ADAPTER *ioc, void mpt3sas_port_enable_complete(struct MPT3SAS_ADAPTER *ioc); struct _raid_device * mpt3sas_raid_device_find_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle); +void mpt3sas_scsih_change_queue_depth(struct scsi_device *sdev, int qdepth); +struct _sas_device * +__mpt3sas_get_sdev_by_rphy(struct MPT3SAS_ADAPTER *ioc, struct sas_rphy *rphy); +struct virtual_phy * +mpt3sas_get_vphy_by_phy(struct MPT3SAS_ADAPTER *ioc, + struct hba_port *port, u32 phy); /* config shared API */ u8 mpt3sas_config_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, @@ -1543,9 +1855,9 @@ int mpt3sas_config_get_number_hba_phys(struct MPT3SAS_ADAPTER *ioc, u8 *num_phys); int mpt3sas_config_get_manufacturing_pg0(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t *mpi_reply, Mpi2ManufacturingPage0_t *config_page); -int mpt3sas_config_get_manufacturing_pg7(struct MPT3SAS_ADAPTER *ioc, - Mpi2ConfigReply_t *mpi_reply, Mpi2ManufacturingPage7_t *config_page, - u16 sz); +int mpt3sas_config_get_manufacturing_pg1(struct MPT3SAS_ADAPTER *ioc, + Mpi2ConfigReply_t *mpi_reply, Mpi2ManufacturingPage1_t *config_page); + int mpt3sas_config_get_manufacturing_pg10(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t *mpi_reply, struct Mpi2ManufacturingPage10_t *config_page); @@ -1561,20 +1873,26 @@ int mpt3sas_config_get_bios_pg2(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t *mpi_reply, Mpi2BiosPage2_t *config_page); int mpt3sas_config_get_bios_pg3(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t *mpi_reply, Mpi2BiosPage3_t *config_page); +int mpt3sas_config_set_bios_pg4(struct MPT3SAS_ADAPTER *ioc, + Mpi2ConfigReply_t *mpi_reply, Mpi2BiosPage4_t *config_page, + int sz_config_page); +int mpt3sas_config_get_bios_pg4(struct MPT3SAS_ADAPTER *ioc, + Mpi2ConfigReply_t *mpi_reply, Mpi2BiosPage4_t *config_page, + int sz_config_page); int mpt3sas_config_get_iounit_pg0(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage0_t *config_page); int mpt3sas_config_get_sas_device_pg0(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t *mpi_reply, Mpi2SasDevicePage0_t *config_page, u32 form, u32 handle); -int mpt3sas_config_get_sas_device_pg1(struct MPT3SAS_ADAPTER *ioc, - Mpi2ConfigReply_t *mpi_reply, Mpi2SasDevicePage1_t *config_page, - u32 form, u32 handle); int mpt3sas_config_get_pcie_device_pg0(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t *mpi_reply, Mpi26PCIeDevicePage0_t *config_page, u32 form, u32 handle); int mpt3sas_config_get_pcie_device_pg2(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t *mpi_reply, Mpi26PCIeDevicePage2_t *config_page, u32 form, u32 handle); +int mpt3sas_config_get_pcie_iounit_pg1(struct MPT3SAS_ADAPTER *ioc, + Mpi2ConfigReply_t *mpi_reply, Mpi26PCIeIOUnitPage1_t *config_page, + u16 sz); int mpt3sas_config_get_sas_iounit_pg0(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t *mpi_reply, Mpi2SasIOUnitPage0_t *config_page, u16 sz); @@ -1592,6 +1910,10 @@ int mpt3sas_config_get_sas_iounit_pg1(struct MPT3SAS_ADAPTER *ioc, int mpt3sas_config_set_sas_iounit_pg1(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t *mpi_reply, Mpi2SasIOUnitPage1_t *config_page, u16 sz); +int mpt3sas_config_get_ioc_pg1(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t + *mpi_reply, Mpi2IOCPage1_t *config_page); +int mpt3sas_config_set_ioc_pg1(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t + *mpi_reply, Mpi2IOCPage1_t *config_page); int mpt3sas_config_get_ioc_pg8(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t *mpi_reply, Mpi2IOCPage8_t *config_page); int mpt3sas_config_get_expander_pg0(struct MPT3SAS_ADAPTER *ioc, @@ -1622,16 +1944,44 @@ int mpt3sas_config_get_volume_handle(struct MPT3SAS_ADAPTER *ioc, u16 pd_handle, u16 *volume_handle); int mpt3sas_config_get_volume_wwid(struct MPT3SAS_ADAPTER *ioc, u16 volume_handle, u64 *wwid); +int +mpt3sas_config_get_driver_trigger_pg0(struct MPT3SAS_ADAPTER *ioc, + Mpi2ConfigReply_t *mpi_reply, Mpi26DriverTriggerPage0_t *config_page); +int +mpt3sas_config_get_driver_trigger_pg1(struct MPT3SAS_ADAPTER *ioc, + Mpi2ConfigReply_t *mpi_reply, Mpi26DriverTriggerPage1_t *config_page); +int +mpt3sas_config_get_driver_trigger_pg2(struct MPT3SAS_ADAPTER *ioc, + Mpi2ConfigReply_t *mpi_reply, Mpi26DriverTriggerPage2_t *config_page); +int +mpt3sas_config_get_driver_trigger_pg3(struct MPT3SAS_ADAPTER *ioc, + Mpi2ConfigReply_t *mpi_reply, Mpi26DriverTriggerPage3_t *config_page); +int +mpt3sas_config_get_driver_trigger_pg4(struct MPT3SAS_ADAPTER *ioc, + Mpi2ConfigReply_t *mpi_reply, Mpi26DriverTriggerPage4_t *config_page); +int +mpt3sas_config_update_driver_trigger_pg1(struct MPT3SAS_ADAPTER *ioc, + struct SL_WH_MASTER_TRIGGER_T *master_tg, bool set); +int +mpt3sas_config_update_driver_trigger_pg2(struct MPT3SAS_ADAPTER *ioc, + struct SL_WH_EVENT_TRIGGERS_T *event_tg, bool set); +int +mpt3sas_config_update_driver_trigger_pg3(struct MPT3SAS_ADAPTER *ioc, + struct SL_WH_SCSI_TRIGGERS_T *scsi_tg, bool set); +int +mpt3sas_config_update_driver_trigger_pg4(struct MPT3SAS_ADAPTER *ioc, + struct SL_WH_MPI_TRIGGERS_T *mpi_tg, bool set); /* ctl shared API */ -extern struct device_attribute *mpt3sas_host_attrs[]; -extern struct device_attribute *mpt3sas_dev_attrs[]; +extern const struct attribute_group *mpt3sas_host_groups[]; +extern const struct attribute_group *mpt3sas_dev_groups[]; void mpt3sas_ctl_init(ushort hbas_to_enumerate); void mpt3sas_ctl_exit(ushort hbas_to_enumerate); +void mpt3sas_ctl_release(struct MPT3SAS_ADAPTER *ioc); u8 mpt3sas_ctl_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply); void mpt3sas_ctl_pre_reset_handler(struct MPT3SAS_ADAPTER *ioc); -void mpt3sas_ctl_after_reset_handler(struct MPT3SAS_ADAPTER *ioc); +void mpt3sas_ctl_clear_outstanding_ioctls(struct MPT3SAS_ADAPTER *ioc); void mpt3sas_ctl_reset_done_handler(struct MPT3SAS_ADAPTER *ioc); u8 mpt3sas_ctl_event_callback(struct MPT3SAS_ADAPTER *ioc, u8 msix_index, u32 reply); @@ -1648,25 +1998,33 @@ extern struct scsi_transport_template *mpt3sas_transport_template; u8 mpt3sas_transport_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply); struct _sas_port *mpt3sas_transport_port_add(struct MPT3SAS_ADAPTER *ioc, - u16 handle, u64 sas_address); + u16 handle, u64 sas_address, struct hba_port *port); void mpt3sas_transport_port_remove(struct MPT3SAS_ADAPTER *ioc, u64 sas_address, - u64 sas_address_parent); + u64 sas_address_parent, struct hba_port *port); int mpt3sas_transport_add_host_phy(struct MPT3SAS_ADAPTER *ioc, struct _sas_phy *mpt3sas_phy, Mpi2SasPhyPage0_t phy_pg0, struct device *parent_dev); int mpt3sas_transport_add_expander_phy(struct MPT3SAS_ADAPTER *ioc, struct _sas_phy *mpt3sas_phy, Mpi2ExpanderPage1_t expander_pg1, struct device *parent_dev); void mpt3sas_transport_update_links(struct MPT3SAS_ADAPTER *ioc, - u64 sas_address, u16 handle, u8 phy_number, u8 link_rate); + u64 sas_address, u16 handle, u8 phy_number, u8 link_rate, + struct hba_port *port); extern struct sas_function_template mpt3sas_transport_functions; extern struct scsi_transport_template *mpt3sas_transport_template; +void +mpt3sas_transport_del_phy_from_an_existing_port(struct MPT3SAS_ADAPTER *ioc, + struct _sas_node *sas_node, struct _sas_phy *mpt3sas_phy); +void +mpt3sas_transport_add_phy_to_an_existing_port(struct MPT3SAS_ADAPTER *ioc, + struct _sas_node *sas_node, struct _sas_phy *mpt3sas_phy, + u64 sas_address, struct hba_port *port); /* trigger data externs */ void mpt3sas_send_trigger_data_event(struct MPT3SAS_ADAPTER *ioc, struct SL_WH_TRIGGERS_EVENT_DATA_T *event_data); void mpt3sas_process_trigger_data(struct MPT3SAS_ADAPTER *ioc, struct SL_WH_TRIGGERS_EVENT_DATA_T *event_data); void mpt3sas_trigger_master(struct MPT3SAS_ADAPTER *ioc, - u32 tigger_bitmask); + u32 trigger_bitmask); void mpt3sas_trigger_event(struct MPT3SAS_ADAPTER *ioc, u16 event, u16 log_entry_qualifier); void mpt3sas_trigger_scsi(struct MPT3SAS_ADAPTER *ioc, u8 sense_key, @@ -1682,7 +2040,25 @@ void mpt3sas_setup_direct_io(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd, struct _raid_device *raid_device, Mpi25SCSIIORequest_t *mpi_request); -/* NCQ Prio Handling Check */ -bool scsih_ncq_prio_supp(struct scsi_device *sdev); +void mpt3sas_setup_debugfs(struct MPT3SAS_ADAPTER *ioc); +void mpt3sas_destroy_debugfs(struct MPT3SAS_ADAPTER *ioc); +void mpt3sas_init_debugfs(void); +void mpt3sas_exit_debugfs(void); +/** + * _scsih_is_pcie_scsi_device - determines if device is an pcie scsi device + * @device_info: bitfield providing information about the device. + * Context: none + * + * Returns 1 if scsi device. + */ +static inline int +mpt3sas_scsih_is_pcie_scsi_device(u32 device_info) +{ + if ((device_info & + MPI26_PCIE_DEVINFO_MASK_DEVICE_TYPE) == MPI26_PCIE_DEVINFO_SCSI) + return 1; + else + return 0; +} #endif /* MPT3SAS_BASE_H_INCLUDED */ diff --git a/drivers/scsi/mpt3sas/mpt3sas_config.c b/drivers/scsi/mpt3sas/mpt3sas_config.c index fb0a17252f86..45ac853e1289 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_config.c +++ b/drivers/scsi/mpt3sas/mpt3sas_config.c @@ -101,9 +101,6 @@ _config_display_some_debug(struct MPT3SAS_ADAPTER *ioc, u16 smid, Mpi2ConfigRequest_t *mpi_request; char *desc = NULL; - if (!(ioc->logging_level & MPT_DEBUG_CONFIG)) - return; - mpi_request = mpt3sas_base_get_msg_frame(ioc, smid); switch (mpi_request->Header.PageType & MPI2_CONFIG_PAGETYPE_MASK) { case MPI2_CONFIG_PAGETYPE_IO_UNIT: @@ -269,7 +266,8 @@ mpt3sas_config_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, mpi_reply->MsgLength*4); } ioc->config_cmds.status &= ~MPT3_CMD_PENDING; - _config_display_some_debug(ioc, smid, "config_done", mpi_reply); + if (ioc->logging_level & MPT_DEBUG_CONFIG) + _config_display_some_debug(ioc, smid, "config_done", mpi_reply); ioc->config_cmds.smid = USHRT_MAX; complete(&ioc->config_cmds.done); return 1; @@ -361,8 +359,11 @@ _config_request(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigRequest_t } r = mpt3sas_wait_for_ioc(ioc, MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT); - if (r) + if (r) { + if (r == -ETIME) + issue_host_reset = 1; goto free_mem; + } smid = mpt3sas_base_get_smid(ioc, ioc->config_cb_idx); if (!smid) { @@ -373,27 +374,33 @@ _config_request(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigRequest_t } r = 0; - memset(mpi_reply, 0, sizeof(Mpi2ConfigReply_t)); + memset(ioc->config_cmds.reply, 0, sizeof(Mpi2ConfigReply_t)); ioc->config_cmds.status = MPT3_CMD_PENDING; config_request = mpt3sas_base_get_msg_frame(ioc, smid); ioc->config_cmds.smid = smid; memcpy(config_request, mpi_request, sizeof(Mpi2ConfigRequest_t)); - _config_display_some_debug(ioc, smid, "config_request", NULL); + if (ioc->logging_level & MPT_DEBUG_CONFIG) + _config_display_some_debug(ioc, smid, "config_request", NULL); init_completion(&ioc->config_cmds.done); - mpt3sas_base_put_smid_default(ioc, smid); + ioc->put_smid_default(ioc, smid); wait_for_completion_timeout(&ioc->config_cmds.done, timeout*HZ); if (!(ioc->config_cmds.status & MPT3_CMD_COMPLETE)) { - mpt3sas_base_check_cmd_timeout(ioc, - ioc->config_cmds.status, mpi_request, - sizeof(Mpi2ConfigRequest_t)/4); + if (!(ioc->logging_level & MPT_DEBUG_CONFIG)) + _config_display_some_debug(ioc, + smid, "config_request", NULL); + ioc_err(ioc, "%s: command timeout\n", __func__); + mpt3sas_base_check_cmd_timeout(ioc, ioc->config_cmds.status, + mpi_request, sizeof(Mpi2ConfigRequest_t) / 4); retry_count++; if (ioc->config_cmds.smid == smid) mpt3sas_base_free_smid(ioc, smid); - if ((ioc->shost_recovery) || (ioc->config_cmds.status & - MPT3_CMD_RESET) || ioc->pci_error_recovery) + if (ioc->config_cmds.status & MPT3_CMD_RESET) goto retry_config; - issue_host_reset = 1; - r = -EFAULT; + if (ioc->shost_recovery || ioc->pci_error_recovery) { + issue_host_reset = 0; + r = -EFAULT; + } else + issue_host_reset = 1; goto free_mem; } @@ -404,8 +411,11 @@ _config_request(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigRequest_t /* Reply Frame Sanity Checks to workaround FW issues */ if ((mpi_request->Header.PageType & 0xF) != (mpi_reply->Header.PageType & 0xF)) { + if (!(ioc->logging_level & MPT_DEBUG_CONFIG)) + _config_display_some_debug(ioc, + smid, "config_request", NULL); _debug_dump_mf(mpi_request, ioc->request_sz/4); - _debug_dump_reply(mpi_reply, ioc->request_sz/4); + _debug_dump_reply(mpi_reply, ioc->reply_sz/4); panic("%s: %s: Firmware BUG: mpi_reply mismatch: Requested PageType(0x%02x) Reply PageType(0x%02x)\n", ioc->name, __func__, mpi_request->Header.PageType & 0xF, @@ -415,8 +425,11 @@ _config_request(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigRequest_t if (((mpi_request->Header.PageType & 0xF) == MPI2_CONFIG_PAGETYPE_EXTENDED) && mpi_request->ExtPageType != mpi_reply->ExtPageType) { + if (!(ioc->logging_level & MPT_DEBUG_CONFIG)) + _config_display_some_debug(ioc, + smid, "config_request", NULL); _debug_dump_mf(mpi_request, ioc->request_sz/4); - _debug_dump_reply(mpi_reply, ioc->request_sz/4); + _debug_dump_reply(mpi_reply, ioc->reply_sz/4); panic("%s: %s: Firmware BUG: mpi_reply mismatch: Requested ExtPageType(0x%02x) Reply ExtPageType(0x%02x)\n", ioc->name, __func__, mpi_request->ExtPageType, @@ -439,8 +452,11 @@ _config_request(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigRequest_t if (p) { if ((mpi_request->Header.PageType & 0xF) != (p[3] & 0xF)) { + if (!(ioc->logging_level & MPT_DEBUG_CONFIG)) + _config_display_some_debug(ioc, + smid, "config_request", NULL); _debug_dump_mf(mpi_request, ioc->request_sz/4); - _debug_dump_reply(mpi_reply, ioc->request_sz/4); + _debug_dump_reply(mpi_reply, ioc->reply_sz/4); _debug_dump_config(p, min_t(u16, mem.sz, config_page_sz)/4); panic("%s: %s: Firmware BUG: config page mismatch: Requested PageType(0x%02x) Reply PageType(0x%02x)\n", @@ -452,8 +468,11 @@ _config_request(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigRequest_t if (((mpi_request->Header.PageType & 0xF) == MPI2_CONFIG_PAGETYPE_EXTENDED) && (mpi_request->ExtPageType != p[6])) { + if (!(ioc->logging_level & MPT_DEBUG_CONFIG)) + _config_display_some_debug(ioc, + smid, "config_request", NULL); _debug_dump_mf(mpi_request, ioc->request_sz/4); - _debug_dump_reply(mpi_reply, ioc->request_sz/4); + _debug_dump_reply(mpi_reply, ioc->reply_sz/4); _debug_dump_config(p, min_t(u16, mem.sz, config_page_sz)/4); panic("%s: %s: Firmware BUG: config page mismatch: Requested ExtPageType(0x%02x) Reply ExtPageType(0x%02x)\n", @@ -472,8 +491,16 @@ _config_request(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigRequest_t ioc->config_cmds.status = MPT3_CMD_NOT_USED; mutex_unlock(&ioc->config_cmds.mutex); - if (issue_host_reset) - mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); + if (issue_host_reset) { + if (ioc->drv_internal_flags & MPT_DRV_INTERNAL_FIRST_PE_ISSUED) { + mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); + r = -EFAULT; + } else { + if (mpt3sas_base_check_for_fault_and_issue_reset(ioc)) + return -EFAULT; + r = -EAGAIN; + } + } return r; } @@ -514,19 +541,17 @@ mpt3sas_config_get_manufacturing_pg0(struct MPT3SAS_ADAPTER *ioc, } /** - * mpt3sas_config_get_manufacturing_pg7 - obtain manufacturing page 7 + * mpt3sas_config_get_manufacturing_pg1 - obtain manufacturing page 1 * @ioc: per adapter object * @mpi_reply: reply mf payload returned from firmware * @config_page: contents of the config page - * @sz: size of buffer passed in config_page * Context: sleep. * * Return: 0 for success, non-zero for failure. */ int -mpt3sas_config_get_manufacturing_pg7(struct MPT3SAS_ADAPTER *ioc, - Mpi2ConfigReply_t *mpi_reply, Mpi2ManufacturingPage7_t *config_page, - u16 sz) +mpt3sas_config_get_manufacturing_pg1(struct MPT3SAS_ADAPTER *ioc, + Mpi2ConfigReply_t *mpi_reply, Mpi2ManufacturingPage1_t *config_page) { Mpi2ConfigRequest_t mpi_request; int r; @@ -535,18 +560,18 @@ mpt3sas_config_get_manufacturing_pg7(struct MPT3SAS_ADAPTER *ioc, mpi_request.Function = MPI2_FUNCTION_CONFIG; mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_MANUFACTURING; - mpi_request.Header.PageNumber = 7; - mpi_request.Header.PageVersion = MPI2_MANUFACTURING7_PAGEVERSION; + mpi_request.Header.PageNumber = 1; + mpi_request.Header.PageVersion = MPI2_MANUFACTURING1_PAGEVERSION; ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); r = _config_request(ioc, &mpi_request, mpi_reply, - MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); + MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); if (r) goto out; mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; r = _config_request(ioc, &mpi_request, mpi_reply, - MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, - sz); + MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, + sizeof(*config_page)); out: return r; } @@ -730,11 +755,99 @@ mpt3sas_config_get_bios_pg3(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t r = _config_request(ioc, &mpi_request, mpi_reply, MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sizeof(*config_page)); + out: return r; } /** + * mpt3sas_config_set_bios_pg4 - write out bios page 4 + * @ioc: per adapter object + * @mpi_reply: reply mf payload returned from firmware + * @config_page: contents of the config page + * @sz_config_pg: sizeof the config page + * Context: sleep. + * + * Return: 0 for success, non-zero for failure. + */ +int +mpt3sas_config_set_bios_pg4(struct MPT3SAS_ADAPTER *ioc, + Mpi2ConfigReply_t *mpi_reply, Mpi2BiosPage4_t *config_page, + int sz_config_pg) +{ + Mpi2ConfigRequest_t mpi_request; + int r; + + memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); + + mpi_request.Function = MPI2_FUNCTION_CONFIG; + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; + mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_BIOS; + mpi_request.Header.PageNumber = 4; + mpi_request.Header.PageVersion = MPI2_BIOSPAGE4_PAGEVERSION; + + ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); + + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); + if (r) + goto out; + + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT; + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, + sz_config_pg); + out: + return r; +} + +/** + * mpt3sas_config_get_bios_pg4 - read bios page 4 + * @ioc: per adapter object + * @mpi_reply: reply mf payload returned from firmware + * @config_page: contents of the config page + * @sz_config_pg: sizeof the config page + * Context: sleep. + * + * Return: 0 for success, non-zero for failure. + */ +int +mpt3sas_config_get_bios_pg4(struct MPT3SAS_ADAPTER *ioc, + Mpi2ConfigReply_t *mpi_reply, Mpi2BiosPage4_t *config_page, + int sz_config_pg) +{ + Mpi2ConfigRequest_t mpi_request; + int r; + + memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); + mpi_request.Function = MPI2_FUNCTION_CONFIG; + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; + mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_BIOS; + mpi_request.Header.PageNumber = 4; + mpi_request.Header.PageVersion = MPI2_BIOSPAGE4_PAGEVERSION; + ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); + if (r) + goto out; + + /* + * The sizeof the page is variable. Allow for just the + * size to be returned + */ + if (config_page && sz_config_pg) { + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; + + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, + sz_config_pg); + } + +out: + return r; +} + +/** * mpt3sas_config_get_iounit_pg0 - obtain iounit page 0 * @ioc: per adapter object * @mpi_reply: reply mf payload returned from firmware @@ -949,22 +1062,18 @@ mpt3sas_config_get_ioc_pg8(struct MPT3SAS_ADAPTER *ioc, out: return r; } - /** - * mpt3sas_config_get_sas_device_pg0 - obtain sas device page 0 + * mpt3sas_config_get_ioc_pg1 - obtain ioc page 1 * @ioc: per adapter object * @mpi_reply: reply mf payload returned from firmware * @config_page: contents of the config page - * @form: GET_NEXT_HANDLE or HANDLE - * @handle: device handle * Context: sleep. * * Return: 0 for success, non-zero for failure. */ int -mpt3sas_config_get_sas_device_pg0(struct MPT3SAS_ADAPTER *ioc, - Mpi2ConfigReply_t *mpi_reply, Mpi2SasDevicePage0_t *config_page, - u32 form, u32 handle) +mpt3sas_config_get_ioc_pg1(struct MPT3SAS_ADAPTER *ioc, + Mpi2ConfigReply_t *mpi_reply, Mpi2IOCPage1_t *config_page) { Mpi2ConfigRequest_t mpi_request; int r; @@ -972,17 +1081,15 @@ mpt3sas_config_get_sas_device_pg0(struct MPT3SAS_ADAPTER *ioc, memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); mpi_request.Function = MPI2_FUNCTION_CONFIG; mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; - mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; - mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE; - mpi_request.Header.PageVersion = MPI2_SASDEVICE0_PAGEVERSION; - mpi_request.Header.PageNumber = 0; + mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IOC; + mpi_request.Header.PageNumber = 1; + mpi_request.Header.PageVersion = MPI2_IOCPAGE8_PAGEVERSION; ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); r = _config_request(ioc, &mpi_request, mpi_reply, MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); if (r) goto out; - mpi_request.PageAddress = cpu_to_le32(form | handle); mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; r = _config_request(ioc, &mpi_request, mpi_reply, MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, @@ -992,7 +1099,43 @@ mpt3sas_config_get_sas_device_pg0(struct MPT3SAS_ADAPTER *ioc, } /** - * mpt3sas_config_get_sas_device_pg1 - obtain sas device page 1 + * mpt3sas_config_set_ioc_pg1 - modify ioc page 1 + * @ioc: per adapter object + * @mpi_reply: reply mf payload returned from firmware + * @config_page: contents of the config page + * Context: sleep. + * + * Return: 0 for success, non-zero for failure. + */ +int +mpt3sas_config_set_ioc_pg1(struct MPT3SAS_ADAPTER *ioc, + Mpi2ConfigReply_t *mpi_reply, Mpi2IOCPage1_t *config_page) +{ + Mpi2ConfigRequest_t mpi_request; + int r; + + memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); + mpi_request.Function = MPI2_FUNCTION_CONFIG; + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; + mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IOC; + mpi_request.Header.PageNumber = 1; + mpi_request.Header.PageVersion = MPI2_IOCPAGE8_PAGEVERSION; + ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); + if (r) + goto out; + + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT; + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, + sizeof(*config_page)); + out: + return r; +} + +/** + * mpt3sas_config_get_sas_device_pg0 - obtain sas device page 0 * @ioc: per adapter object * @mpi_reply: reply mf payload returned from firmware * @config_page: contents of the config page @@ -1003,8 +1146,8 @@ mpt3sas_config_get_sas_device_pg0(struct MPT3SAS_ADAPTER *ioc, * Return: 0 for success, non-zero for failure. */ int -mpt3sas_config_get_sas_device_pg1(struct MPT3SAS_ADAPTER *ioc, - Mpi2ConfigReply_t *mpi_reply, Mpi2SasDevicePage1_t *config_page, +mpt3sas_config_get_sas_device_pg0(struct MPT3SAS_ADAPTER *ioc, + Mpi2ConfigReply_t *mpi_reply, Mpi2SasDevicePage0_t *config_page, u32 form, u32 handle) { Mpi2ConfigRequest_t mpi_request; @@ -1015,8 +1158,8 @@ mpt3sas_config_get_sas_device_pg1(struct MPT3SAS_ADAPTER *ioc, mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE; - mpi_request.Header.PageVersion = MPI2_SASDEVICE1_PAGEVERSION; - mpi_request.Header.PageNumber = 1; + mpi_request.Header.PageVersion = MPI2_SASDEVICE0_PAGEVERSION; + mpi_request.Header.PageNumber = 0; ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); r = _config_request(ioc, &mpi_request, mpi_reply, MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); @@ -1074,6 +1217,43 @@ out: } /** + * mpt3sas_config_get_pcie_iounit_pg1 - obtain pcie iounit page 1 + * @ioc: per adapter object + * @mpi_reply: reply mf payload returned from firmware + * @config_page: contents of the config page + * @sz: size of buffer passed in config_page + * Context: sleep. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt3sas_config_get_pcie_iounit_pg1(struct MPT3SAS_ADAPTER *ioc, + Mpi2ConfigReply_t *mpi_reply, Mpi26PCIeIOUnitPage1_t *config_page, + u16 sz) +{ + Mpi2ConfigRequest_t mpi_request; + int r; + + memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); + mpi_request.Function = MPI2_FUNCTION_CONFIG; + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; + mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; + mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_PCIE_IO_UNIT; + mpi_request.Header.PageVersion = MPI26_PCIEIOUNITPAGE1_PAGEVERSION; + mpi_request.Header.PageNumber = 1; + ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); + if (r) + goto out; + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz); +out: + return r; +} + +/** * mpt3sas_config_get_pcie_device_pg2 - obtain pcie device page 2 * @ioc: per adapter object * @mpi_reply: reply mf payload returned from firmware @@ -1658,6 +1838,766 @@ mpt3sas_config_get_phys_disk_pg0(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t } /** + * mpt3sas_config_get_driver_trigger_pg0 - obtain driver trigger page 0 + * @ioc: per adapter object + * @mpi_reply: reply mf payload returned from firmware + * @config_page: contents of the config page + * Context: sleep. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt3sas_config_get_driver_trigger_pg0(struct MPT3SAS_ADAPTER *ioc, + Mpi2ConfigReply_t *mpi_reply, Mpi26DriverTriggerPage0_t *config_page) +{ + Mpi2ConfigRequest_t mpi_request; + int r; + + memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); + mpi_request.Function = MPI2_FUNCTION_CONFIG; + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; + mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; + mpi_request.ExtPageType = + MPI2_CONFIG_EXTPAGETYPE_DRIVER_PERSISTENT_TRIGGER; + mpi_request.Header.PageNumber = 0; + mpi_request.Header.PageVersion = MPI26_DRIVER_TRIGGER_PAGE0_PAGEVERSION; + ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); + if (r) + goto out; + + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, + sizeof(*config_page)); + out: + return r; +} + +/** + * _config_set_driver_trigger_pg0 - write driver trigger page 0 + * @ioc: per adapter object + * @mpi_reply: reply mf payload returned from firmware + * @config_page: contents of the config page + * Context: sleep. + * + * Returns 0 for success, non-zero for failure. + */ +static int +_config_set_driver_trigger_pg0(struct MPT3SAS_ADAPTER *ioc, + Mpi2ConfigReply_t *mpi_reply, Mpi26DriverTriggerPage0_t *config_page) +{ + Mpi2ConfigRequest_t mpi_request; + int r; + + memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); + mpi_request.Function = MPI2_FUNCTION_CONFIG; + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; + mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; + mpi_request.ExtPageType = + MPI2_CONFIG_EXTPAGETYPE_DRIVER_PERSISTENT_TRIGGER; + mpi_request.Header.PageNumber = 0; + mpi_request.Header.PageVersion = MPI26_DRIVER_TRIGGER_PAGE0_PAGEVERSION; + ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); + if (r) + goto out; + + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT; + _config_request(ioc, &mpi_request, mpi_reply, + MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, + sizeof(*config_page)); + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM; + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, + sizeof(*config_page)); + out: + return r; +} + +/** + * mpt3sas_config_update_driver_trigger_pg0 - update driver trigger page 0 + * @ioc: per adapter object + * @trigger_flag: trigger type bit map + * @set: set ot clear trigger values + * Context: sleep. + * + * Returns 0 for success, non-zero for failure. + */ +static int +mpt3sas_config_update_driver_trigger_pg0(struct MPT3SAS_ADAPTER *ioc, + u16 trigger_flag, bool set) +{ + Mpi26DriverTriggerPage0_t tg_pg0; + Mpi2ConfigReply_t mpi_reply; + int rc; + u16 flags, ioc_status; + + rc = mpt3sas_config_get_driver_trigger_pg0(ioc, &mpi_reply, &tg_pg0); + if (rc) + return rc; + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + dcprintk(ioc, + ioc_err(ioc, + "%s: Failed to get trigger pg0, ioc_status(0x%04x)\n", + __func__, ioc_status)); + return -EFAULT; + } + + if (set) + flags = le16_to_cpu(tg_pg0.TriggerFlags) | trigger_flag; + else + flags = le16_to_cpu(tg_pg0.TriggerFlags) & ~trigger_flag; + + tg_pg0.TriggerFlags = cpu_to_le16(flags); + + rc = _config_set_driver_trigger_pg0(ioc, &mpi_reply, &tg_pg0); + if (rc) + return rc; + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + dcprintk(ioc, + ioc_err(ioc, + "%s: Failed to update trigger pg0, ioc_status(0x%04x)\n", + __func__, ioc_status)); + return -EFAULT; + } + + return 0; +} + +/** + * mpt3sas_config_get_driver_trigger_pg1 - obtain driver trigger page 1 + * @ioc: per adapter object + * @mpi_reply: reply mf payload returned from firmware + * @config_page: contents of the config page + * Context: sleep. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt3sas_config_get_driver_trigger_pg1(struct MPT3SAS_ADAPTER *ioc, + Mpi2ConfigReply_t *mpi_reply, Mpi26DriverTriggerPage1_t *config_page) +{ + Mpi2ConfigRequest_t mpi_request; + int r; + + memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); + mpi_request.Function = MPI2_FUNCTION_CONFIG; + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; + mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; + mpi_request.ExtPageType = + MPI2_CONFIG_EXTPAGETYPE_DRIVER_PERSISTENT_TRIGGER; + mpi_request.Header.PageNumber = 1; + mpi_request.Header.PageVersion = MPI26_DRIVER_TRIGGER_PAGE1_PAGEVERSION; + ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); + if (r) + goto out; + + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, + sizeof(*config_page)); + out: + return r; +} + +/** + * _config_set_driver_trigger_pg1 - write driver trigger page 1 + * @ioc: per adapter object + * @mpi_reply: reply mf payload returned from firmware + * @config_page: contents of the config page + * Context: sleep. + * + * Returns 0 for success, non-zero for failure. + */ +static int +_config_set_driver_trigger_pg1(struct MPT3SAS_ADAPTER *ioc, + Mpi2ConfigReply_t *mpi_reply, Mpi26DriverTriggerPage1_t *config_page) +{ + Mpi2ConfigRequest_t mpi_request; + int r; + + memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); + mpi_request.Function = MPI2_FUNCTION_CONFIG; + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; + mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; + mpi_request.ExtPageType = + MPI2_CONFIG_EXTPAGETYPE_DRIVER_PERSISTENT_TRIGGER; + mpi_request.Header.PageNumber = 1; + mpi_request.Header.PageVersion = MPI26_DRIVER_TRIGGER_PAGE1_PAGEVERSION; + ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); + if (r) + goto out; + + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT; + _config_request(ioc, &mpi_request, mpi_reply, + MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, + sizeof(*config_page)); + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM; + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, + sizeof(*config_page)); + out: + return r; +} + +/** + * mpt3sas_config_update_driver_trigger_pg1 - update driver trigger page 1 + * @ioc: per adapter object + * @master_tg: Master trigger bit map + * @set: set ot clear trigger values + * Context: sleep. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt3sas_config_update_driver_trigger_pg1(struct MPT3SAS_ADAPTER *ioc, + struct SL_WH_MASTER_TRIGGER_T *master_tg, bool set) +{ + Mpi26DriverTriggerPage1_t tg_pg1; + Mpi2ConfigReply_t mpi_reply; + int rc; + u16 ioc_status; + + rc = mpt3sas_config_update_driver_trigger_pg0(ioc, + MPI26_DRIVER_TRIGGER0_FLAG_MASTER_TRIGGER_VALID, set); + if (rc) + return rc; + + rc = mpt3sas_config_get_driver_trigger_pg1(ioc, &mpi_reply, &tg_pg1); + if (rc) + goto out; + + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + dcprintk(ioc, + ioc_err(ioc, + "%s: Failed to get trigger pg1, ioc_status(0x%04x)\n", + __func__, ioc_status)); + rc = -EFAULT; + goto out; + } + + if (set) { + tg_pg1.NumMasterTrigger = cpu_to_le16(1); + tg_pg1.MasterTriggers[0].MasterTriggerFlags = cpu_to_le32( + master_tg->MasterData); + } else { + tg_pg1.NumMasterTrigger = 0; + tg_pg1.MasterTriggers[0].MasterTriggerFlags = 0; + } + + rc = _config_set_driver_trigger_pg1(ioc, &mpi_reply, &tg_pg1); + if (rc) + goto out; + + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + dcprintk(ioc, + ioc_err(ioc, + "%s: Failed to get trigger pg1, ioc_status(0x%04x)\n", + __func__, ioc_status)); + rc = -EFAULT; + goto out; + } + + return 0; + +out: + mpt3sas_config_update_driver_trigger_pg0(ioc, + MPI26_DRIVER_TRIGGER0_FLAG_MASTER_TRIGGER_VALID, !set); + + return rc; +} + +/** + * mpt3sas_config_get_driver_trigger_pg2 - obtain driver trigger page 2 + * @ioc: per adapter object + * @mpi_reply: reply mf payload returned from firmware + * @config_page: contents of the config page + * Context: sleep. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt3sas_config_get_driver_trigger_pg2(struct MPT3SAS_ADAPTER *ioc, + Mpi2ConfigReply_t *mpi_reply, Mpi26DriverTriggerPage2_t *config_page) +{ + Mpi2ConfigRequest_t mpi_request; + int r; + + memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); + mpi_request.Function = MPI2_FUNCTION_CONFIG; + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; + mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; + mpi_request.ExtPageType = + MPI2_CONFIG_EXTPAGETYPE_DRIVER_PERSISTENT_TRIGGER; + mpi_request.Header.PageNumber = 2; + mpi_request.Header.PageVersion = MPI26_DRIVER_TRIGGER_PAGE2_PAGEVERSION; + ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); + if (r) + goto out; + + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, + sizeof(*config_page)); + out: + return r; +} + +/** + * _config_set_driver_trigger_pg2 - write driver trigger page 2 + * @ioc: per adapter object + * @mpi_reply: reply mf payload returned from firmware + * @config_page: contents of the config page + * Context: sleep. + * + * Returns 0 for success, non-zero for failure. + */ +static int +_config_set_driver_trigger_pg2(struct MPT3SAS_ADAPTER *ioc, + Mpi2ConfigReply_t *mpi_reply, Mpi26DriverTriggerPage2_t *config_page) +{ + Mpi2ConfigRequest_t mpi_request; + int r; + + memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); + mpi_request.Function = MPI2_FUNCTION_CONFIG; + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; + mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; + mpi_request.ExtPageType = + MPI2_CONFIG_EXTPAGETYPE_DRIVER_PERSISTENT_TRIGGER; + mpi_request.Header.PageNumber = 2; + mpi_request.Header.PageVersion = MPI26_DRIVER_TRIGGER_PAGE2_PAGEVERSION; + ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); + if (r) + goto out; + + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT; + _config_request(ioc, &mpi_request, mpi_reply, + MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, + sizeof(*config_page)); + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM; + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, + sizeof(*config_page)); + out: + return r; +} + +/** + * mpt3sas_config_update_driver_trigger_pg2 - update driver trigger page 2 + * @ioc: per adapter object + * @event_tg: list of Event Triggers + * @set: set ot clear trigger values + * Context: sleep. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt3sas_config_update_driver_trigger_pg2(struct MPT3SAS_ADAPTER *ioc, + struct SL_WH_EVENT_TRIGGERS_T *event_tg, bool set) +{ + Mpi26DriverTriggerPage2_t tg_pg2; + Mpi2ConfigReply_t mpi_reply; + int rc, i, count; + u16 ioc_status; + + rc = mpt3sas_config_update_driver_trigger_pg0(ioc, + MPI26_DRIVER_TRIGGER0_FLAG_MPI_EVENT_TRIGGER_VALID, set); + if (rc) + return rc; + + rc = mpt3sas_config_get_driver_trigger_pg2(ioc, &mpi_reply, &tg_pg2); + if (rc) + goto out; + + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + dcprintk(ioc, + ioc_err(ioc, + "%s: Failed to get trigger pg2, ioc_status(0x%04x)\n", + __func__, ioc_status)); + rc = -EFAULT; + goto out; + } + + if (set) { + count = event_tg->ValidEntries; + tg_pg2.NumMPIEventTrigger = cpu_to_le16(count); + for (i = 0; i < count; i++) { + tg_pg2.MPIEventTriggers[i].MPIEventCode = + cpu_to_le16( + event_tg->EventTriggerEntry[i].EventValue); + tg_pg2.MPIEventTriggers[i].MPIEventCodeSpecific = + cpu_to_le16( + event_tg->EventTriggerEntry[i].LogEntryQualifier); + } + } else { + tg_pg2.NumMPIEventTrigger = 0; + memset(&tg_pg2.MPIEventTriggers[0], 0, + NUM_VALID_ENTRIES * sizeof( + MPI26_DRIVER_MPI_EVENT_TRIGGER_ENTRY)); + } + + rc = _config_set_driver_trigger_pg2(ioc, &mpi_reply, &tg_pg2); + if (rc) + goto out; + + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + dcprintk(ioc, + ioc_err(ioc, + "%s: Failed to get trigger pg2, ioc_status(0x%04x)\n", + __func__, ioc_status)); + rc = -EFAULT; + goto out; + } + + return 0; + +out: + mpt3sas_config_update_driver_trigger_pg0(ioc, + MPI26_DRIVER_TRIGGER0_FLAG_MPI_EVENT_TRIGGER_VALID, !set); + + return rc; +} + +/** + * mpt3sas_config_get_driver_trigger_pg3 - obtain driver trigger page 3 + * @ioc: per adapter object + * @mpi_reply: reply mf payload returned from firmware + * @config_page: contents of the config page + * Context: sleep. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt3sas_config_get_driver_trigger_pg3(struct MPT3SAS_ADAPTER *ioc, + Mpi2ConfigReply_t *mpi_reply, Mpi26DriverTriggerPage3_t *config_page) +{ + Mpi2ConfigRequest_t mpi_request; + int r; + + memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); + mpi_request.Function = MPI2_FUNCTION_CONFIG; + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; + mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; + mpi_request.ExtPageType = + MPI2_CONFIG_EXTPAGETYPE_DRIVER_PERSISTENT_TRIGGER; + mpi_request.Header.PageNumber = 3; + mpi_request.Header.PageVersion = MPI26_DRIVER_TRIGGER_PAGE3_PAGEVERSION; + ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); + if (r) + goto out; + + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, + sizeof(*config_page)); + out: + return r; +} + +/** + * _config_set_driver_trigger_pg3 - write driver trigger page 3 + * @ioc: per adapter object + * @mpi_reply: reply mf payload returned from firmware + * @config_page: contents of the config page + * Context: sleep. + * + * Returns 0 for success, non-zero for failure. + */ +static int +_config_set_driver_trigger_pg3(struct MPT3SAS_ADAPTER *ioc, + Mpi2ConfigReply_t *mpi_reply, Mpi26DriverTriggerPage3_t *config_page) +{ + Mpi2ConfigRequest_t mpi_request; + int r; + + memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); + mpi_request.Function = MPI2_FUNCTION_CONFIG; + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; + mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; + mpi_request.ExtPageType = + MPI2_CONFIG_EXTPAGETYPE_DRIVER_PERSISTENT_TRIGGER; + mpi_request.Header.PageNumber = 3; + mpi_request.Header.PageVersion = MPI26_DRIVER_TRIGGER_PAGE3_PAGEVERSION; + ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); + if (r) + goto out; + + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT; + _config_request(ioc, &mpi_request, mpi_reply, + MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, + sizeof(*config_page)); + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM; + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, + sizeof(*config_page)); + out: + return r; +} + +/** + * mpt3sas_config_update_driver_trigger_pg3 - update driver trigger page 3 + * @ioc: per adapter object + * @scsi_tg: scsi trigger list + * @set: set ot clear trigger values + * Context: sleep. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt3sas_config_update_driver_trigger_pg3(struct MPT3SAS_ADAPTER *ioc, + struct SL_WH_SCSI_TRIGGERS_T *scsi_tg, bool set) +{ + Mpi26DriverTriggerPage3_t tg_pg3; + Mpi2ConfigReply_t mpi_reply; + int rc, i, count; + u16 ioc_status; + + rc = mpt3sas_config_update_driver_trigger_pg0(ioc, + MPI26_DRIVER_TRIGGER0_FLAG_SCSI_SENSE_TRIGGER_VALID, set); + if (rc) + return rc; + + rc = mpt3sas_config_get_driver_trigger_pg3(ioc, &mpi_reply, &tg_pg3); + if (rc) + goto out; + + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + dcprintk(ioc, + ioc_err(ioc, + "%s: Failed to get trigger pg3, ioc_status(0x%04x)\n", + __func__, ioc_status)); + return -EFAULT; + } + + if (set) { + count = scsi_tg->ValidEntries; + tg_pg3.NumSCSISenseTrigger = cpu_to_le16(count); + for (i = 0; i < count; i++) { + tg_pg3.SCSISenseTriggers[i].ASCQ = + scsi_tg->SCSITriggerEntry[i].ASCQ; + tg_pg3.SCSISenseTriggers[i].ASC = + scsi_tg->SCSITriggerEntry[i].ASC; + tg_pg3.SCSISenseTriggers[i].SenseKey = + scsi_tg->SCSITriggerEntry[i].SenseKey; + } + } else { + tg_pg3.NumSCSISenseTrigger = 0; + memset(&tg_pg3.SCSISenseTriggers[0], 0, + NUM_VALID_ENTRIES * sizeof( + MPI26_DRIVER_SCSI_SENSE_TRIGGER_ENTRY)); + } + + rc = _config_set_driver_trigger_pg3(ioc, &mpi_reply, &tg_pg3); + if (rc) + goto out; + + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + dcprintk(ioc, + ioc_err(ioc, + "%s: Failed to get trigger pg3, ioc_status(0x%04x)\n", + __func__, ioc_status)); + return -EFAULT; + } + + return 0; +out: + mpt3sas_config_update_driver_trigger_pg0(ioc, + MPI26_DRIVER_TRIGGER0_FLAG_SCSI_SENSE_TRIGGER_VALID, !set); + + return rc; +} + +/** + * mpt3sas_config_get_driver_trigger_pg4 - obtain driver trigger page 4 + * @ioc: per adapter object + * @mpi_reply: reply mf payload returned from firmware + * @config_page: contents of the config page + * Context: sleep. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt3sas_config_get_driver_trigger_pg4(struct MPT3SAS_ADAPTER *ioc, + Mpi2ConfigReply_t *mpi_reply, Mpi26DriverTriggerPage4_t *config_page) +{ + Mpi2ConfigRequest_t mpi_request; + int r; + + memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); + mpi_request.Function = MPI2_FUNCTION_CONFIG; + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; + mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; + mpi_request.ExtPageType = + MPI2_CONFIG_EXTPAGETYPE_DRIVER_PERSISTENT_TRIGGER; + mpi_request.Header.PageNumber = 4; + mpi_request.Header.PageVersion = MPI26_DRIVER_TRIGGER_PAGE4_PAGEVERSION; + ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); + if (r) + goto out; + + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, + sizeof(*config_page)); + out: + return r; +} + +/** + * _config_set_driver_trigger_pg4 - write driver trigger page 4 + * @ioc: per adapter object + * @mpi_reply: reply mf payload returned from firmware + * @config_page: contents of the config page + * Context: sleep. + * + * Returns 0 for success, non-zero for failure. + */ +static int +_config_set_driver_trigger_pg4(struct MPT3SAS_ADAPTER *ioc, + Mpi2ConfigReply_t *mpi_reply, Mpi26DriverTriggerPage4_t *config_page) +{ + Mpi2ConfigRequest_t mpi_request; + int r; + + memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); + mpi_request.Function = MPI2_FUNCTION_CONFIG; + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; + mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; + mpi_request.ExtPageType = + MPI2_CONFIG_EXTPAGETYPE_DRIVER_PERSISTENT_TRIGGER; + mpi_request.Header.PageNumber = 4; + mpi_request.Header.PageVersion = MPI26_DRIVER_TRIGGER_PAGE4_PAGEVERSION; + ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); + if (r) + goto out; + + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT; + _config_request(ioc, &mpi_request, mpi_reply, + MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, + sizeof(*config_page)); + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM; + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, + sizeof(*config_page)); + out: + return r; +} + +/** + * mpt3sas_config_update_driver_trigger_pg4 - update driver trigger page 4 + * @ioc: per adapter object + * @mpi_tg: mpi trigger list + * @set: set ot clear trigger values + * Context: sleep. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt3sas_config_update_driver_trigger_pg4(struct MPT3SAS_ADAPTER *ioc, + struct SL_WH_MPI_TRIGGERS_T *mpi_tg, bool set) +{ + Mpi26DriverTriggerPage4_t tg_pg4; + Mpi2ConfigReply_t mpi_reply; + int rc, i, count; + u16 ioc_status; + + rc = mpt3sas_config_update_driver_trigger_pg0(ioc, + MPI26_DRIVER_TRIGGER0_FLAG_LOGINFO_TRIGGER_VALID, set); + if (rc) + return rc; + + rc = mpt3sas_config_get_driver_trigger_pg4(ioc, &mpi_reply, &tg_pg4); + if (rc) + goto out; + + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + dcprintk(ioc, + ioc_err(ioc, + "%s: Failed to get trigger pg4, ioc_status(0x%04x)\n", + __func__, ioc_status)); + rc = -EFAULT; + goto out; + } + + if (set) { + count = mpi_tg->ValidEntries; + tg_pg4.NumIOCStatusLogInfoTrigger = cpu_to_le16(count); + for (i = 0; i < count; i++) { + tg_pg4.IOCStatusLoginfoTriggers[i].IOCStatus = + cpu_to_le16(mpi_tg->MPITriggerEntry[i].IOCStatus); + tg_pg4.IOCStatusLoginfoTriggers[i].LogInfo = + cpu_to_le32(mpi_tg->MPITriggerEntry[i].IocLogInfo); + } + } else { + tg_pg4.NumIOCStatusLogInfoTrigger = 0; + memset(&tg_pg4.IOCStatusLoginfoTriggers[0], 0, + NUM_VALID_ENTRIES * sizeof( + MPI26_DRIVER_IOCSTATUS_LOGINFO_TRIGGER_ENTRY)); + } + + rc = _config_set_driver_trigger_pg4(ioc, &mpi_reply, &tg_pg4); + if (rc) + goto out; + + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + dcprintk(ioc, + ioc_err(ioc, + "%s: Failed to get trigger pg4, ioc_status(0x%04x)\n", + __func__, ioc_status)); + rc = -EFAULT; + goto out; + } + + return 0; + +out: + mpt3sas_config_update_driver_trigger_pg0(ioc, + MPI26_DRIVER_TRIGGER0_FLAG_LOGINFO_TRIGGER_VALID, !set); + + return rc; +} + +/** * mpt3sas_config_get_volume_handle - returns volume handle for give hidden * raid components * @ioc: per adapter object diff --git a/drivers/scsi/mpt3sas/mpt3sas_ctl.c b/drivers/scsi/mpt3sas/mpt3sas_ctl.c index b2bb47c14d35..3b951589feeb 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_ctl.c +++ b/drivers/scsi/mpt3sas/mpt3sas_ctl.c @@ -180,6 +180,15 @@ _ctl_display_some_debug(struct MPT3SAS_ADAPTER *ioc, u16 smid, case MPI2_FUNCTION_SMP_PASSTHROUGH: desc = "smp_passthrough"; break; + case MPI2_FUNCTION_TOOLBOX: + desc = "toolbox"; + break; + case MPI2_FUNCTION_NVME_ENCAPSULATED: + desc = "nvme_encapsulated"; + break; + case MPI2_FUNCTION_MCTP_PASSTHROUGH: + desc = "mctp_passthrough"; + break; } if (!desc) @@ -448,7 +457,7 @@ out: } /** - * mpt3sas_ctl_reset_handler - reset callback handler (for ctl) + * mpt3sas_ctl_pre_reset_handler - reset callback handler (for ctl) * @ioc: per adapter object * * The handler for doing any required cleanup or initialization. @@ -466,19 +475,29 @@ void mpt3sas_ctl_pre_reset_handler(struct MPT3SAS_ADAPTER *ioc) if ((ioc->diag_buffer_status[i] & MPT3_DIAG_BUFFER_IS_RELEASED)) continue; + + /* + * add a log message to indicate the release + */ + ioc_info(ioc, + "%s: Releasing the trace buffer due to adapter reset.", + __func__); + ioc->htb_rel.buffer_rel_condition = + MPT3_DIAG_BUFFER_REL_TRIGGER; mpt3sas_send_diag_release(ioc, i, &issue_reset); } } /** - * mpt3sas_ctl_reset_handler - reset callback handler (for ctl) + * mpt3sas_ctl_clear_outstanding_ioctls - clears outstanding ioctl cmd. * @ioc: per adapter object * * The handler for doing any required cleanup or initialization. */ -void mpt3sas_ctl_after_reset_handler(struct MPT3SAS_ADAPTER *ioc) +void mpt3sas_ctl_clear_outstanding_ioctls(struct MPT3SAS_ADAPTER *ioc) { - dtmprintk(ioc, ioc_info(ioc, "%s: MPT3_IOC_AFTER_RESET\n", __func__)); + dtmprintk(ioc, + ioc_info(ioc, "%s: clear outstanding ioctl cmd\n", __func__)); if (ioc->ctl_cmds.status & MPT3_CMD_PENDING) { ioc->ctl_cmds.status |= MPT3_CMD_RESET; mpt3sas_base_free_smid(ioc, ioc->ctl_cmds.smid); @@ -487,7 +506,7 @@ void mpt3sas_ctl_after_reset_handler(struct MPT3SAS_ADAPTER *ioc) } /** - * mpt3sas_ctl_reset_handler - reset callback handler (for ctl) + * mpt3sas_ctl_reset_done_handler - reset callback handler (for ctl) * @ioc: per adapter object * * The handler for doing any required cleanup or initialization. @@ -562,7 +581,7 @@ static int _ctl_set_task_mid(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command *karg, Mpi2SCSITaskManagementRequest_t *tm_request) { - u8 found = 0; + bool found = false; u16 smid; u16 handle; struct scsi_cmnd *scmd; @@ -584,6 +603,7 @@ _ctl_set_task_mid(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command *karg, handle = le16_to_cpu(tm_request->DevHandle); for (smid = ioc->scsiio_depth; smid && !found; smid--) { struct scsiio_tracker *st; + __le16 task_mid; scmd = mpt3sas_scsih_scsi_lookup_get(ioc, smid); if (!scmd) @@ -596,8 +616,16 @@ _ctl_set_task_mid(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command *karg, if (priv_data->sas_target->handle != handle) continue; st = scsi_cmd_priv(scmd); - tm_request->TaskMID = cpu_to_le16(st->smid); - found = 1; + + /* + * If the given TaskMID from the user space is zero, then the + * first outstanding smid will be picked up. Otherwise, + * targeted smid will be the one. + */ + task_mid = cpu_to_le16(st->smid); + if (!tm_request->TaskMID) + tm_request->TaskMID = task_mid; + found = tm_request->TaskMID == task_mid; } if (!found) { @@ -628,6 +656,40 @@ _ctl_set_task_mid(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command *karg, } /** + * _ctl_send_mctp_passthru_req - Send an MCTP passthru request + * @ioc: per adapter object + * @mctp_passthru_req: MPI mctp passhthru request from caller + * @psge: pointer to the H2DSGL + * @data_out_dma: DMA buffer for H2D SGL + * @data_out_sz: H2D length + * @data_in_dma: DMA buffer for D2H SGL + * @data_in_sz: D2H length + * @smid: SMID to submit the request + * + */ +static void +_ctl_send_mctp_passthru_req( + struct MPT3SAS_ADAPTER *ioc, + Mpi26MctpPassthroughRequest_t *mctp_passthru_req, void *psge, + dma_addr_t data_out_dma, int data_out_sz, + dma_addr_t data_in_dma, int data_in_sz, + u16 smid) +{ + mctp_passthru_req->H2DLength = data_out_sz; + mctp_passthru_req->D2HLength = data_in_sz; + + /* Build the H2D SGL from the data out buffer */ + ioc->build_sg(ioc, psge, data_out_dma, data_out_sz, 0, 0); + + psge += ioc->sge_size_ieee; + + /* Build the D2H SGL for the data in buffer */ + ioc->build_sg(ioc, psge, 0, 0, data_in_dma, data_in_sz); + + ioc->put_smid_default(ioc, smid); +} + +/** * _ctl_do_mpt_command - main handler for MPT3COMMAND opcode * @ioc: per adapter object * @karg: (struct mpt3_ioctl_command) @@ -642,7 +704,7 @@ _ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg, Mpi26NVMeEncapsulatedRequest_t *nvme_encap_request = NULL; struct _pcie_device *pcie_device = NULL; u16 smid; - u8 timeout; + unsigned long timeout; u8 issue_reset; u32 sz, sz_arg; void *psge; @@ -654,7 +716,7 @@ _ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg, size_t data_in_sz = 0; long ret; u16 device_handle = MPT3SAS_INVALID_DEVICE_HANDLE; - u8 tr_method = MPI26_SCSITASKMGMT_MSGFLAGS_PROTOCOL_LVL_RST_PCIE; + int tm_ret; issue_reset = 0; @@ -707,6 +769,7 @@ _ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg, ioc->ctl_cmds.status = MPT3_CMD_PENDING; memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz); request = mpt3sas_base_get_msg_frame(ioc, smid); + memset(request, 0, ioc->request_sz); memcpy(request, mpi_request, karg.data_sge_offset*4); ioc->ctl_cmds.smid = smid; data_out_sz = karg.data_out_size; @@ -767,9 +830,38 @@ _ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg, init_completion(&ioc->ctl_cmds.done); switch (mpi_request->Function) { + case MPI2_FUNCTION_MCTP_PASSTHROUGH: + { + Mpi26MctpPassthroughRequest_t *mctp_passthru_req = + (Mpi26MctpPassthroughRequest_t *)request; + + if (!(ioc->facts.IOCCapabilities & MPI26_IOCFACTS_CAPABILITY_MCTP_PASSTHRU)) { + ioc_err(ioc, "%s: MCTP Passthrough request not supported\n", + __func__); + mpt3sas_base_free_smid(ioc, smid); + ret = -EINVAL; + goto out; + } + + _ctl_send_mctp_passthru_req(ioc, mctp_passthru_req, psge, data_out_dma, + data_out_sz, data_in_dma, data_in_sz, smid); + break; + } case MPI2_FUNCTION_NVME_ENCAPSULATED: { nvme_encap_request = (Mpi26NVMeEncapsulatedRequest_t *)request; + if (!ioc->pcie_sg_lookup) { + dtmprintk(ioc, ioc_info(ioc, + "HBA doesn't support NVMe. Rejecting NVMe Encapsulated request.\n" + )); + + if (ioc->logging_level & MPT_DEBUG_TM) + _debug_dump_mf(nvme_encap_request, + ioc->request_sz/4); + mpt3sas_base_free_smid(ioc, smid); + ret = -EINVAL; + goto out; + } /* * Get the Physical Address of the sense buffer. * Use Error Response buffer address field to hold the sense @@ -822,7 +914,7 @@ _ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg, if (mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST) ioc->put_smid_scsi_io(ioc, smid, device_handle); else - mpt3sas_base_put_smid_default(ioc, smid); + ioc->put_smid_default(ioc, smid); break; } case MPI2_FUNCTION_SCSI_TASK_MGMT: @@ -859,7 +951,7 @@ _ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg, tm_request->DevHandle)); ioc->build_sg_mpi(ioc, psge, data_out_dma, data_out_sz, data_in_dma, data_in_sz); - mpt3sas_base_put_smid_hi_priority(ioc, smid, 0); + ioc->put_smid_hi_priority(ioc, smid, 0); break; } case MPI2_FUNCTION_SMP_PASSTHROUGH: @@ -868,8 +960,10 @@ _ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg, (Mpi2SmpPassthroughRequest_t *)mpi_request; u8 *data; - /* ioc determines which port to use */ - smp_request->PhysicalPort = 0xFF; + if (!ioc->multipath_on_hba) { + /* ioc determines which port to use */ + smp_request->PhysicalPort = 0xFF; + } if (smp_request->PassthroughFlags & MPI2_SMP_PT_REQ_PT_FLAGS_IMMEDIATE) data = (u8 *)&smp_request->SGL; @@ -890,7 +984,7 @@ _ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg, } ioc->build_sg(ioc, psge, data_out_dma, data_out_sz, data_in_dma, data_in_sz); - mpt3sas_base_put_smid_default(ioc, smid); + ioc->put_smid_default(ioc, smid); break; } case MPI2_FUNCTION_SATA_PASSTHROUGH: @@ -905,15 +999,23 @@ _ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg, } ioc->build_sg(ioc, psge, data_out_dma, data_out_sz, data_in_dma, data_in_sz); - mpt3sas_base_put_smid_default(ioc, smid); + ioc->put_smid_default(ioc, smid); break; } case MPI2_FUNCTION_FW_DOWNLOAD: + { + if (ioc->pdev->vendor == MPI2_MFGPAGE_VENDORID_ATTO) { + ioc_info(ioc, "Firmware download not supported for ATTO HBA.\n"); + ret = -EPERM; + break; + } + fallthrough; + } case MPI2_FUNCTION_FW_UPLOAD: { ioc->build_sg(ioc, psge, data_out_dma, data_out_sz, data_in_dma, data_in_sz); - mpt3sas_base_put_smid_default(ioc, smid); + ioc->put_smid_default(ioc, smid); break; } case MPI2_FUNCTION_TOOLBOX: @@ -921,14 +1023,38 @@ _ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg, Mpi2ToolboxCleanRequest_t *toolbox_request = (Mpi2ToolboxCleanRequest_t *)mpi_request; - if (toolbox_request->Tool == MPI2_TOOLBOX_DIAGNOSTIC_CLI_TOOL) { + if ((toolbox_request->Tool == MPI2_TOOLBOX_DIAGNOSTIC_CLI_TOOL) + || (toolbox_request->Tool == + MPI26_TOOLBOX_BACKEND_PCIE_LANE_MARGIN)) ioc->build_sg(ioc, psge, data_out_dma, data_out_sz, data_in_dma, data_in_sz); - } else { + else if (toolbox_request->Tool == + MPI2_TOOLBOX_MEMORY_MOVE_TOOL) { + Mpi2ToolboxMemMoveRequest_t *mem_move_request = + (Mpi2ToolboxMemMoveRequest_t *)request; + Mpi2SGESimple64_t tmp, *src = NULL, *dst = NULL; + + ioc->build_sg_mpi(ioc, psge, data_out_dma, + data_out_sz, data_in_dma, data_in_sz); + if (data_out_sz && !data_in_sz) { + dst = + (Mpi2SGESimple64_t *)&mem_move_request->SGL; + src = (void *)dst + ioc->sge_size; + + memcpy(&tmp, src, ioc->sge_size); + memcpy(src, dst, ioc->sge_size); + memcpy(dst, &tmp, ioc->sge_size); + } + if (ioc->logging_level & MPT_DEBUG_TM) { + ioc_info(ioc, + "Mpi2ToolboxMemMoveRequest_t request msg\n"); + _debug_dump_mf(mem_move_request, + ioc->request_sz/4); + } + } else ioc->build_sg_mpi(ioc, psge, data_out_dma, data_out_sz, - data_in_dma, data_in_sz); - } - mpt3sas_base_put_smid_default(ioc, smid); + data_in_dma, data_in_sz); + ioc->put_smid_default(ioc, smid); break; } case MPI2_FUNCTION_SAS_IO_UNIT_CONTROL: @@ -944,11 +1070,11 @@ _ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg, } /* drop to default case for posting the request */ } - /* fall through */ + fallthrough; default: ioc->build_sg_mpi(ioc, psge, data_out_dma, data_out_sz, data_in_dma, data_in_sz); - mpt3sas_base_put_smid_default(ioc, smid); + ioc->put_smid_default(ioc, smid); break; } @@ -970,10 +1096,9 @@ _ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg, ioc->ignore_loginfos = 0; } if (!(ioc->ctl_cmds.status & MPT3_CMD_COMPLETE)) { - issue_reset = - mpt3sas_base_check_cmd_timeout(ioc, - ioc->ctl_cmds.status, mpi_request, - karg.data_sge_offset); + mpt3sas_check_cmd_timeout(ioc, + ioc->ctl_cmds.status, mpi_request, + karg.data_sge_offset, issue_reset); goto issue_host_reset; } @@ -1047,17 +1172,28 @@ _ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg, mpt3sas_halt_firmware(ioc); pcie_device = mpt3sas_get_pdev_by_handle(ioc, le16_to_cpu(mpi_request->FunctionDependent1)); - if (pcie_device && (!ioc->tm_custom_handling)) - mpt3sas_scsih_issue_locked_tm(ioc, + if (pcie_device && (!ioc->tm_custom_handling) && + (!(mpt3sas_scsih_is_pcie_scsi_device( + pcie_device->device_info)))) + tm_ret = mpt3sas_scsih_issue_locked_tm(ioc, le16_to_cpu(mpi_request->FunctionDependent1), - 0, MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, + 0, 0, 0, + MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, 0, pcie_device->reset_timeout, - tr_method); + MPI26_SCSITASKMGMT_MSGFLAGS_PROTOCOL_LVL_RST_PCIE); else - mpt3sas_scsih_issue_locked_tm(ioc, + tm_ret = mpt3sas_scsih_issue_locked_tm(ioc, le16_to_cpu(mpi_request->FunctionDependent1), - 0, MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, + 0, 0, 0, + MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, 0, 30, MPI2_SCSITASKMGMT_MSGFLAGS_LINK_RESET); + + if (tm_ret != SUCCESS) { + ioc_info(ioc, + "target reset failed, issue hard reset: handle (0x%04x)\n", + le16_to_cpu(mpi_request->FunctionDependent1)); + mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); + } } else mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); } @@ -1126,6 +1262,8 @@ _ctl_getiocinfo(struct MPT3SAS_ADAPTER *ioc, void __user *arg) } karg.bios_version = le32_to_cpu(ioc->bios_pg3.BiosVersion); + karg.driver_capability |= MPT3_IOCTL_IOCINFO_DRIVER_CAP_MCTP_PASSTHRU; + if (copy_to_user(arg, &karg, sizeof(karg))) { pr_err("failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); @@ -1271,8 +1409,10 @@ _ctl_do_reset(struct MPT3SAS_ADAPTER *ioc, void __user *arg) dctlprintk(ioc, ioc_info(ioc, "%s: enter\n", __func__)); + ioc->reset_from_user = 1; retval = mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); - ioc_info(ioc, "host reset: %s\n", ((!retval) ? "SUCCESS" : "FAILED")); + ioc_info(ioc, + "Ioctl: host reset: %s\n", ((!retval) ? "SUCCESS" : "FAILED")); return 0; } @@ -1450,6 +1590,26 @@ _ctl_diag_capability(struct MPT3SAS_ADAPTER *ioc, u8 buffer_type) return rc; } +/** + * _ctl_diag_get_bufftype - return diag buffer type + * either TRACE, SNAPSHOT, or EXTENDED + * @ioc: per adapter object + * @unique_id: specifies the unique_id for the buffer + * + * returns MPT3_DIAG_UID_NOT_FOUND if the id not found + */ +static u8 +_ctl_diag_get_bufftype(struct MPT3SAS_ADAPTER *ioc, u32 unique_id) +{ + u8 index; + + for (index = 0; index < MPI2_DIAG_BUF_TYPE_COUNT; index++) { + if (ioc->unique_id[index] == unique_id) + return index; + } + + return MPT3_DIAG_UID_NOT_FOUND; +} /** * _ctl_diag_register_2 - wrapper for registering diag buffer support @@ -1497,11 +1657,88 @@ _ctl_diag_register_2(struct MPT3SAS_ADAPTER *ioc, return -EPERM; } + if (diag_register->unique_id == 0) { + ioc_err(ioc, + "%s: Invalid UID(0x%08x), buffer_type(0x%02x)\n", __func__, + diag_register->unique_id, buffer_type); + return -EINVAL; + } + + if ((ioc->diag_buffer_status[buffer_type] & + MPT3_DIAG_BUFFER_IS_APP_OWNED) && + !(ioc->diag_buffer_status[buffer_type] & + MPT3_DIAG_BUFFER_IS_RELEASED)) { + ioc_err(ioc, + "%s: buffer_type(0x%02x) is already registered by application with UID(0x%08x)\n", + __func__, buffer_type, ioc->unique_id[buffer_type]); + return -EINVAL; + } + if (ioc->diag_buffer_status[buffer_type] & MPT3_DIAG_BUFFER_IS_REGISTERED) { - ioc_err(ioc, "%s: already has a registered buffer for buffer_type(0x%02x)\n", - __func__, buffer_type); - return -EINVAL; + /* + * If driver posts buffer initially, then an application wants + * to Register that buffer (own it) without Releasing first, + * the application Register command MUST have the same buffer + * type and size in the Register command (obtained from the + * Query command). Otherwise that Register command will be + * failed. If the application has released the buffer but wants + * to re-register it, it should be allowed as long as the + * Unique-Id/Size match. + */ + + if (ioc->unique_id[buffer_type] == MPT3DIAGBUFFUNIQUEID && + ioc->diag_buffer_sz[buffer_type] == + diag_register->requested_buffer_size) { + + if (!(ioc->diag_buffer_status[buffer_type] & + MPT3_DIAG_BUFFER_IS_RELEASED)) { + dctlprintk(ioc, ioc_info(ioc, + "%s: diag_buffer (%d) ownership changed. old-ID(0x%08x), new-ID(0x%08x)\n", + __func__, buffer_type, + ioc->unique_id[buffer_type], + diag_register->unique_id)); + + /* + * Application wants to own the buffer with + * the same size. + */ + ioc->unique_id[buffer_type] = + diag_register->unique_id; + rc = 0; /* success */ + goto out; + } + } else if (ioc->unique_id[buffer_type] != + MPT3DIAGBUFFUNIQUEID) { + if (ioc->unique_id[buffer_type] != + diag_register->unique_id || + ioc->diag_buffer_sz[buffer_type] != + diag_register->requested_buffer_size || + !(ioc->diag_buffer_status[buffer_type] & + MPT3_DIAG_BUFFER_IS_RELEASED)) { + ioc_err(ioc, + "%s: already has a registered buffer for buffer_type(0x%02x)\n", + __func__, buffer_type); + return -EINVAL; + } + } else { + ioc_err(ioc, "%s: already has a registered buffer for buffer_type(0x%02x)\n", + __func__, buffer_type); + return -EINVAL; + } + } else if (ioc->diag_buffer_status[buffer_type] & + MPT3_DIAG_BUFFER_IS_DRIVER_ALLOCATED) { + + if (ioc->unique_id[buffer_type] != MPT3DIAGBUFFUNIQUEID || + ioc->diag_buffer_sz[buffer_type] != + diag_register->requested_buffer_size) { + + ioc_err(ioc, + "%s: already a buffer is allocated for buffer_type(0x%02x) of size %d bytes, so please try registering again with same size\n", + __func__, buffer_type, + ioc->diag_buffer_sz[buffer_type]); + return -EINVAL; + } } if (diag_register->requested_buffer_size % 4) { @@ -1521,12 +1758,17 @@ _ctl_diag_register_2(struct MPT3SAS_ADAPTER *ioc, ioc->ctl_cmds.status = MPT3_CMD_PENDING; memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz); mpi_request = mpt3sas_base_get_msg_frame(ioc, smid); + memset(mpi_request, 0, ioc->request_sz); ioc->ctl_cmds.smid = smid; request_data = ioc->diag_buffer[buffer_type]; request_data_sz = diag_register->requested_buffer_size; ioc->unique_id[buffer_type] = diag_register->unique_id; - ioc->diag_buffer_status[buffer_type] = 0; + /* Reset ioc variables used for additional query commands */ + ioc->reset_from_user = 0; + memset(&ioc->htb_rel, 0, sizeof(struct htb_rel_query)); + ioc->diag_buffer_status[buffer_type] &= + MPT3_DIAG_BUFFER_IS_DRIVER_ALLOCATED; memcpy(ioc->product_specific[buffer_type], diag_register->product_specific, MPT3_PRODUCT_SPECIFIC_DWORDS); ioc->diagnostic_flags[buffer_type] = diag_register->diagnostic_flags; @@ -1550,7 +1792,8 @@ _ctl_diag_register_2(struct MPT3SAS_ADAPTER *ioc, ioc_err(ioc, "%s: failed allocating memory for diag buffers, requested size(%d)\n", __func__, request_data_sz); mpt3sas_base_free_smid(ioc, smid); - return -ENOMEM; + rc = -ENOMEM; + goto out; } ioc->diag_buffer[buffer_type] = request_data; ioc->diag_buffer_sz[buffer_type] = request_data_sz; @@ -1576,15 +1819,14 @@ _ctl_diag_register_2(struct MPT3SAS_ADAPTER *ioc, cpu_to_le32(ioc->product_specific[buffer_type][i]); init_completion(&ioc->ctl_cmds.done); - mpt3sas_base_put_smid_default(ioc, smid); + ioc->put_smid_default(ioc, smid); wait_for_completion_timeout(&ioc->ctl_cmds.done, MPT3_IOCTL_DEFAULT_TIMEOUT*HZ); if (!(ioc->ctl_cmds.status & MPT3_CMD_COMPLETE)) { - issue_reset = - mpt3sas_base_check_cmd_timeout(ioc, - ioc->ctl_cmds.status, mpi_request, - sizeof(Mpi2DiagBufferPostRequest_t)/4); + mpt3sas_check_cmd_timeout(ioc, + ioc->ctl_cmds.status, mpi_request, + sizeof(Mpi2DiagBufferPostRequest_t)/4, issue_reset); goto issue_host_reset; } @@ -1615,9 +1857,13 @@ _ctl_diag_register_2(struct MPT3SAS_ADAPTER *ioc, out: - if (rc && request_data) + if (rc && request_data) { dma_free_coherent(&ioc->pdev->dev, request_data_sz, request_data, request_data_dma); + ioc->diag_buffer[buffer_type] = NULL; + ioc->diag_buffer_status[buffer_type] &= + ~MPT3_DIAG_BUFFER_IS_DRIVER_ALLOCATED; + } ioc->ctl_cmds.status = MPT3_CMD_NOT_USED; return rc; @@ -1635,6 +1881,10 @@ void mpt3sas_enable_diag_buffer(struct MPT3SAS_ADAPTER *ioc, u8 bits_to_register) { struct mpt3_diag_register diag_register; + u32 ret_val; + u32 trace_buff_size = ioc->manu_pg11.HostTraceBufferMaxSizeKB<<10; + u32 min_trace_buff_size = 0; + u32 decr_trace_buff_size = 0; memset(&diag_register, 0, sizeof(struct mpt3_diag_register)); @@ -1643,10 +1893,68 @@ mpt3sas_enable_diag_buffer(struct MPT3SAS_ADAPTER *ioc, u8 bits_to_register) ioc->diag_trigger_master.MasterData = (MASTER_TRIGGER_FW_FAULT + MASTER_TRIGGER_ADAPTER_RESET); diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_TRACE; - /* register for 2MB buffers */ - diag_register.requested_buffer_size = 2 * (1024 * 1024); - diag_register.unique_id = 0x7075900; - _ctl_diag_register_2(ioc, &diag_register); + diag_register.unique_id = + (ioc->hba_mpi_version_belonged == MPI2_VERSION) ? + (MPT2DIAGBUFFUNIQUEID):(MPT3DIAGBUFFUNIQUEID); + + if (trace_buff_size != 0) { + diag_register.requested_buffer_size = trace_buff_size; + min_trace_buff_size = + ioc->manu_pg11.HostTraceBufferMinSizeKB<<10; + decr_trace_buff_size = + ioc->manu_pg11.HostTraceBufferDecrementSizeKB<<10; + + if (min_trace_buff_size > trace_buff_size) { + /* The buff size is not set correctly */ + ioc_err(ioc, + "Min Trace Buff size (%d KB) greater than Max Trace Buff size (%d KB)\n", + min_trace_buff_size>>10, + trace_buff_size>>10); + ioc_err(ioc, + "Using zero Min Trace Buff Size\n"); + min_trace_buff_size = 0; + } + + if (decr_trace_buff_size == 0) { + /* + * retry the min size if decrement + * is not available. + */ + decr_trace_buff_size = + trace_buff_size - min_trace_buff_size; + } + } else { + /* register for 2MB buffers */ + diag_register.requested_buffer_size = 2 * (1024 * 1024); + } + + do { + ret_val = _ctl_diag_register_2(ioc, &diag_register); + + if (ret_val == -ENOMEM && min_trace_buff_size && + (trace_buff_size - decr_trace_buff_size) >= + min_trace_buff_size) { + /* adjust the buffer size */ + trace_buff_size -= decr_trace_buff_size; + diag_register.requested_buffer_size = + trace_buff_size; + } else + break; + } while (true); + + if (ret_val == -ENOMEM) + ioc_err(ioc, + "Cannot allocate trace buffer memory. Last memory tried = %d KB\n", + diag_register.requested_buffer_size>>10); + else if (ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] + & MPT3_DIAG_BUFFER_IS_REGISTERED) { + ioc_info(ioc, "Trace buffer memory %d KB allocated\n", + diag_register.requested_buffer_size>>10); + if (ioc->hba_mpi_version_belonged != MPI2_VERSION) + ioc->diag_buffer_status[ + MPI2_DIAG_BUF_TYPE_TRACE] |= + MPT3_DIAG_BUFFER_IS_DRIVER_ALLOCATED; + } } if (bits_to_register & 2) { @@ -1689,6 +1997,12 @@ _ctl_diag_register(struct MPT3SAS_ADAPTER *ioc, void __user *arg) } rc = _ctl_diag_register_2(ioc, &karg); + + if (!rc && (ioc->diag_buffer_status[karg.buffer_type] & + MPT3_DIAG_BUFFER_IS_REGISTERED)) + ioc->diag_buffer_status[karg.buffer_type] |= + MPT3_DIAG_BUFFER_IS_APP_OWNED; + return rc; } @@ -1718,7 +2032,13 @@ _ctl_diag_unregister(struct MPT3SAS_ADAPTER *ioc, void __user *arg) dctlprintk(ioc, ioc_info(ioc, "%s\n", __func__)); - buffer_type = karg.unique_id & 0x000000ff; + buffer_type = _ctl_diag_get_bufftype(ioc, karg.unique_id); + if (buffer_type == MPT3_DIAG_UID_NOT_FOUND) { + ioc_err(ioc, "%s: buffer with unique_id(0x%08x) not found\n", + __func__, karg.unique_id); + return -EINVAL; + } + if (!_ctl_diag_capability(ioc, buffer_type)) { ioc_err(ioc, "%s: doesn't have capability for buffer_type(0x%02x)\n", __func__, buffer_type); @@ -1751,12 +2071,21 @@ _ctl_diag_unregister(struct MPT3SAS_ADAPTER *ioc, void __user *arg) return -ENOMEM; } - request_data_sz = ioc->diag_buffer_sz[buffer_type]; - request_data_dma = ioc->diag_buffer_dma[buffer_type]; - dma_free_coherent(&ioc->pdev->dev, request_data_sz, - request_data, request_data_dma); - ioc->diag_buffer[buffer_type] = NULL; - ioc->diag_buffer_status[buffer_type] = 0; + if (ioc->diag_buffer_status[buffer_type] & + MPT3_DIAG_BUFFER_IS_DRIVER_ALLOCATED) { + ioc->unique_id[buffer_type] = MPT3DIAGBUFFUNIQUEID; + ioc->diag_buffer_status[buffer_type] &= + ~MPT3_DIAG_BUFFER_IS_APP_OWNED; + ioc->diag_buffer_status[buffer_type] &= + ~MPT3_DIAG_BUFFER_IS_REGISTERED; + } else { + request_data_sz = ioc->diag_buffer_sz[buffer_type]; + request_data_dma = ioc->diag_buffer_dma[buffer_type]; + dma_free_coherent(&ioc->pdev->dev, request_data_sz, + request_data, request_data_dma); + ioc->diag_buffer[buffer_type] = NULL; + ioc->diag_buffer_status[buffer_type] = 0; + } return 0; } @@ -1795,14 +2124,17 @@ _ctl_diag_query(struct MPT3SAS_ADAPTER *ioc, void __user *arg) return -EPERM; } - if ((ioc->diag_buffer_status[buffer_type] & - MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) { - ioc_err(ioc, "%s: buffer_type(0x%02x) is not registered\n", - __func__, buffer_type); - return -EINVAL; + if (!(ioc->diag_buffer_status[buffer_type] & + MPT3_DIAG_BUFFER_IS_DRIVER_ALLOCATED)) { + if ((ioc->diag_buffer_status[buffer_type] & + MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) { + ioc_err(ioc, "%s: buffer_type(0x%02x) is not registered\n", + __func__, buffer_type); + return -EINVAL; + } } - if (karg.unique_id & 0xffffff00) { + if (karg.unique_id) { if (karg.unique_id != ioc->unique_id[buffer_type]) { ioc_err(ioc, "%s: unique_id(0x%08x) is not registered\n", __func__, karg.unique_id); @@ -1817,13 +2149,21 @@ _ctl_diag_query(struct MPT3SAS_ADAPTER *ioc, void __user *arg) return -ENOMEM; } - if (ioc->diag_buffer_status[buffer_type] & MPT3_DIAG_BUFFER_IS_RELEASED) - karg.application_flags = (MPT3_APP_FLAGS_APP_OWNED | - MPT3_APP_FLAGS_BUFFER_VALID); - else - karg.application_flags = (MPT3_APP_FLAGS_APP_OWNED | - MPT3_APP_FLAGS_BUFFER_VALID | - MPT3_APP_FLAGS_FW_BUFFER_ACCESS); + if ((ioc->diag_buffer_status[buffer_type] & + MPT3_DIAG_BUFFER_IS_REGISTERED)) + karg.application_flags |= MPT3_APP_FLAGS_BUFFER_VALID; + + if (!(ioc->diag_buffer_status[buffer_type] & + MPT3_DIAG_BUFFER_IS_RELEASED)) + karg.application_flags |= MPT3_APP_FLAGS_FW_BUFFER_ACCESS; + + if (!(ioc->diag_buffer_status[buffer_type] & + MPT3_DIAG_BUFFER_IS_DRIVER_ALLOCATED)) + karg.application_flags |= MPT3_APP_FLAGS_DYNAMIC_BUFFER_ALLOC; + + if ((ioc->diag_buffer_status[buffer_type] & + MPT3_DIAG_BUFFER_IS_APP_OWNED)) + karg.application_flags |= MPT3_APP_FLAGS_APP_OWNED; for (i = 0; i < MPT3_PRODUCT_SPECIFIC_DWORDS; i++) karg.product_specific[i] = @@ -1859,6 +2199,7 @@ mpt3sas_send_diag_release(struct MPT3SAS_ADAPTER *ioc, u8 buffer_type, u16 ioc_status; u32 ioc_state; int rc; + u8 reset_needed = 0; dctlprintk(ioc, ioc_info(ioc, "%s\n", __func__)); @@ -1866,6 +2207,7 @@ mpt3sas_send_diag_release(struct MPT3SAS_ADAPTER *ioc, u8 buffer_type, rc = 0; *issue_reset = 0; + ioc_state = mpt3sas_base_get_iocstate(ioc, 1); if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { if (ioc->diag_buffer_status[buffer_type] & @@ -1895,6 +2237,7 @@ mpt3sas_send_diag_release(struct MPT3SAS_ADAPTER *ioc, u8 buffer_type, ioc->ctl_cmds.status = MPT3_CMD_PENDING; memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz); mpi_request = mpt3sas_base_get_msg_frame(ioc, smid); + memset(mpi_request, 0, ioc->request_sz); ioc->ctl_cmds.smid = smid; mpi_request->Function = MPI2_FUNCTION_DIAG_RELEASE; @@ -1903,14 +2246,15 @@ mpt3sas_send_diag_release(struct MPT3SAS_ADAPTER *ioc, u8 buffer_type, mpi_request->VP_ID = 0; init_completion(&ioc->ctl_cmds.done); - mpt3sas_base_put_smid_default(ioc, smid); + ioc->put_smid_default(ioc, smid); wait_for_completion_timeout(&ioc->ctl_cmds.done, MPT3_IOCTL_DEFAULT_TIMEOUT*HZ); if (!(ioc->ctl_cmds.status & MPT3_CMD_COMPLETE)) { - *issue_reset = mpt3sas_base_check_cmd_timeout(ioc, - ioc->ctl_cmds.status, mpi_request, - sizeof(Mpi2DiagReleaseRequest_t)/4); + mpt3sas_check_cmd_timeout(ioc, + ioc->ctl_cmds.status, mpi_request, + sizeof(Mpi2DiagReleaseRequest_t)/4, reset_needed); + *issue_reset = reset_needed; rc = -EFAULT; goto out; } @@ -1968,7 +2312,13 @@ _ctl_diag_release(struct MPT3SAS_ADAPTER *ioc, void __user *arg) dctlprintk(ioc, ioc_info(ioc, "%s\n", __func__)); - buffer_type = karg.unique_id & 0x000000ff; + buffer_type = _ctl_diag_get_bufftype(ioc, karg.unique_id); + if (buffer_type == MPT3_DIAG_UID_NOT_FOUND) { + ioc_err(ioc, "%s: buffer with unique_id(0x%08x) not found\n", + __func__, karg.unique_id); + return -EINVAL; + } + if (!_ctl_diag_capability(ioc, buffer_type)) { ioc_err(ioc, "%s: doesn't have capability for buffer_type(0x%02x)\n", __func__, buffer_type); @@ -1992,7 +2342,7 @@ _ctl_diag_release(struct MPT3SAS_ADAPTER *ioc, void __user *arg) MPT3_DIAG_BUFFER_IS_RELEASED) { ioc_err(ioc, "%s: buffer_type(0x%02x) is already released\n", __func__, buffer_type); - return 0; + return -EINVAL; } request_data = ioc->diag_buffer[buffer_type]; @@ -2052,7 +2402,13 @@ _ctl_diag_read_buffer(struct MPT3SAS_ADAPTER *ioc, void __user *arg) dctlprintk(ioc, ioc_info(ioc, "%s\n", __func__)); - buffer_type = karg.unique_id & 0x000000ff; + buffer_type = _ctl_diag_get_bufftype(ioc, karg.unique_id); + if (buffer_type == MPT3_DIAG_UID_NOT_FOUND) { + ioc_err(ioc, "%s: buffer with unique_id(0x%08x) not found\n", + __func__, karg.unique_id); + return -EINVAL; + } + if (!_ctl_diag_capability(ioc, buffer_type)) { ioc_err(ioc, "%s: doesn't have capability for buffer_type(0x%02x)\n", __func__, buffer_type); @@ -2136,6 +2492,7 @@ _ctl_diag_read_buffer(struct MPT3SAS_ADAPTER *ioc, void __user *arg) ioc->ctl_cmds.status = MPT3_CMD_PENDING; memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz); mpi_request = mpt3sas_base_get_msg_frame(ioc, smid); + memset(mpi_request, 0, ioc->request_sz); ioc->ctl_cmds.smid = smid; mpi_request->Function = MPI2_FUNCTION_DIAG_BUFFER_POST; @@ -2151,15 +2508,14 @@ _ctl_diag_read_buffer(struct MPT3SAS_ADAPTER *ioc, void __user *arg) mpi_request->VP_ID = 0; init_completion(&ioc->ctl_cmds.done); - mpt3sas_base_put_smid_default(ioc, smid); + ioc->put_smid_default(ioc, smid); wait_for_completion_timeout(&ioc->ctl_cmds.done, MPT3_IOCTL_DEFAULT_TIMEOUT*HZ); if (!(ioc->ctl_cmds.status & MPT3_CMD_COMPLETE)) { - issue_reset = - mpt3sas_base_check_cmd_timeout(ioc, - ioc->ctl_cmds.status, mpi_request, - sizeof(Mpi2DiagBufferPostRequest_t)/4); + mpt3sas_check_cmd_timeout(ioc, + ioc->ctl_cmds.status, mpi_request, + sizeof(Mpi2DiagBufferPostRequest_t)/4, issue_reset); goto issue_host_reset; } @@ -2176,6 +2532,8 @@ _ctl_diag_read_buffer(struct MPT3SAS_ADAPTER *ioc, void __user *arg) if (ioc_status == MPI2_IOCSTATUS_SUCCESS) { ioc->diag_buffer_status[buffer_type] |= MPT3_DIAG_BUFFER_IS_REGISTERED; + ioc->diag_buffer_status[buffer_type] &= + ~MPT3_DIAG_BUFFER_IS_RELEASED; dctlprintk(ioc, ioc_info(ioc, "%s: success\n", __func__)); } else { ioc_info(ioc, "%s: ioc_status(0x%04x) log_info(0x%08x)\n", @@ -2194,7 +2552,110 @@ _ctl_diag_read_buffer(struct MPT3SAS_ADAPTER *ioc, void __user *arg) return rc; } +/** + * _ctl_addnl_diag_query - query relevant info associated with diag buffers + * @ioc: per adapter object + * @arg: user space buffer containing ioctl content + * + * The application will send only unique_id. Driver will + * inspect unique_id first, if valid, fill the details related to cause + * for diag buffer release. + */ +static long +_ctl_addnl_diag_query(struct MPT3SAS_ADAPTER *ioc, void __user *arg) +{ + struct mpt3_addnl_diag_query karg; + u32 buffer_type = 0; + + if (copy_from_user(&karg, arg, sizeof(karg))) { + pr_err("%s: failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return -EFAULT; + } + dctlprintk(ioc, ioc_info(ioc, "%s\n", __func__)); + if (karg.unique_id == 0) { + ioc_err(ioc, "%s: unique_id is(0x%08x)\n", + __func__, karg.unique_id); + return -EPERM; + } + buffer_type = _ctl_diag_get_bufftype(ioc, karg.unique_id); + if (buffer_type == MPT3_DIAG_UID_NOT_FOUND) { + ioc_err(ioc, "%s: buffer with unique_id(0x%08x) not found\n", + __func__, karg.unique_id); + return -EPERM; + } + memset(&karg.rel_query, 0, sizeof(karg.rel_query)); + if ((ioc->diag_buffer_status[buffer_type] & + MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) { + ioc_info(ioc, "%s: buffer_type(0x%02x) is not registered\n", + __func__, buffer_type); + goto out; + } + if ((ioc->diag_buffer_status[buffer_type] & + MPT3_DIAG_BUFFER_IS_RELEASED) == 0) { + ioc_err(ioc, "%s: buffer_type(0x%02x) is not released\n", + __func__, buffer_type); + return -EPERM; + } + memcpy(&karg.rel_query, &ioc->htb_rel, sizeof(karg.rel_query)); +out: + if (copy_to_user(arg, &karg, sizeof(struct mpt3_addnl_diag_query))) { + ioc_err(ioc, "%s: unable to write mpt3_addnl_diag_query data @ %p\n", + __func__, arg); + return -EFAULT; + } + return 0; +} + +/** + * _ctl_enable_diag_sbr_reload - enable sbr reload bit + * @ioc: per adapter object + * @arg: user space buffer containing ioctl content + * + * Enable the SBR reload bit + */ +static int +_ctl_enable_diag_sbr_reload(struct MPT3SAS_ADAPTER *ioc, void __user *arg) +{ + u32 ioc_state, host_diagnostic; + + if (ioc->shost_recovery || + ioc->pci_error_recovery || ioc->is_driver_loading || + ioc->remove_host) + return -EAGAIN; + + ioc_state = mpt3sas_base_get_iocstate(ioc, 1); + + if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) + return -EFAULT; + + host_diagnostic = ioc->base_readl(&ioc->chip->HostDiagnostic); + if (host_diagnostic & MPI2_DIAG_SBR_RELOAD) + return 0; + + if (mutex_trylock(&ioc->hostdiag_unlock_mutex)) { + if (mpt3sas_base_unlock_and_get_host_diagnostic(ioc, &host_diagnostic)) { + mutex_unlock(&ioc->hostdiag_unlock_mutex); + return -EFAULT; + } + } else + return -EAGAIN; + + host_diagnostic |= MPI2_DIAG_SBR_RELOAD; + writel(host_diagnostic, &ioc->chip->HostDiagnostic); + host_diagnostic = ioc->base_readl(&ioc->chip->HostDiagnostic); + mpt3sas_base_lock_host_diagnostic(ioc); + mutex_unlock(&ioc->hostdiag_unlock_mutex); + + if (!(host_diagnostic & MPI2_DIAG_SBR_RELOAD)) { + ioc_err(ioc, "%s: Failed to set Diag SBR Reload Bit\n", __func__); + return -EFAULT; + } + + ioc_info(ioc, "%s: Successfully set the Diag SBR Reload Bit\n", __func__); + return 0; +} #ifdef CONFIG_COMPAT /** @@ -2258,7 +2719,7 @@ _ctl_ioctl_main(struct file *file, unsigned int cmd, void __user *arg, struct MPT3SAS_ADAPTER *ioc; struct mpt3_ioctl_header ioctl_header; enum block_state state; - long ret = -EINVAL; + long ret = -ENOIOCTLCMD; /* get IOCTL header */ if (copy_from_user(&ioctl_header, (char __user *)arg, @@ -2319,6 +2780,10 @@ _ctl_ioctl_main(struct file *file, unsigned int cmd, void __user *arg, break; } + if (karg.hdr.ioc_number != ioctl_header.ioc_number) { + ret = -EINVAL; + break; + } if (_IOC_SIZE(cmd) == sizeof(struct mpt3_ioctl_command)) { uarg = arg; ret = _ctl_do_mpt_command(ioc, karg, &uarg->mf); @@ -2364,6 +2829,14 @@ _ctl_ioctl_main(struct file *file, unsigned int cmd, void __user *arg, if (_IOC_SIZE(cmd) == sizeof(struct mpt3_diag_read_buffer)) ret = _ctl_diag_read_buffer(ioc, arg); break; + case MPT3ADDNLDIAGQUERY: + if (_IOC_SIZE(cmd) == sizeof(struct mpt3_addnl_diag_query)) + ret = _ctl_addnl_diag_query(ioc, arg); + break; + case MPT3ENABLEDIAGSBRRELOAD: + if (_IOC_SIZE(cmd) == sizeof(struct mpt3_enable_diag_sbr_reload)) + ret = _ctl_enable_diag_sbr_reload(ioc, arg); + break; default: dctlprintk(ioc, ioc_info(ioc, "unsupported ioctl opcode(0x%08x)\n", @@ -2378,6 +2851,215 @@ out_unlock_pciaccess: } /** + * _ctl_get_mpt_mctp_passthru_adapter - Traverse the IOC list and return the IOC at + * dev_index positionthat support MCTP passhtru + * @dev_index: position in the mpt3sas_ioc_list to search for + * Return pointer to the IOC on success + * NULL if device not found error + */ +static struct MPT3SAS_ADAPTER * +_ctl_get_mpt_mctp_passthru_adapter(int dev_index) +{ + struct MPT3SAS_ADAPTER *ioc = NULL; + int count = 0; + + spin_lock(&gioc_lock); + /* Traverse ioc list and return number of IOC that support MCTP passthru */ + list_for_each_entry(ioc, &mpt3sas_ioc_list, list) { + if (ioc->facts.IOCCapabilities & MPI26_IOCFACTS_CAPABILITY_MCTP_PASSTHRU) { + if (count == dev_index) { + spin_unlock(&gioc_lock); + return ioc; + } + count++; + } + } + spin_unlock(&gioc_lock); + + return NULL; +} + +/** + * mpt3sas_get_device_count - Retrieve the count of MCTP passthrough + * capable devices managed by the driver. + * + * Returns number of devices that support MCTP passthrough. + */ +int +mpt3sas_get_device_count(void) +{ + int count = 0; + struct MPT3SAS_ADAPTER *ioc = NULL; + + spin_lock(&gioc_lock); + /* Traverse ioc list and return number of IOC that support MCTP passthru */ + list_for_each_entry(ioc, &mpt3sas_ioc_list, list) + if (ioc->facts.IOCCapabilities & MPI26_IOCFACTS_CAPABILITY_MCTP_PASSTHRU) + count++; + + spin_unlock(&gioc_lock); + + return count; +} +EXPORT_SYMBOL(mpt3sas_get_device_count); + +/** + * mpt3sas_send_passthru_cmd - Send an MPI MCTP passthrough command to + * firmware + * @command: The MPI MCTP passthrough command to send to firmware + * + * Returns 0 on success, anything else is error. + */ +int mpt3sas_send_mctp_passthru_req(struct mpt3_passthru_command *command) +{ + struct MPT3SAS_ADAPTER *ioc; + MPI2RequestHeader_t *mpi_request = NULL, *request; + Mpi26MctpPassthroughRequest_t *mctp_passthru_req; + u16 smid; + unsigned long timeout; + u8 issue_reset = 0; + u32 sz; + void *psge; + void *data_out = NULL; + dma_addr_t data_out_dma = 0; + size_t data_out_sz = 0; + void *data_in = NULL; + dma_addr_t data_in_dma = 0; + size_t data_in_sz = 0; + long ret; + + /* Retrieve ioc from dev_index */ + ioc = _ctl_get_mpt_mctp_passthru_adapter(command->dev_index); + if (!ioc) + return -ENODEV; + + mutex_lock(&ioc->pci_access_mutex); + if (ioc->shost_recovery || + ioc->pci_error_recovery || ioc->is_driver_loading || + ioc->remove_host) { + ret = -EAGAIN; + goto unlock_pci_access; + } + + /* Lock the ctl_cmds mutex to ensure a single ctl cmd is pending */ + if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex)) { + ret = -ERESTARTSYS; + goto unlock_pci_access; + } + + if (ioc->ctl_cmds.status != MPT3_CMD_NOT_USED) { + ioc_err(ioc, "%s: ctl_cmd in use\n", __func__); + ret = -EAGAIN; + goto unlock_ctl_cmds; + } + + ret = mpt3sas_wait_for_ioc(ioc, IOC_OPERATIONAL_WAIT_COUNT); + if (ret) + goto unlock_ctl_cmds; + + mpi_request = (MPI2RequestHeader_t *)command->mpi_request; + if (mpi_request->Function != MPI2_FUNCTION_MCTP_PASSTHROUGH) { + ioc_err(ioc, "%s: Invalid request received, Function 0x%x\n", + __func__, mpi_request->Function); + ret = -EINVAL; + goto unlock_ctl_cmds; + } + + /* Use first reserved smid for passthrough commands */ + smid = ioc->scsiio_depth - INTERNAL_SCSIIO_CMDS_COUNT + 1; + ret = 0; + ioc->ctl_cmds.status = MPT3_CMD_PENDING; + memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz); + request = mpt3sas_base_get_msg_frame(ioc, smid); + memset(request, 0, ioc->request_sz); + memcpy(request, command->mpi_request, sizeof(Mpi26MctpPassthroughRequest_t)); + ioc->ctl_cmds.smid = smid; + data_out_sz = command->data_out_size; + data_in_sz = command->data_in_size; + + /* obtain dma-able memory for data transfer */ + if (data_out_sz) /* WRITE */ { + data_out = dma_alloc_coherent(&ioc->pdev->dev, data_out_sz, + &data_out_dma, GFP_ATOMIC); + if (!data_out) { + ret = -ENOMEM; + mpt3sas_base_free_smid(ioc, smid); + goto out; + } + memcpy(data_out, command->data_out_buf_ptr, data_out_sz); + + } + + if (data_in_sz) /* READ */ { + data_in = dma_alloc_coherent(&ioc->pdev->dev, data_in_sz, + &data_in_dma, GFP_ATOMIC); + if (!data_in) { + ret = -ENOMEM; + mpt3sas_base_free_smid(ioc, smid); + goto out; + } + } + + psge = &((Mpi26MctpPassthroughRequest_t *)request)->H2DSGL; + + init_completion(&ioc->ctl_cmds.done); + + mctp_passthru_req = (Mpi26MctpPassthroughRequest_t *)request; + + _ctl_send_mctp_passthru_req(ioc, mctp_passthru_req, psge, data_out_dma, + data_out_sz, data_in_dma, data_in_sz, smid); + + timeout = command->timeout; + if (timeout < MPT3_IOCTL_DEFAULT_TIMEOUT) + timeout = MPT3_IOCTL_DEFAULT_TIMEOUT; + + wait_for_completion_timeout(&ioc->ctl_cmds.done, timeout*HZ); + if (!(ioc->ctl_cmds.status & MPT3_CMD_COMPLETE)) { + mpt3sas_check_cmd_timeout(ioc, + ioc->ctl_cmds.status, mpi_request, + sizeof(Mpi26MctpPassthroughRequest_t) / 4, issue_reset); + goto issue_host_reset; + } + + /* copy out xdata to user */ + if (data_in_sz) + memcpy(command->data_in_buf_ptr, data_in, data_in_sz); + + /* copy out reply message frame to user */ + if (command->max_reply_bytes) { + sz = min_t(u32, command->max_reply_bytes, ioc->reply_sz); + memcpy(command->reply_frame_buf_ptr, ioc->ctl_cmds.reply, sz); + } + +issue_host_reset: + if (issue_reset) { + ret = -ENODATA; + mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); + } + +out: + /* free memory associated with sg buffers */ + if (data_in) + dma_free_coherent(&ioc->pdev->dev, data_in_sz, data_in, + data_in_dma); + + if (data_out) + dma_free_coherent(&ioc->pdev->dev, data_out_sz, data_out, + data_out_dma); + + ioc->ctl_cmds.status = MPT3_CMD_NOT_USED; + +unlock_ctl_cmds: + mutex_unlock(&ioc->ctl_cmds.mutex); + +unlock_pci_access: + mutex_unlock(&ioc->pci_access_mutex); + return ret; + +} +EXPORT_SYMBOL(mpt3sas_send_mctp_passthru_req); + +/** * _ctl_ioctl - mpt3ctl main ioctl entry point (unlocked) * @file: (struct file) * @cmd: ioctl opcode @@ -2416,7 +3098,7 @@ _ctl_mpt2_ioctl(struct file *file, unsigned int cmd, unsigned long arg) } #ifdef CONFIG_COMPAT /** - *_ ctl_ioctl_compat - main ioctl entry point (compat) + * _ctl_ioctl_compat - main ioctl entry point (compat) * @file: ? * @cmd: ? * @arg: ? @@ -2434,7 +3116,7 @@ _ctl_ioctl_compat(struct file *file, unsigned cmd, unsigned long arg) } /** - *_ ctl_mpt2_ioctl_compat - main ioctl entry point (compat) + * _ctl_mpt2_ioctl_compat - main ioctl entry point (compat) * @file: ? * @cmd: ? * @arg: ? @@ -2453,7 +3135,7 @@ _ctl_mpt2_ioctl_compat(struct file *file, unsigned cmd, unsigned long arg) /* scsi host attributes */ /** - * _ctl_version_fw_show - firmware version + * version_fw_show - firmware version * @cdev: pointer to embedded class device * @attr: ? * @buf: the buffer returned @@ -2461,7 +3143,7 @@ _ctl_mpt2_ioctl_compat(struct file *file, unsigned cmd, unsigned long arg) * A sysfs 'read-only' shost attribute. */ static ssize_t -_ctl_version_fw_show(struct device *cdev, struct device_attribute *attr, +version_fw_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); @@ -2473,10 +3155,10 @@ _ctl_version_fw_show(struct device *cdev, struct device_attribute *attr, (ioc->facts.FWVersion.Word & 0x0000FF00) >> 8, ioc->facts.FWVersion.Word & 0x000000FF); } -static DEVICE_ATTR(version_fw, S_IRUGO, _ctl_version_fw_show, NULL); +static DEVICE_ATTR_RO(version_fw); /** - * _ctl_version_bios_show - bios version + * version_bios_show - bios version * @cdev: pointer to embedded class device * @attr: ? * @buf: the buffer returned @@ -2484,7 +3166,7 @@ static DEVICE_ATTR(version_fw, S_IRUGO, _ctl_version_fw_show, NULL); * A sysfs 'read-only' shost attribute. */ static ssize_t -_ctl_version_bios_show(struct device *cdev, struct device_attribute *attr, +version_bios_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); @@ -2498,10 +3180,10 @@ _ctl_version_bios_show(struct device *cdev, struct device_attribute *attr, (version & 0x0000FF00) >> 8, version & 0x000000FF); } -static DEVICE_ATTR(version_bios, S_IRUGO, _ctl_version_bios_show, NULL); +static DEVICE_ATTR_RO(version_bios); /** - * _ctl_version_mpi_show - MPI (message passing interface) version + * version_mpi_show - MPI (message passing interface) version * @cdev: pointer to embedded class device * @attr: ? * @buf: the buffer returned @@ -2509,7 +3191,7 @@ static DEVICE_ATTR(version_bios, S_IRUGO, _ctl_version_bios_show, NULL); * A sysfs 'read-only' shost attribute. */ static ssize_t -_ctl_version_mpi_show(struct device *cdev, struct device_attribute *attr, +version_mpi_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); @@ -2518,10 +3200,10 @@ _ctl_version_mpi_show(struct device *cdev, struct device_attribute *attr, return snprintf(buf, PAGE_SIZE, "%03x.%02x\n", ioc->facts.MsgVersion, ioc->facts.HeaderVersion >> 8); } -static DEVICE_ATTR(version_mpi, S_IRUGO, _ctl_version_mpi_show, NULL); +static DEVICE_ATTR_RO(version_mpi); /** - * _ctl_version_product_show - product name + * version_product_show - product name * @cdev: pointer to embedded class device * @attr: ? * @buf: the buffer returned @@ -2529,7 +3211,7 @@ static DEVICE_ATTR(version_mpi, S_IRUGO, _ctl_version_mpi_show, NULL); * A sysfs 'read-only' shost attribute. */ static ssize_t -_ctl_version_product_show(struct device *cdev, struct device_attribute *attr, +version_product_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); @@ -2537,10 +3219,10 @@ _ctl_version_product_show(struct device *cdev, struct device_attribute *attr, return snprintf(buf, 16, "%s\n", ioc->manu_pg0.ChipName); } -static DEVICE_ATTR(version_product, S_IRUGO, _ctl_version_product_show, NULL); +static DEVICE_ATTR_RO(version_product); /** - * _ctl_version_nvdata_persistent_show - ndvata persistent version + * version_nvdata_persistent_show - ndvata persistent version * @cdev: pointer to embedded class device * @attr: ? * @buf: the buffer returned @@ -2548,7 +3230,7 @@ static DEVICE_ATTR(version_product, S_IRUGO, _ctl_version_product_show, NULL); * A sysfs 'read-only' shost attribute. */ static ssize_t -_ctl_version_nvdata_persistent_show(struct device *cdev, +version_nvdata_persistent_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); @@ -2557,11 +3239,10 @@ _ctl_version_nvdata_persistent_show(struct device *cdev, return snprintf(buf, PAGE_SIZE, "%08xh\n", le32_to_cpu(ioc->iounit_pg0.NvdataVersionPersistent.Word)); } -static DEVICE_ATTR(version_nvdata_persistent, S_IRUGO, - _ctl_version_nvdata_persistent_show, NULL); +static DEVICE_ATTR_RO(version_nvdata_persistent); /** - * _ctl_version_nvdata_default_show - nvdata default version + * version_nvdata_default_show - nvdata default version * @cdev: pointer to embedded class device * @attr: ? * @buf: the buffer returned @@ -2569,7 +3250,7 @@ static DEVICE_ATTR(version_nvdata_persistent, S_IRUGO, * A sysfs 'read-only' shost attribute. */ static ssize_t -_ctl_version_nvdata_default_show(struct device *cdev, struct device_attribute +version_nvdata_default_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); @@ -2578,11 +3259,10 @@ _ctl_version_nvdata_default_show(struct device *cdev, struct device_attribute return snprintf(buf, PAGE_SIZE, "%08xh\n", le32_to_cpu(ioc->iounit_pg0.NvdataVersionDefault.Word)); } -static DEVICE_ATTR(version_nvdata_default, S_IRUGO, - _ctl_version_nvdata_default_show, NULL); +static DEVICE_ATTR_RO(version_nvdata_default); /** - * _ctl_board_name_show - board name + * board_name_show - board name * @cdev: pointer to embedded class device * @attr: ? * @buf: the buffer returned @@ -2590,7 +3270,7 @@ static DEVICE_ATTR(version_nvdata_default, S_IRUGO, * A sysfs 'read-only' shost attribute. */ static ssize_t -_ctl_board_name_show(struct device *cdev, struct device_attribute *attr, +board_name_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); @@ -2598,10 +3278,10 @@ _ctl_board_name_show(struct device *cdev, struct device_attribute *attr, return snprintf(buf, 16, "%s\n", ioc->manu_pg0.BoardName); } -static DEVICE_ATTR(board_name, S_IRUGO, _ctl_board_name_show, NULL); +static DEVICE_ATTR_RO(board_name); /** - * _ctl_board_assembly_show - board assembly name + * board_assembly_show - board assembly name * @cdev: pointer to embedded class device * @attr: ? * @buf: the buffer returned @@ -2609,7 +3289,7 @@ static DEVICE_ATTR(board_name, S_IRUGO, _ctl_board_name_show, NULL); * A sysfs 'read-only' shost attribute. */ static ssize_t -_ctl_board_assembly_show(struct device *cdev, struct device_attribute *attr, +board_assembly_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); @@ -2617,10 +3297,10 @@ _ctl_board_assembly_show(struct device *cdev, struct device_attribute *attr, return snprintf(buf, 16, "%s\n", ioc->manu_pg0.BoardAssembly); } -static DEVICE_ATTR(board_assembly, S_IRUGO, _ctl_board_assembly_show, NULL); +static DEVICE_ATTR_RO(board_assembly); /** - * _ctl_board_tracer_show - board tracer number + * board_tracer_show - board tracer number * @cdev: pointer to embedded class device * @attr: ? * @buf: the buffer returned @@ -2628,7 +3308,7 @@ static DEVICE_ATTR(board_assembly, S_IRUGO, _ctl_board_assembly_show, NULL); * A sysfs 'read-only' shost attribute. */ static ssize_t -_ctl_board_tracer_show(struct device *cdev, struct device_attribute *attr, +board_tracer_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); @@ -2636,10 +3316,10 @@ _ctl_board_tracer_show(struct device *cdev, struct device_attribute *attr, return snprintf(buf, 16, "%s\n", ioc->manu_pg0.BoardTracerNumber); } -static DEVICE_ATTR(board_tracer, S_IRUGO, _ctl_board_tracer_show, NULL); +static DEVICE_ATTR_RO(board_tracer); /** - * _ctl_io_delay_show - io missing delay + * io_delay_show - io missing delay * @cdev: pointer to embedded class device * @attr: ? * @buf: the buffer returned @@ -2650,7 +3330,7 @@ static DEVICE_ATTR(board_tracer, S_IRUGO, _ctl_board_tracer_show, NULL); * A sysfs 'read-only' shost attribute. */ static ssize_t -_ctl_io_delay_show(struct device *cdev, struct device_attribute *attr, +io_delay_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); @@ -2658,10 +3338,10 @@ _ctl_io_delay_show(struct device *cdev, struct device_attribute *attr, return snprintf(buf, PAGE_SIZE, "%02d\n", ioc->io_missing_delay); } -static DEVICE_ATTR(io_delay, S_IRUGO, _ctl_io_delay_show, NULL); +static DEVICE_ATTR_RO(io_delay); /** - * _ctl_device_delay_show - device missing delay + * device_delay_show - device missing delay * @cdev: pointer to embedded class device * @attr: ? * @buf: the buffer returned @@ -2672,7 +3352,7 @@ static DEVICE_ATTR(io_delay, S_IRUGO, _ctl_io_delay_show, NULL); * A sysfs 'read-only' shost attribute. */ static ssize_t -_ctl_device_delay_show(struct device *cdev, struct device_attribute *attr, +device_delay_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); @@ -2680,10 +3360,10 @@ _ctl_device_delay_show(struct device *cdev, struct device_attribute *attr, return snprintf(buf, PAGE_SIZE, "%02d\n", ioc->device_missing_delay); } -static DEVICE_ATTR(device_delay, S_IRUGO, _ctl_device_delay_show, NULL); +static DEVICE_ATTR_RO(device_delay); /** - * _ctl_fw_queue_depth_show - global credits + * fw_queue_depth_show - global credits * @cdev: pointer to embedded class device * @attr: ? * @buf: the buffer returned @@ -2693,7 +3373,7 @@ static DEVICE_ATTR(device_delay, S_IRUGO, _ctl_device_delay_show, NULL); * A sysfs 'read-only' shost attribute. */ static ssize_t -_ctl_fw_queue_depth_show(struct device *cdev, struct device_attribute *attr, +fw_queue_depth_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); @@ -2701,10 +3381,10 @@ _ctl_fw_queue_depth_show(struct device *cdev, struct device_attribute *attr, return snprintf(buf, PAGE_SIZE, "%02d\n", ioc->facts.RequestCredit); } -static DEVICE_ATTR(fw_queue_depth, S_IRUGO, _ctl_fw_queue_depth_show, NULL); +static DEVICE_ATTR_RO(fw_queue_depth); /** - * _ctl_sas_address_show - sas address + * host_sas_address_show - sas address * @cdev: pointer to embedded class device * @attr: ? * @buf: the buffer returned @@ -2714,7 +3394,7 @@ static DEVICE_ATTR(fw_queue_depth, S_IRUGO, _ctl_fw_queue_depth_show, NULL); * A sysfs 'read-only' shost attribute. */ static ssize_t -_ctl_host_sas_address_show(struct device *cdev, struct device_attribute *attr, +host_sas_address_show(struct device *cdev, struct device_attribute *attr, char *buf) { @@ -2724,11 +3404,10 @@ _ctl_host_sas_address_show(struct device *cdev, struct device_attribute *attr, return snprintf(buf, PAGE_SIZE, "0x%016llx\n", (unsigned long long)ioc->sas_hba.sas_address); } -static DEVICE_ATTR(host_sas_address, S_IRUGO, - _ctl_host_sas_address_show, NULL); +static DEVICE_ATTR_RO(host_sas_address); /** - * _ctl_logging_level_show - logging level + * logging_level_show - logging level * @cdev: pointer to embedded class device * @attr: ? * @buf: the buffer returned @@ -2736,7 +3415,7 @@ static DEVICE_ATTR(host_sas_address, S_IRUGO, * A sysfs 'read/write' shost attribute. */ static ssize_t -_ctl_logging_level_show(struct device *cdev, struct device_attribute *attr, +logging_level_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); @@ -2745,7 +3424,7 @@ _ctl_logging_level_show(struct device *cdev, struct device_attribute *attr, return snprintf(buf, PAGE_SIZE, "%08xh\n", ioc->logging_level); } static ssize_t -_ctl_logging_level_store(struct device *cdev, struct device_attribute *attr, +logging_level_store(struct device *cdev, struct device_attribute *attr, const char *buf, size_t count) { struct Scsi_Host *shost = class_to_shost(cdev); @@ -2760,11 +3439,10 @@ _ctl_logging_level_store(struct device *cdev, struct device_attribute *attr, ioc->logging_level); return strlen(buf); } -static DEVICE_ATTR(logging_level, S_IRUGO | S_IWUSR, _ctl_logging_level_show, - _ctl_logging_level_store); +static DEVICE_ATTR_RW(logging_level); /** - * _ctl_fwfault_debug_show - show/store fwfault_debug + * fwfault_debug_show - show/store fwfault_debug * @cdev: pointer to embedded class device * @attr: ? * @buf: the buffer returned @@ -2773,7 +3451,7 @@ static DEVICE_ATTR(logging_level, S_IRUGO | S_IWUSR, _ctl_logging_level_show, * A sysfs 'read/write' shost attribute. */ static ssize_t -_ctl_fwfault_debug_show(struct device *cdev, struct device_attribute *attr, +fwfault_debug_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); @@ -2782,7 +3460,7 @@ _ctl_fwfault_debug_show(struct device *cdev, struct device_attribute *attr, return snprintf(buf, PAGE_SIZE, "%d\n", ioc->fwfault_debug); } static ssize_t -_ctl_fwfault_debug_store(struct device *cdev, struct device_attribute *attr, +fwfault_debug_store(struct device *cdev, struct device_attribute *attr, const char *buf, size_t count) { struct Scsi_Host *shost = class_to_shost(cdev); @@ -2797,11 +3475,10 @@ _ctl_fwfault_debug_store(struct device *cdev, struct device_attribute *attr, ioc->fwfault_debug); return strlen(buf); } -static DEVICE_ATTR(fwfault_debug, S_IRUGO | S_IWUSR, - _ctl_fwfault_debug_show, _ctl_fwfault_debug_store); +static DEVICE_ATTR_RW(fwfault_debug); /** - * _ctl_ioc_reset_count_show - ioc reset count + * ioc_reset_count_show - ioc reset count * @cdev: pointer to embedded class device * @attr: ? * @buf: the buffer returned @@ -2811,7 +3488,7 @@ static DEVICE_ATTR(fwfault_debug, S_IRUGO | S_IWUSR, * A sysfs 'read-only' shost attribute. */ static ssize_t -_ctl_ioc_reset_count_show(struct device *cdev, struct device_attribute *attr, +ioc_reset_count_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); @@ -2819,10 +3496,10 @@ _ctl_ioc_reset_count_show(struct device *cdev, struct device_attribute *attr, return snprintf(buf, PAGE_SIZE, "%d\n", ioc->ioc_reset_count); } -static DEVICE_ATTR(ioc_reset_count, S_IRUGO, _ctl_ioc_reset_count_show, NULL); +static DEVICE_ATTR_RO(ioc_reset_count); /** - * _ctl_ioc_reply_queue_count_show - number of reply queues + * reply_queue_count_show - number of reply queues * @cdev: pointer to embedded class device * @attr: ? * @buf: the buffer returned @@ -2832,7 +3509,7 @@ static DEVICE_ATTR(ioc_reset_count, S_IRUGO, _ctl_ioc_reset_count_show, NULL); * A sysfs 'read-only' shost attribute. */ static ssize_t -_ctl_ioc_reply_queue_count_show(struct device *cdev, +reply_queue_count_show(struct device *cdev, struct device_attribute *attr, char *buf) { u8 reply_queue_count; @@ -2847,11 +3524,10 @@ _ctl_ioc_reply_queue_count_show(struct device *cdev, return snprintf(buf, PAGE_SIZE, "%d\n", reply_queue_count); } -static DEVICE_ATTR(reply_queue_count, S_IRUGO, _ctl_ioc_reply_queue_count_show, - NULL); +static DEVICE_ATTR_RO(reply_queue_count); /** - * _ctl_BRM_status_show - Backup Rail Monitor Status + * BRM_status_show - Backup Rail Monitor Status * @cdev: pointer to embedded class device * @attr: ? * @buf: the buffer returned @@ -2861,12 +3537,12 @@ static DEVICE_ATTR(reply_queue_count, S_IRUGO, _ctl_ioc_reply_queue_count_show, * A sysfs 'read-only' shost attribute. */ static ssize_t -_ctl_BRM_status_show(struct device *cdev, struct device_attribute *attr, +BRM_status_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); - Mpi2IOUnitPage3_t *io_unit_pg3 = NULL; + Mpi2IOUnitPage3_t io_unit_pg3; Mpi2ConfigReply_t mpi_reply; u16 backup_rail_monitor_status = 0; u16 ioc_status; @@ -2876,28 +3552,21 @@ _ctl_BRM_status_show(struct device *cdev, struct device_attribute *attr, if (!ioc->is_warpdrive) { ioc_err(ioc, "%s: BRM attribute is only for warpdrive\n", __func__); - goto out; + return 0; } /* pci_access_mutex lock acquired by sysfs show path */ mutex_lock(&ioc->pci_access_mutex); - if (ioc->pci_error_recovery || ioc->remove_host) { - mutex_unlock(&ioc->pci_access_mutex); - return 0; - } - - /* allocate upto GPIOVal 36 entries */ - sz = offsetof(Mpi2IOUnitPage3_t, GPIOVal) + (sizeof(u16) * 36); - io_unit_pg3 = kzalloc(sz, GFP_KERNEL); - if (!io_unit_pg3) { - ioc_err(ioc, "%s: failed allocating memory for iounit_pg3: (%d) bytes\n", - __func__, sz); + if (ioc->pci_error_recovery || ioc->remove_host) goto out; - } - if (mpt3sas_config_get_iounit_pg3(ioc, &mpi_reply, io_unit_pg3, sz) != + sz = sizeof(io_unit_pg3); + memset(&io_unit_pg3, 0, sz); + + if (mpt3sas_config_get_iounit_pg3(ioc, &mpi_reply, &io_unit_pg3, sz) != 0) { ioc_err(ioc, "%s: failed reading iounit_pg3\n", __func__); + rc = -EINVAL; goto out; } @@ -2905,25 +3574,26 @@ _ctl_BRM_status_show(struct device *cdev, struct device_attribute *attr, if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { ioc_err(ioc, "%s: iounit_pg3 failed with ioc_status(0x%04x)\n", __func__, ioc_status); + rc = -EINVAL; goto out; } - if (io_unit_pg3->GPIOCount < 25) { - ioc_err(ioc, "%s: iounit_pg3->GPIOCount less than 25 entries, detected (%d) entries\n", - __func__, io_unit_pg3->GPIOCount); + if (io_unit_pg3.GPIOCount < 25) { + ioc_err(ioc, "%s: iounit_pg3.GPIOCount less than 25 entries, detected (%d) entries\n", + __func__, io_unit_pg3.GPIOCount); + rc = -EINVAL; goto out; } /* BRM status is in bit zero of GPIOVal[24] */ - backup_rail_monitor_status = le16_to_cpu(io_unit_pg3->GPIOVal[24]); + backup_rail_monitor_status = le16_to_cpu(io_unit_pg3.GPIOVal[24]); rc = snprintf(buf, PAGE_SIZE, "%d\n", (backup_rail_monitor_status & 1)); out: - kfree(io_unit_pg3); mutex_unlock(&ioc->pci_access_mutex); return rc; } -static DEVICE_ATTR(BRM_status, S_IRUGO, _ctl_BRM_status_show, NULL); +static DEVICE_ATTR_RO(BRM_status); struct DIAG_BUFFER_START { __le32 Size; @@ -2936,7 +3606,7 @@ struct DIAG_BUFFER_START { }; /** - * _ctl_host_trace_buffer_size_show - host buffer size (trace only) + * host_trace_buffer_size_show - host buffer size (trace only) * @cdev: pointer to embedded class device * @attr: ? * @buf: the buffer returned @@ -2944,7 +3614,7 @@ struct DIAG_BUFFER_START { * A sysfs 'read-only' shost attribute. */ static ssize_t -_ctl_host_trace_buffer_size_show(struct device *cdev, +host_trace_buffer_size_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); @@ -2976,11 +3646,10 @@ _ctl_host_trace_buffer_size_show(struct device *cdev, ioc->ring_buffer_sz = size; return snprintf(buf, PAGE_SIZE, "%d\n", size); } -static DEVICE_ATTR(host_trace_buffer_size, S_IRUGO, - _ctl_host_trace_buffer_size_show, NULL); +static DEVICE_ATTR_RO(host_trace_buffer_size); /** - * _ctl_host_trace_buffer_show - firmware ring buffer (trace only) + * host_trace_buffer_show - firmware ring buffer (trace only) * @cdev: pointer to embedded class device * @attr: ? * @buf: the buffer returned @@ -2992,7 +3661,7 @@ static DEVICE_ATTR(host_trace_buffer_size, S_IRUGO, * offset to the same attribute, it will move the pointer. */ static ssize_t -_ctl_host_trace_buffer_show(struct device *cdev, struct device_attribute *attr, +host_trace_buffer_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); @@ -3024,7 +3693,7 @@ _ctl_host_trace_buffer_show(struct device *cdev, struct device_attribute *attr, } static ssize_t -_ctl_host_trace_buffer_store(struct device *cdev, struct device_attribute *attr, +host_trace_buffer_store(struct device *cdev, struct device_attribute *attr, const char *buf, size_t count) { struct Scsi_Host *shost = class_to_shost(cdev); @@ -3037,14 +3706,13 @@ _ctl_host_trace_buffer_store(struct device *cdev, struct device_attribute *attr, ioc->ring_buffer_offset = val; return strlen(buf); } -static DEVICE_ATTR(host_trace_buffer, S_IRUGO | S_IWUSR, - _ctl_host_trace_buffer_show, _ctl_host_trace_buffer_store); +static DEVICE_ATTR_RW(host_trace_buffer); /*****************************************/ /** - * _ctl_host_trace_buffer_enable_show - firmware ring buffer (trace only) + * host_trace_buffer_enable_show - firmware ring buffer (trace only) * @cdev: pointer to embedded class device * @attr: ? * @buf: the buffer returned @@ -3054,7 +3722,7 @@ static DEVICE_ATTR(host_trace_buffer, S_IRUGO | S_IWUSR, * This is a mechnism to post/release host_trace_buffers */ static ssize_t -_ctl_host_trace_buffer_enable_show(struct device *cdev, +host_trace_buffer_enable_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); @@ -3072,7 +3740,7 @@ _ctl_host_trace_buffer_enable_show(struct device *cdev, } static ssize_t -_ctl_host_trace_buffer_enable_store(struct device *cdev, +host_trace_buffer_enable_store(struct device *cdev, struct device_attribute *attr, const char *buf, size_t count) { struct Scsi_Host *shost = class_to_shost(cdev); @@ -3100,10 +3768,47 @@ _ctl_host_trace_buffer_enable_store(struct device *cdev, memset(&diag_register, 0, sizeof(struct mpt3_diag_register)); ioc_info(ioc, "posting host trace buffers\n"); diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_TRACE; - diag_register.requested_buffer_size = (1024 * 1024); - diag_register.unique_id = 0x7075900; + + if (ioc->manu_pg11.HostTraceBufferMaxSizeKB != 0 && + ioc->diag_buffer_sz[MPI2_DIAG_BUF_TYPE_TRACE] != 0) { + /* post the same buffer allocated previously */ + diag_register.requested_buffer_size = + ioc->diag_buffer_sz[MPI2_DIAG_BUF_TYPE_TRACE]; + } else { + /* + * Free the diag buffer memory which was previously + * allocated by an application. + */ + if ((ioc->diag_buffer_sz[MPI2_DIAG_BUF_TYPE_TRACE] != 0) + && + (ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & + MPT3_DIAG_BUFFER_IS_APP_OWNED)) { + dma_free_coherent(&ioc->pdev->dev, + ioc->diag_buffer_sz[MPI2_DIAG_BUF_TYPE_TRACE], + ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE], + ioc->diag_buffer_dma[MPI2_DIAG_BUF_TYPE_TRACE]); + ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE] = + NULL; + } + + diag_register.requested_buffer_size = (1024 * 1024); + } + + diag_register.unique_id = + (ioc->hba_mpi_version_belonged == MPI2_VERSION) ? + (MPT2DIAGBUFFUNIQUEID):(MPT3DIAGBUFFUNIQUEID); ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] = 0; _ctl_diag_register_2(ioc, &diag_register); + if (ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & + MPT3_DIAG_BUFFER_IS_REGISTERED) { + ioc_info(ioc, + "Trace buffer %d KB allocated through sysfs\n", + diag_register.requested_buffer_size>>10); + if (ioc->hba_mpi_version_belonged != MPI2_VERSION) + ioc->diag_buffer_status[ + MPI2_DIAG_BUF_TYPE_TRACE] |= + MPT3_DIAG_BUFFER_IS_DRIVER_ALLOCATED; + } } else if (!strcmp(str, "release")) { /* exit out if host buffers are already released */ if (!ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]) @@ -3115,6 +3820,7 @@ _ctl_host_trace_buffer_enable_store(struct device *cdev, MPT3_DIAG_BUFFER_IS_RELEASED)) goto out; ioc_info(ioc, "releasing host trace buffer\n"); + ioc->htb_rel.buffer_rel_condition = MPT3_DIAG_BUFFER_REL_SYSFS; mpt3sas_send_diag_release(ioc, MPI2_DIAG_BUF_TYPE_TRACE, &issue_reset); } @@ -3122,14 +3828,12 @@ _ctl_host_trace_buffer_enable_store(struct device *cdev, out: return strlen(buf); } -static DEVICE_ATTR(host_trace_buffer_enable, S_IRUGO | S_IWUSR, - _ctl_host_trace_buffer_enable_show, - _ctl_host_trace_buffer_enable_store); +static DEVICE_ATTR_RW(host_trace_buffer_enable); /*********** diagnostic trigger suppport *********************************/ /** - * _ctl_diag_trigger_master_show - show the diag_trigger_master attribute + * diag_trigger_master_show - show the diag_trigger_master attribute * @cdev: pointer to embedded class device * @attr: ? * @buf: the buffer returned @@ -3137,7 +3841,7 @@ static DEVICE_ATTR(host_trace_buffer_enable, S_IRUGO | S_IWUSR, * A sysfs 'read/write' shost attribute. */ static ssize_t -_ctl_diag_trigger_master_show(struct device *cdev, +diag_trigger_master_show(struct device *cdev, struct device_attribute *attr, char *buf) { @@ -3154,7 +3858,7 @@ _ctl_diag_trigger_master_show(struct device *cdev, } /** - * _ctl_diag_trigger_master_store - store the diag_trigger_master attribute + * diag_trigger_master_store - store the diag_trigger_master attribute * @cdev: pointer to embedded class device * @attr: ? * @buf: the buffer returned @@ -3163,17 +3867,37 @@ _ctl_diag_trigger_master_show(struct device *cdev, * A sysfs 'read/write' shost attribute. */ static ssize_t -_ctl_diag_trigger_master_store(struct device *cdev, +diag_trigger_master_store(struct device *cdev, struct device_attribute *attr, const char *buf, size_t count) { struct Scsi_Host *shost = class_to_shost(cdev); struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); + struct SL_WH_MASTER_TRIGGER_T *master_tg; unsigned long flags; ssize_t rc; + bool set = 1; - spin_lock_irqsave(&ioc->diag_trigger_lock, flags); rc = min(sizeof(struct SL_WH_MASTER_TRIGGER_T), count); + + if (ioc->supports_trigger_pages) { + master_tg = kzalloc(sizeof(struct SL_WH_MASTER_TRIGGER_T), + GFP_KERNEL); + if (!master_tg) + return -ENOMEM; + + memcpy(master_tg, buf, rc); + if (!master_tg->MasterData) + set = 0; + if (mpt3sas_config_update_driver_trigger_pg1(ioc, master_tg, + set)) { + kfree(master_tg); + return -EFAULT; + } + kfree(master_tg); + } + + spin_lock_irqsave(&ioc->diag_trigger_lock, flags); memset(&ioc->diag_trigger_master, 0, sizeof(struct SL_WH_MASTER_TRIGGER_T)); memcpy(&ioc->diag_trigger_master, buf, rc); @@ -3182,12 +3906,11 @@ _ctl_diag_trigger_master_store(struct device *cdev, spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); return rc; } -static DEVICE_ATTR(diag_trigger_master, S_IRUGO | S_IWUSR, - _ctl_diag_trigger_master_show, _ctl_diag_trigger_master_store); +static DEVICE_ATTR_RW(diag_trigger_master); /** - * _ctl_diag_trigger_event_show - show the diag_trigger_event attribute + * diag_trigger_event_show - show the diag_trigger_event attribute * @cdev: pointer to embedded class device * @attr: ? * @buf: the buffer returned @@ -3195,7 +3918,7 @@ static DEVICE_ATTR(diag_trigger_master, S_IRUGO | S_IWUSR, * A sysfs 'read/write' shost attribute. */ static ssize_t -_ctl_diag_trigger_event_show(struct device *cdev, +diag_trigger_event_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); @@ -3211,7 +3934,7 @@ _ctl_diag_trigger_event_show(struct device *cdev, } /** - * _ctl_diag_trigger_event_store - store the diag_trigger_event attribute + * diag_trigger_event_store - store the diag_trigger_event attribute * @cdev: pointer to embedded class device * @attr: ? * @buf: the buffer returned @@ -3220,17 +3943,37 @@ _ctl_diag_trigger_event_show(struct device *cdev, * A sysfs 'read/write' shost attribute. */ static ssize_t -_ctl_diag_trigger_event_store(struct device *cdev, +diag_trigger_event_store(struct device *cdev, struct device_attribute *attr, const char *buf, size_t count) { struct Scsi_Host *shost = class_to_shost(cdev); struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); + struct SL_WH_EVENT_TRIGGERS_T *event_tg; unsigned long flags; ssize_t sz; + bool set = 1; - spin_lock_irqsave(&ioc->diag_trigger_lock, flags); sz = min(sizeof(struct SL_WH_EVENT_TRIGGERS_T), count); + if (ioc->supports_trigger_pages) { + event_tg = kzalloc(sizeof(struct SL_WH_EVENT_TRIGGERS_T), + GFP_KERNEL); + if (!event_tg) + return -ENOMEM; + + memcpy(event_tg, buf, sz); + if (!event_tg->ValidEntries) + set = 0; + if (mpt3sas_config_update_driver_trigger_pg2(ioc, event_tg, + set)) { + kfree(event_tg); + return -EFAULT; + } + kfree(event_tg); + } + + spin_lock_irqsave(&ioc->diag_trigger_lock, flags); + memset(&ioc->diag_trigger_event, 0, sizeof(struct SL_WH_EVENT_TRIGGERS_T)); memcpy(&ioc->diag_trigger_event, buf, sz); @@ -3239,12 +3982,11 @@ _ctl_diag_trigger_event_store(struct device *cdev, spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); return sz; } -static DEVICE_ATTR(diag_trigger_event, S_IRUGO | S_IWUSR, - _ctl_diag_trigger_event_show, _ctl_diag_trigger_event_store); +static DEVICE_ATTR_RW(diag_trigger_event); /** - * _ctl_diag_trigger_scsi_show - show the diag_trigger_scsi attribute + * diag_trigger_scsi_show - show the diag_trigger_scsi attribute * @cdev: pointer to embedded class device * @attr: ? * @buf: the buffer returned @@ -3252,7 +3994,7 @@ static DEVICE_ATTR(diag_trigger_event, S_IRUGO | S_IWUSR, * A sysfs 'read/write' shost attribute. */ static ssize_t -_ctl_diag_trigger_scsi_show(struct device *cdev, +diag_trigger_scsi_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); @@ -3268,7 +4010,7 @@ _ctl_diag_trigger_scsi_show(struct device *cdev, } /** - * _ctl_diag_trigger_scsi_store - store the diag_trigger_scsi attribute + * diag_trigger_scsi_store - store the diag_trigger_scsi attribute * @cdev: pointer to embedded class device * @attr: ? * @buf: the buffer returned @@ -3277,30 +4019,48 @@ _ctl_diag_trigger_scsi_show(struct device *cdev, * A sysfs 'read/write' shost attribute. */ static ssize_t -_ctl_diag_trigger_scsi_store(struct device *cdev, +diag_trigger_scsi_store(struct device *cdev, struct device_attribute *attr, const char *buf, size_t count) { struct Scsi_Host *shost = class_to_shost(cdev); struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); + struct SL_WH_SCSI_TRIGGERS_T *scsi_tg; unsigned long flags; ssize_t sz; + bool set = 1; - spin_lock_irqsave(&ioc->diag_trigger_lock, flags); sz = min(sizeof(struct SL_WH_SCSI_TRIGGERS_T), count); - memset(&ioc->diag_trigger_scsi, 0, - sizeof(struct SL_WH_EVENT_TRIGGERS_T)); + if (ioc->supports_trigger_pages) { + scsi_tg = kzalloc(sizeof(struct SL_WH_SCSI_TRIGGERS_T), + GFP_KERNEL); + if (!scsi_tg) + return -ENOMEM; + + memcpy(scsi_tg, buf, sz); + if (!scsi_tg->ValidEntries) + set = 0; + if (mpt3sas_config_update_driver_trigger_pg3(ioc, scsi_tg, + set)) { + kfree(scsi_tg); + return -EFAULT; + } + kfree(scsi_tg); + } + + spin_lock_irqsave(&ioc->diag_trigger_lock, flags); + + memset(&ioc->diag_trigger_scsi, 0, sizeof(ioc->diag_trigger_scsi)); memcpy(&ioc->diag_trigger_scsi, buf, sz); if (ioc->diag_trigger_scsi.ValidEntries > NUM_VALID_ENTRIES) ioc->diag_trigger_scsi.ValidEntries = NUM_VALID_ENTRIES; spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags); return sz; } -static DEVICE_ATTR(diag_trigger_scsi, S_IRUGO | S_IWUSR, - _ctl_diag_trigger_scsi_show, _ctl_diag_trigger_scsi_store); +static DEVICE_ATTR_RW(diag_trigger_scsi); /** - * _ctl_diag_trigger_scsi_show - show the diag_trigger_mpi attribute + * diag_trigger_mpi_show - show the diag_trigger_mpi attribute * @cdev: pointer to embedded class device * @attr: ? * @buf: the buffer returned @@ -3308,7 +4068,7 @@ static DEVICE_ATTR(diag_trigger_scsi, S_IRUGO | S_IWUSR, * A sysfs 'read/write' shost attribute. */ static ssize_t -_ctl_diag_trigger_mpi_show(struct device *cdev, +diag_trigger_mpi_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); @@ -3324,7 +4084,7 @@ _ctl_diag_trigger_mpi_show(struct device *cdev, } /** - * _ctl_diag_trigger_mpi_store - store the diag_trigger_mpi attribute + * diag_trigger_mpi_store - store the diag_trigger_mpi attribute * @cdev: pointer to embedded class device * @attr: ? * @buf: the buffer returned @@ -3333,16 +4093,35 @@ _ctl_diag_trigger_mpi_show(struct device *cdev, * A sysfs 'read/write' shost attribute. */ static ssize_t -_ctl_diag_trigger_mpi_store(struct device *cdev, +diag_trigger_mpi_store(struct device *cdev, struct device_attribute *attr, const char *buf, size_t count) { struct Scsi_Host *shost = class_to_shost(cdev); struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); + struct SL_WH_MPI_TRIGGERS_T *mpi_tg; unsigned long flags; ssize_t sz; + bool set = 1; - spin_lock_irqsave(&ioc->diag_trigger_lock, flags); sz = min(sizeof(struct SL_WH_MPI_TRIGGERS_T), count); + if (ioc->supports_trigger_pages) { + mpi_tg = kzalloc(sizeof(struct SL_WH_MPI_TRIGGERS_T), + GFP_KERNEL); + if (!mpi_tg) + return -ENOMEM; + + memcpy(mpi_tg, buf, sz); + if (!mpi_tg->ValidEntries) + set = 0; + if (mpt3sas_config_update_driver_trigger_pg4(ioc, mpi_tg, + set)) { + kfree(mpi_tg); + return -EFAULT; + } + kfree(mpi_tg); + } + + spin_lock_irqsave(&ioc->diag_trigger_lock, flags); memset(&ioc->diag_trigger_mpi, 0, sizeof(ioc->diag_trigger_mpi)); memcpy(&ioc->diag_trigger_mpi, buf, sz); @@ -3352,46 +4131,180 @@ _ctl_diag_trigger_mpi_store(struct device *cdev, return sz; } -static DEVICE_ATTR(diag_trigger_mpi, S_IRUGO | S_IWUSR, - _ctl_diag_trigger_mpi_show, _ctl_diag_trigger_mpi_store); +static DEVICE_ATTR_RW(diag_trigger_mpi); /*********** diagnostic trigger suppport *** END ****************************/ /*****************************************/ -struct device_attribute *mpt3sas_host_attrs[] = { - &dev_attr_version_fw, - &dev_attr_version_bios, - &dev_attr_version_mpi, - &dev_attr_version_product, - &dev_attr_version_nvdata_persistent, - &dev_attr_version_nvdata_default, - &dev_attr_board_name, - &dev_attr_board_assembly, - &dev_attr_board_tracer, - &dev_attr_io_delay, - &dev_attr_device_delay, - &dev_attr_logging_level, - &dev_attr_fwfault_debug, - &dev_attr_fw_queue_depth, - &dev_attr_host_sas_address, - &dev_attr_ioc_reset_count, - &dev_attr_host_trace_buffer_size, - &dev_attr_host_trace_buffer, - &dev_attr_host_trace_buffer_enable, - &dev_attr_reply_queue_count, - &dev_attr_diag_trigger_master, - &dev_attr_diag_trigger_event, - &dev_attr_diag_trigger_scsi, - &dev_attr_diag_trigger_mpi, - &dev_attr_BRM_status, +/** + * drv_support_bitmap_show - driver supported feature bitmap + * @cdev: pointer to embedded class device + * @attr: unused + * @buf: the buffer returned + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t +drv_support_bitmap_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); + + return snprintf(buf, PAGE_SIZE, "0x%08x\n", ioc->drv_support_bitmap); +} +static DEVICE_ATTR_RO(drv_support_bitmap); + +/** + * enable_sdev_max_qd_show - display whether sdev max qd is enabled/disabled + * @cdev: pointer to embedded class device + * @attr: unused + * @buf: the buffer returned + * + * A sysfs read/write shost attribute. This attribute is used to set the + * targets queue depth to HBA IO queue depth if this attribute is enabled. + */ +static ssize_t +enable_sdev_max_qd_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); + + return snprintf(buf, PAGE_SIZE, "%d\n", ioc->enable_sdev_max_qd); +} + +/** + * enable_sdev_max_qd_store - Enable/disable sdev max qd + * @cdev: pointer to embedded class device + * @attr: unused + * @buf: the buffer returned + * @count: unused + * + * A sysfs read/write shost attribute. This attribute is used to set the + * targets queue depth to HBA IO queue depth if this attribute is enabled. + * If this attribute is disabled then targets will have corresponding default + * queue depth. + */ +static ssize_t +enable_sdev_max_qd_store(struct device *cdev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); + struct MPT3SAS_DEVICE *sas_device_priv_data; + struct MPT3SAS_TARGET *sas_target_priv_data; + int val = 0; + struct scsi_device *sdev; + struct _raid_device *raid_device; + int qdepth; + + if (kstrtoint(buf, 0, &val) != 0) + return -EINVAL; + + switch (val) { + case 0: + ioc->enable_sdev_max_qd = 0; + shost_for_each_device(sdev, ioc->shost) { + sas_device_priv_data = sdev->hostdata; + if (!sas_device_priv_data) + continue; + sas_target_priv_data = sas_device_priv_data->sas_target; + if (!sas_target_priv_data) + continue; + + if (sas_target_priv_data->flags & + MPT_TARGET_FLAGS_VOLUME) { + raid_device = + mpt3sas_raid_device_find_by_handle(ioc, + sas_target_priv_data->handle); + + switch (raid_device->volume_type) { + case MPI2_RAID_VOL_TYPE_RAID0: + if (raid_device->device_info & + MPI2_SAS_DEVICE_INFO_SSP_TARGET) + qdepth = + MPT3SAS_SAS_QUEUE_DEPTH; + else + qdepth = + MPT3SAS_SATA_QUEUE_DEPTH; + break; + case MPI2_RAID_VOL_TYPE_RAID1E: + case MPI2_RAID_VOL_TYPE_RAID1: + case MPI2_RAID_VOL_TYPE_RAID10: + case MPI2_RAID_VOL_TYPE_UNKNOWN: + default: + qdepth = MPT3SAS_RAID_QUEUE_DEPTH; + } + } else if (sas_target_priv_data->flags & + MPT_TARGET_FLAGS_PCIE_DEVICE) + qdepth = ioc->max_nvme_qd; + else + qdepth = (sas_target_priv_data->sas_dev->port_type > 1) ? + ioc->max_wideport_qd : ioc->max_narrowport_qd; + + mpt3sas_scsih_change_queue_depth(sdev, qdepth); + } + break; + case 1: + ioc->enable_sdev_max_qd = 1; + shost_for_each_device(sdev, ioc->shost) + mpt3sas_scsih_change_queue_depth(sdev, + shost->can_queue); + break; + default: + return -EINVAL; + } + + return strlen(buf); +} +static DEVICE_ATTR_RW(enable_sdev_max_qd); + +static struct attribute *mpt3sas_host_attrs[] = { + &dev_attr_version_fw.attr, + &dev_attr_version_bios.attr, + &dev_attr_version_mpi.attr, + &dev_attr_version_product.attr, + &dev_attr_version_nvdata_persistent.attr, + &dev_attr_version_nvdata_default.attr, + &dev_attr_board_name.attr, + &dev_attr_board_assembly.attr, + &dev_attr_board_tracer.attr, + &dev_attr_io_delay.attr, + &dev_attr_device_delay.attr, + &dev_attr_logging_level.attr, + &dev_attr_fwfault_debug.attr, + &dev_attr_fw_queue_depth.attr, + &dev_attr_host_sas_address.attr, + &dev_attr_ioc_reset_count.attr, + &dev_attr_host_trace_buffer_size.attr, + &dev_attr_host_trace_buffer.attr, + &dev_attr_host_trace_buffer_enable.attr, + &dev_attr_reply_queue_count.attr, + &dev_attr_diag_trigger_master.attr, + &dev_attr_diag_trigger_event.attr, + &dev_attr_diag_trigger_scsi.attr, + &dev_attr_diag_trigger_mpi.attr, + &dev_attr_drv_support_bitmap.attr, + &dev_attr_BRM_status.attr, + &dev_attr_enable_sdev_max_qd.attr, NULL, }; +static const struct attribute_group mpt3sas_host_attr_group = { + .attrs = mpt3sas_host_attrs +}; + +const struct attribute_group *mpt3sas_host_groups[] = { + &mpt3sas_host_attr_group, + NULL +}; + /* device attributes */ /** - * _ctl_device_sas_address_show - sas address + * sas_address_show - sas address * @dev: pointer to embedded class device * @attr: ? * @buf: the buffer returned @@ -3401,7 +4314,7 @@ struct device_attribute *mpt3sas_host_attrs[] = { * A sysfs 'read-only' shost attribute. */ static ssize_t -_ctl_device_sas_address_show(struct device *dev, struct device_attribute *attr, +sas_address_show(struct device *dev, struct device_attribute *attr, char *buf) { struct scsi_device *sdev = to_scsi_device(dev); @@ -3410,10 +4323,10 @@ _ctl_device_sas_address_show(struct device *dev, struct device_attribute *attr, return snprintf(buf, PAGE_SIZE, "0x%016llx\n", (unsigned long long)sas_device_priv_data->sas_target->sas_address); } -static DEVICE_ATTR(sas_address, S_IRUGO, _ctl_device_sas_address_show, NULL); +static DEVICE_ATTR_RO(sas_address); /** - * _ctl_device_handle_show - device handle + * sas_device_handle_show - device handle * @dev: pointer to embedded class device * @attr: ? * @buf: the buffer returned @@ -3423,7 +4336,7 @@ static DEVICE_ATTR(sas_address, S_IRUGO, _ctl_device_sas_address_show, NULL); * A sysfs 'read-only' shost attribute. */ static ssize_t -_ctl_device_handle_show(struct device *dev, struct device_attribute *attr, +sas_device_handle_show(struct device *dev, struct device_attribute *attr, char *buf) { struct scsi_device *sdev = to_scsi_device(dev); @@ -3432,10 +4345,28 @@ _ctl_device_handle_show(struct device *dev, struct device_attribute *attr, return snprintf(buf, PAGE_SIZE, "0x%04x\n", sas_device_priv_data->sas_target->handle); } -static DEVICE_ATTR(sas_device_handle, S_IRUGO, _ctl_device_handle_show, NULL); +static DEVICE_ATTR_RO(sas_device_handle); + +/** + * sas_ncq_prio_supported_show - Indicate if device supports NCQ priority + * @dev: pointer to embedded device + * @attr: sas_ncq_prio_supported attribute descriptor + * @buf: the buffer returned + * + * A sysfs 'read-only' sdev attribute, only works with SATA + */ +static ssize_t +sas_ncq_prio_supported_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct scsi_device *sdev = to_scsi_device(dev); + + return sysfs_emit(buf, "%d\n", sas_ata_ncq_prio_supported(sdev)); +} +static DEVICE_ATTR_RO(sas_ncq_prio_supported); /** - * _ctl_device_ncq_io_prio_show - send prioritized io commands to device + * sas_ncq_prio_enable_show - send prioritized io commands to device * @dev: pointer to embedded device * @attr: ? * @buf: the buffer returned @@ -3443,7 +4374,7 @@ static DEVICE_ATTR(sas_device_handle, S_IRUGO, _ctl_device_handle_show, NULL); * A sysfs 'read/write' sdev attribute, only works with SATA */ static ssize_t -_ctl_device_ncq_prio_enable_show(struct device *dev, +sas_ncq_prio_enable_show(struct device *dev, struct device_attribute *attr, char *buf) { struct scsi_device *sdev = to_scsi_device(dev); @@ -3454,7 +4385,7 @@ _ctl_device_ncq_prio_enable_show(struct device *dev, } static ssize_t -_ctl_device_ncq_prio_enable_store(struct device *dev, +sas_ncq_prio_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -3465,23 +4396,31 @@ _ctl_device_ncq_prio_enable_store(struct device *dev, if (kstrtobool(buf, &ncq_prio_enable)) return -EINVAL; - if (!scsih_ncq_prio_supp(sdev)) + if (!sas_ata_ncq_prio_supported(sdev)) return -EINVAL; sas_device_priv_data->ncq_prio_enable = ncq_prio_enable; return strlen(buf); } -static DEVICE_ATTR(sas_ncq_prio_enable, S_IRUGO | S_IWUSR, - _ctl_device_ncq_prio_enable_show, - _ctl_device_ncq_prio_enable_store); +static DEVICE_ATTR_RW(sas_ncq_prio_enable); -struct device_attribute *mpt3sas_dev_attrs[] = { - &dev_attr_sas_address, - &dev_attr_sas_device_handle, - &dev_attr_sas_ncq_prio_enable, +static struct attribute *mpt3sas_dev_attrs[] = { + &dev_attr_sas_address.attr, + &dev_attr_sas_device_handle.attr, + &dev_attr_sas_ncq_prio_supported.attr, + &dev_attr_sas_ncq_prio_enable.attr, NULL, }; +static const struct attribute_group mpt3sas_dev_attr_group = { + .attrs = mpt3sas_dev_attrs +}; + +const struct attribute_group *mpt3sas_dev_groups[] = { + &mpt3sas_dev_attr_group, + NULL +}; + /* file operations table for mpt3ctl device */ static const struct file_operations ctl_fops = { .owner = THIS_MODULE, @@ -3545,37 +4484,37 @@ mpt3sas_ctl_init(ushort hbas_to_enumerate) } /** - * mpt3sas_ctl_exit - exit point for ctl - * @hbas_to_enumerate: ? + * mpt3sas_ctl_release - release dma for ctl + * @ioc: per adapter object */ void -mpt3sas_ctl_exit(ushort hbas_to_enumerate) +mpt3sas_ctl_release(struct MPT3SAS_ADAPTER *ioc) { - struct MPT3SAS_ADAPTER *ioc; int i; - list_for_each_entry(ioc, &mpt3sas_ioc_list, list) { + /* free memory associated to diag buffers */ + for (i = 0; i < MPI2_DIAG_BUF_TYPE_COUNT; i++) { + if (!ioc->diag_buffer[i]) + continue; + dma_free_coherent(&ioc->pdev->dev, + ioc->diag_buffer_sz[i], + ioc->diag_buffer[i], + ioc->diag_buffer_dma[i]); + ioc->diag_buffer[i] = NULL; + ioc->diag_buffer_status[i] = 0; + } - /* free memory associated to diag buffers */ - for (i = 0; i < MPI2_DIAG_BUF_TYPE_COUNT; i++) { - if (!ioc->diag_buffer[i]) - continue; - if (!(ioc->diag_buffer_status[i] & - MPT3_DIAG_BUFFER_IS_REGISTERED)) - continue; - if ((ioc->diag_buffer_status[i] & - MPT3_DIAG_BUFFER_IS_RELEASED)) - continue; - dma_free_coherent(&ioc->pdev->dev, - ioc->diag_buffer_sz[i], - ioc->diag_buffer[i], - ioc->diag_buffer_dma[i]); - ioc->diag_buffer[i] = NULL; - ioc->diag_buffer_status[i] = 0; - } + kfree(ioc->event_log); +} + +/** + * mpt3sas_ctl_exit - exit point for ctl + * @hbas_to_enumerate: ? + */ +void +mpt3sas_ctl_exit(ushort hbas_to_enumerate) +{ - kfree(ioc->event_log); - } if (hbas_to_enumerate != 1) misc_deregister(&ctl_dev); if (hbas_to_enumerate != 2) diff --git a/drivers/scsi/mpt3sas/mpt3sas_ctl.h b/drivers/scsi/mpt3sas/mpt3sas_ctl.h index 18b46faef6f1..483e0549c02f 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_ctl.h +++ b/drivers/scsi/mpt3sas/mpt3sas_ctl.h @@ -50,6 +50,8 @@ #include <linux/miscdevice.h> #endif +#include "mpt3sas_base.h" + #ifndef MPT2SAS_MINOR #define MPT2SAS_MINOR (MPT_MINOR + 1) #endif @@ -94,6 +96,18 @@ struct mpt3_diag_query) #define MPT3DIAGREADBUFFER _IOWR(MPT3_MAGIC_NUMBER, 30, \ struct mpt3_diag_read_buffer) +#define MPT3ADDNLDIAGQUERY _IOWR(MPT3_MAGIC_NUMBER, 32, \ + struct mpt3_addnl_diag_query) +#define MPT3ENABLEDIAGSBRRELOAD _IOWR(MPT3_MAGIC_NUMBER, 33, \ + struct mpt3_enable_diag_sbr_reload) + +/* Trace Buffer default UniqueId */ +#define MPT2DIAGBUFFUNIQUEID (0x07075900) +#define MPT3DIAGBUFFUNIQUEID (0x4252434D) + +/* UID not found */ +#define MPT3_DIAG_UID_NOT_FOUND (0xFF) + /** * struct mpt3_ioctl_header - main header structure @@ -146,6 +160,9 @@ struct mpt3_ioctl_pci_info { #define MPT3_IOCTL_INTERFACE_SAS35 (0x07) #define MPT2_IOCTL_VERSION_LENGTH (32) +/* Bits set for mpt3_ioctl_iocinfo.driver_cap */ +#define MPT3_IOCTL_IOCINFO_DRIVER_CAP_MCTP_PASSTHRU 0x1 + /** * struct mpt3_ioctl_iocinfo - generic controller info * @hdr - generic header @@ -161,6 +178,7 @@ struct mpt3_ioctl_pci_info { * @driver_version - driver version - 32 ASCII characters * @rsvd1 - reserved * @scsi_id - scsi id of adapter 0 + * @driver_capability - driver capabilities * @rsvd2 - reserved * @pci_information - pci info (2nd revision) */ @@ -178,7 +196,8 @@ struct mpt3_ioctl_iocinfo { uint8_t driver_version[MPT2_IOCTL_VERSION_LENGTH]; uint8_t rsvd1; uint8_t scsi_id; - uint16_t rsvd2; + uint8_t driver_capability; + uint8_t rsvd2; struct mpt3_ioctl_pci_info pci_information; }; @@ -310,6 +329,7 @@ struct mpt3_ioctl_btdh_mapping { #define MPT3_APP_FLAGS_APP_OWNED (0x0001) #define MPT3_APP_FLAGS_BUFFER_VALID (0x0002) #define MPT3_APP_FLAGS_FW_BUFFER_ACCESS (0x0004) +#define MPT3_APP_FLAGS_DYNAMIC_BUFFER_ALLOC (0x0008) /* flags for mpt3_diag_read_buffer */ #define MPT3_FLAGS_REREGISTER (0x0001) @@ -421,4 +441,68 @@ struct mpt3_diag_read_buffer { uint32_t diagnostic_data[1]; }; +/** + * struct mpt3_addnl_diag_query - diagnostic buffer release reason + * @hdr - generic header + * @unique_id - unique id associated with this buffer. + * @rel_query - release query. + * @reserved2 + */ +struct mpt3_addnl_diag_query { + struct mpt3_ioctl_header hdr; + uint32_t unique_id; + struct htb_rel_query rel_query; + uint32_t reserved2[2]; +}; + +/** + * struct mpt3_enable_diag_sbr_reload - enable sbr reload + * @hdr - generic header + */ +struct mpt3_enable_diag_sbr_reload { + struct mpt3_ioctl_header hdr; +}; + +/** + * struct mpt3_passthru_command - generic mpt firmware passthru command + * @dev_index - device index + * @timeout - command timeout in seconds. (if zero then use driver default + * value). + * @reply_frame_buf_ptr - MPI reply location + * @data_in_buf_ptr - destination for read + * @data_out_buf_ptr - data source for write + * @max_reply_bytes - maximum number of reply bytes to be sent to app. + * @data_in_size - number bytes for data transfer in (read) + * @data_out_size - number bytes for data transfer out (write) + * @mpi_request - request frame + */ +struct mpt3_passthru_command { + u8 dev_index; + uint32_t timeout; + void *reply_frame_buf_ptr; + void *data_in_buf_ptr; + void *data_out_buf_ptr; + uint32_t max_reply_bytes; + uint32_t data_in_size; + uint32_t data_out_size; + Mpi26MctpPassthroughRequest_t *mpi_request; +}; + +/* + * mpt3sas_get_device_count - Retrieve the count of MCTP passthrough + * capable devices managed by the driver. + * + * Returns number of devices that support MCTP passthrough. + */ +int mpt3sas_get_device_count(void); + +/* + * mpt3sas_send_passthru_cmd - Send an MPI MCTP passthrough command to + * firmware + * @command: The MPI MCTP passthrough command to send to firmware + * + * Returns 0 on success, anything else is error . + */ +int mpt3sas_send_mctp_passthru_req(struct mpt3_passthru_command *command); + #endif /* MPT3SAS_CTL_H_INCLUDED */ diff --git a/drivers/scsi/mpt3sas/mpt3sas_debugfs.c b/drivers/scsi/mpt3sas/mpt3sas_debugfs.c new file mode 100644 index 000000000000..a6ab1db81167 --- /dev/null +++ b/drivers/scsi/mpt3sas/mpt3sas_debugfs.c @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Debugfs interface Support for MPT (Message Passing Technology) based + * controllers. + * + * Copyright (C) 2020 Broadcom Inc. + * + * Authors: Broadcom Inc. + * Sreekanth Reddy <sreekanth.reddy@broadcom.com> + * Suganath Prabu <suganath-prabu.subramani@broadcom.com> + * + * Send feedback to : MPT-FusionLinux.pdl@broadcom.com) + * + **/ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/interrupt.h> +#include <linux/compat.h> +#include <linux/uio.h> + +#include <scsi/scsi.h> +#include <scsi/scsi_device.h> +#include <scsi/scsi_host.h> +#include "mpt3sas_base.h" +#include <linux/debugfs.h> + +static struct dentry *mpt3sas_debugfs_root; + +/* + * _debugfs_iocdump_read - copy ioc dump from debugfs buffer + * @filep: File Pointer + * @ubuf: Buffer to fill data + * @cnt: Length of the buffer + * @ppos: Offset in the file + */ + +static ssize_t +_debugfs_iocdump_read(struct file *filp, char __user *ubuf, size_t cnt, + loff_t *ppos) + +{ + struct mpt3sas_debugfs_buffer *debug = filp->private_data; + + if (!debug || !debug->buf) + return 0; + + return simple_read_from_buffer(ubuf, cnt, ppos, debug->buf, debug->len); +} + +/* + * _debugfs_iocdump_open : open the ioc_dump debugfs attribute file + */ +static int +_debugfs_iocdump_open(struct inode *inode, struct file *file) +{ + struct MPT3SAS_ADAPTER *ioc = inode->i_private; + struct mpt3sas_debugfs_buffer *debug; + + debug = kzalloc(sizeof(struct mpt3sas_debugfs_buffer), GFP_KERNEL); + if (!debug) + return -ENOMEM; + + debug->buf = (void *)ioc; + debug->len = sizeof(struct MPT3SAS_ADAPTER); + file->private_data = debug; + return 0; +} + +/* + * _debugfs_iocdump_release : release the ioc_dump debugfs attribute + * @inode: inode structure to the corresponds device + * @file: File pointer + */ +static int +_debugfs_iocdump_release(struct inode *inode, struct file *file) +{ + struct mpt3sas_debugfs_buffer *debug = file->private_data; + + if (!debug) + return 0; + + file->private_data = NULL; + kfree(debug); + return 0; +} + +static const struct file_operations mpt3sas_debugfs_iocdump_fops = { + .owner = THIS_MODULE, + .open = _debugfs_iocdump_open, + .read = _debugfs_iocdump_read, + .release = _debugfs_iocdump_release, +}; + +/* + * mpt3sas_init_debugfs : Create debugfs root for mpt3sas driver + */ +void mpt3sas_init_debugfs(void) +{ + mpt3sas_debugfs_root = debugfs_create_dir("mpt3sas", NULL); + if (!mpt3sas_debugfs_root) + pr_info("mpt3sas: Cannot create debugfs root\n"); +} + +/* + * mpt3sas_exit_debugfs : Remove debugfs root for mpt3sas driver + */ +void mpt3sas_exit_debugfs(void) +{ + debugfs_remove_recursive(mpt3sas_debugfs_root); +} + +/* + * mpt3sas_setup_debugfs : Setup debugfs per HBA adapter + * ioc: MPT3SAS_ADAPTER object + */ +void +mpt3sas_setup_debugfs(struct MPT3SAS_ADAPTER *ioc) +{ + char name[64]; + + snprintf(name, sizeof(name), "scsi_host%d", ioc->shost->host_no); + if (!ioc->debugfs_root) { + ioc->debugfs_root = + debugfs_create_dir(name, mpt3sas_debugfs_root); + if (!ioc->debugfs_root) { + dev_err(&ioc->pdev->dev, + "Cannot create per adapter debugfs directory\n"); + return; + } + } + + snprintf(name, sizeof(name), "ioc_dump"); + ioc->ioc_dump = debugfs_create_file(name, 0444, + ioc->debugfs_root, ioc, &mpt3sas_debugfs_iocdump_fops); + if (!ioc->ioc_dump) { + dev_err(&ioc->pdev->dev, + "Cannot create ioc_dump debugfs file\n"); + debugfs_remove(ioc->debugfs_root); + return; + } + + snprintf(name, sizeof(name), "host_recovery"); + debugfs_create_u8(name, 0444, ioc->debugfs_root, &ioc->shost_recovery); + +} + +/* + * mpt3sas_destroy_debugfs : Destroy debugfs per HBA adapter + * @ioc: MPT3SAS_ADAPTER object + */ +void mpt3sas_destroy_debugfs(struct MPT3SAS_ADAPTER *ioc) +{ + debugfs_remove_recursive(ioc->debugfs_root); +} + diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c index 6be39dc27103..7092d0debef3 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -51,11 +51,9 @@ #include <linux/workqueue.h> #include <linux/delay.h> #include <linux/pci.h> -#include <linux/pci-aspm.h> #include <linux/interrupt.h> -#include <linux/aer.h> #include <linux/raid_class.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include "mpt3sas_base.h" @@ -78,6 +76,7 @@ static void _scsih_pcie_device_remove_from_sml(struct MPT3SAS_ADAPTER *ioc, static void _scsih_pcie_check_device(struct MPT3SAS_ADAPTER *ioc, u16 handle); static u8 _scsih_check_for_pending_tm(struct MPT3SAS_ADAPTER *ioc, u16 smid); +static void _scsih_complete_devices_scanning(struct MPT3SAS_ADAPTER *ioc); /* global parameters */ LIST_HEAD(mpt3sas_ioc_list); @@ -113,22 +112,22 @@ MODULE_PARM_DESC(logging_level, static ushort max_sectors = 0xFFFF; -module_param(max_sectors, ushort, 0); +module_param(max_sectors, ushort, 0444); MODULE_PARM_DESC(max_sectors, "max sectors, range 64 to 32767 default=32767"); static int missing_delay[2] = {-1, -1}; -module_param_array(missing_delay, int, NULL, 0); +module_param_array(missing_delay, int, NULL, 0444); MODULE_PARM_DESC(missing_delay, " device missing delay , io missing delay"); /* scsi-mid layer global parmeter is max_report_luns, which is 511 */ #define MPT3SAS_MAX_LUN (16895) static u64 max_lun = MPT3SAS_MAX_LUN; -module_param(max_lun, ullong, 0); +module_param(max_lun, ullong, 0444); MODULE_PARM_DESC(max_lun, " max lun, default=16895 "); static ushort hbas_to_enumerate; -module_param(hbas_to_enumerate, ushort, 0); +module_param(hbas_to_enumerate, ushort, 0444); MODULE_PARM_DESC(hbas_to_enumerate, " 0 - enumerates both SAS 2.0 & SAS 3.0 generation HBAs\n \ 1 - enumerates only SAS 2.0 generation HBAs\n \ @@ -142,19 +141,37 @@ MODULE_PARM_DESC(hbas_to_enumerate, * Either bit can be set, or both */ static int diag_buffer_enable = -1; -module_param(diag_buffer_enable, int, 0); +module_param(diag_buffer_enable, int, 0444); MODULE_PARM_DESC(diag_buffer_enable, " post diag buffers (TRACE=1/SNAPSHOT=2/EXTENDED=4/default=0)"); static int disable_discovery = -1; -module_param(disable_discovery, int, 0); +module_param(disable_discovery, int, 0444); MODULE_PARM_DESC(disable_discovery, " disable discovery "); /* permit overriding the host protection capabilities mask (EEDP/T10 PI) */ static int prot_mask = -1; -module_param(prot_mask, int, 0); +module_param(prot_mask, int, 0444); MODULE_PARM_DESC(prot_mask, " host protection capabilities mask, def=7 "); +static bool enable_sdev_max_qd; +module_param(enable_sdev_max_qd, bool, 0444); +MODULE_PARM_DESC(enable_sdev_max_qd, + "Enable sdev max qd as can_queue, def=disabled(0)"); + +static int multipath_on_hba = -1; +module_param(multipath_on_hba, int, 0); +MODULE_PARM_DESC(multipath_on_hba, + "Multipath support to add same target device\n\t\t" + "as many times as it is visible to HBA from various paths\n\t\t" + "(by default:\n\t\t" + "\t SAS 2.0 & SAS 3.0 HBA - This will be disabled,\n\t\t" + "\t SAS 3.5 HBA - This will be enabled)"); + +static int host_tagset_enable = 1; +module_param(host_tagset_enable, int, 0444); +MODULE_PARM_DESC(host_tagset_enable, + "Shared host tagset enable/disable Default: enable(1)"); /* raid transport support */ static struct raid_template *mpt3sas_raid_template; @@ -178,6 +195,14 @@ struct sense_info { #define MPT3SAS_PORT_ENABLE_COMPLETE (0xFFFD) #define MPT3SAS_ABRT_TASK_SET (0xFFFE) #define MPT3SAS_REMOVE_UNRESPONDING_DEVICES (0xFFFF) + +/* + * SAS Log info code for a NCQ collateral abort after an NCQ error: + * IOC_LOGINFO_PREFIX_PL | PL_LOGINFO_CODE_SATA_NCQ_FAIL_ALL_CMDS_AFTR_ERR + * See: drivers/message/fusion/lsi/mpi_log_sas.h + */ +#define IOC_LOGINFO_SATA_NCQ_FAIL_AFTER_ERR 0x31080000 + /** * struct fw_event_work - firmware event struct * @list: link list framework @@ -204,7 +229,7 @@ struct fw_event_work { u8 ignore; u16 event; struct kref refcount; - char event_data[0] __aligned(4); + char event_data[] __aligned(4); }; static void fw_event_work_free(struct kref *r) @@ -284,8 +309,8 @@ struct _scsi_io_transfer { /** * _scsih_set_debug_level - global setting of ioc->logging_level. - * @val: ? - * @kp: ? + * @val: value of the parameter to be set + * @kp: pointer to kernel_param structure * * Note: The logging levels are defined in mpt3sas_debug.h. */ @@ -354,6 +379,87 @@ _scsih_srch_boot_encl_slot(u64 enclosure_logical_id, u16 slot_number, } /** + * mpt3sas_get_port_by_id - get hba port entry corresponding to provided + * port number from port list + * @ioc: per adapter object + * @port_id: port number + * @bypass_dirty_port_flag: when set look the matching hba port entry even + * if hba port entry is marked as dirty. + * + * Search for hba port entry corresponding to provided port number, + * if available return port object otherwise return NULL. + */ +struct hba_port * +mpt3sas_get_port_by_id(struct MPT3SAS_ADAPTER *ioc, + u8 port_id, u8 bypass_dirty_port_flag) +{ + struct hba_port *port, *port_next; + + /* + * When multipath_on_hba is disabled then + * search the hba_port entry using default + * port id i.e. 255 + */ + if (!ioc->multipath_on_hba) + port_id = MULTIPATH_DISABLED_PORT_ID; + + list_for_each_entry_safe(port, port_next, + &ioc->port_table_list, list) { + if (port->port_id != port_id) + continue; + if (bypass_dirty_port_flag) + return port; + if (port->flags & HBA_PORT_FLAG_DIRTY_PORT) + continue; + return port; + } + + /* + * Allocate hba_port object for default port id (i.e. 255) + * when multipath_on_hba is disabled for the HBA. + * And add this object to port_table_list. + */ + if (!ioc->multipath_on_hba) { + port = kzalloc(sizeof(struct hba_port), GFP_ATOMIC); + if (!port) + return NULL; + + port->port_id = port_id; + ioc_info(ioc, + "hba_port entry: %p, port: %d is added to hba_port list\n", + port, port->port_id); + list_add_tail(&port->list, + &ioc->port_table_list); + return port; + } + return NULL; +} + +/** + * mpt3sas_get_vphy_by_phy - get virtual_phy object corresponding to phy number + * @ioc: per adapter object + * @port: hba_port object + * @phy: phy number + * + * Return virtual_phy object corresponding to phy number. + */ +struct virtual_phy * +mpt3sas_get_vphy_by_phy(struct MPT3SAS_ADAPTER *ioc, + struct hba_port *port, u32 phy) +{ + struct virtual_phy *vphy, *vphy_next; + + if (!port->vphys_mask) + return NULL; + + list_for_each_entry_safe(vphy, vphy_next, &port->vphys_list, list) { + if (vphy->phy_mask & (1 << phy)) + return vphy; + } + return NULL; +} + +/** * _scsih_is_boot_device - search for matching boot device. * @sas_address: sas address * @device_name: device name specified in INDENTIFY fram @@ -611,48 +717,106 @@ mpt3sas_get_pdev_from_target(struct MPT3SAS_ADAPTER *ioc, return ret; } + +/** + * __mpt3sas_get_sdev_by_rphy - sas device search + * @ioc: per adapter object + * @rphy: sas_rphy pointer + * + * Context: This function will acquire ioc->sas_device_lock and will release + * before returning the sas_device object. + * + * This searches for sas_device from rphy object + * then return sas_device object. + */ struct _sas_device * -__mpt3sas_get_sdev_by_addr(struct MPT3SAS_ADAPTER *ioc, - u64 sas_address) +__mpt3sas_get_sdev_by_rphy(struct MPT3SAS_ADAPTER *ioc, + struct sas_rphy *rphy) { struct _sas_device *sas_device; assert_spin_locked(&ioc->sas_device_lock); - list_for_each_entry(sas_device, &ioc->sas_device_list, list) - if (sas_device->sas_address == sas_address) - goto found_device; + list_for_each_entry(sas_device, &ioc->sas_device_list, list) { + if (sas_device->rphy != rphy) + continue; + sas_device_get(sas_device); + return sas_device; + } - list_for_each_entry(sas_device, &ioc->sas_device_init_list, list) - if (sas_device->sas_address == sas_address) - goto found_device; + sas_device = NULL; + list_for_each_entry(sas_device, &ioc->sas_device_init_list, list) { + if (sas_device->rphy != rphy) + continue; + sas_device_get(sas_device); + return sas_device; + } return NULL; +} -found_device: - sas_device_get(sas_device); - return sas_device; +/** + * __mpt3sas_get_sdev_by_addr - get _sas_device object corresponding to provided + * sas address from sas_device_list list + * @ioc: per adapter object + * @sas_address: device sas address + * @port: port number + * + * Search for _sas_device object corresponding to provided sas address, + * if available return _sas_device object address otherwise return NULL. + */ +struct _sas_device * +__mpt3sas_get_sdev_by_addr(struct MPT3SAS_ADAPTER *ioc, + u64 sas_address, struct hba_port *port) +{ + struct _sas_device *sas_device; + + if (!port) + return NULL; + + assert_spin_locked(&ioc->sas_device_lock); + + list_for_each_entry(sas_device, &ioc->sas_device_list, list) { + if (sas_device->sas_address != sas_address) + continue; + if (sas_device->port != port) + continue; + sas_device_get(sas_device); + return sas_device; + } + + list_for_each_entry(sas_device, &ioc->sas_device_init_list, list) { + if (sas_device->sas_address != sas_address) + continue; + if (sas_device->port != port) + continue; + sas_device_get(sas_device); + return sas_device; + } + + return NULL; } /** * mpt3sas_get_sdev_by_addr - sas device search * @ioc: per adapter object * @sas_address: sas address + * @port: hba port entry * Context: Calling function should acquire ioc->sas_device_lock * - * This searches for sas_device based on sas_address, then return sas_device - * object. + * This searches for sas_device based on sas_address & port number, + * then return sas_device object. */ struct _sas_device * mpt3sas_get_sdev_by_addr(struct MPT3SAS_ADAPTER *ioc, - u64 sas_address) + u64 sas_address, struct hba_port *port) { struct _sas_device *sas_device; unsigned long flags; spin_lock_irqsave(&ioc->sas_device_lock, flags); sas_device = __mpt3sas_get_sdev_by_addr(ioc, - sas_address); + sas_address, port); spin_unlock_irqrestore(&ioc->sas_device_lock, flags); return sas_device; @@ -821,13 +985,17 @@ _scsih_device_remove_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle) } /** - * mpt3sas_device_remove_by_sas_address - removing device object by sas address + * mpt3sas_device_remove_by_sas_address - removing device object by + * sas address & port number * @ioc: per adapter object * @sas_address: device sas_address + * @port: hba port entry + * + * Return nothing. */ void mpt3sas_device_remove_by_sas_address(struct MPT3SAS_ADAPTER *ioc, - u64 sas_address) + u64 sas_address, struct hba_port *port) { struct _sas_device *sas_device; unsigned long flags; @@ -836,7 +1004,7 @@ mpt3sas_device_remove_by_sas_address(struct MPT3SAS_ADAPTER *ioc, return; spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = __mpt3sas_get_sdev_by_addr(ioc, sas_address); + sas_device = __mpt3sas_get_sdev_by_addr(ioc, sas_address, port); if (sas_device) { list_del_init(&sas_device->list); sas_device_put(sas_device); @@ -881,7 +1049,7 @@ _scsih_sas_device_add(struct MPT3SAS_ADAPTER *ioc, } if (!mpt3sas_transport_port_add(ioc, sas_device->handle, - sas_device->sas_address_parent)) { + sas_device->sas_address_parent, sas_device->port)) { _scsih_sas_device_remove(ioc, sas_device); } else if (!sas_device->starget) { /* @@ -892,7 +1060,8 @@ _scsih_sas_device_add(struct MPT3SAS_ADAPTER *ioc, if (!ioc->is_driver_loading) { mpt3sas_transport_port_remove(ioc, sas_device->sas_address, - sas_device->sas_address_parent); + sas_device->sas_address_parent, + sas_device->port); _scsih_sas_device_remove(ioc, sas_device); } } else @@ -1047,6 +1216,34 @@ mpt3sas_get_pdev_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle) } /** + * _scsih_set_nvme_max_shutdown_latency - Update max_shutdown_latency. + * @ioc: per adapter object + * Context: This function will acquire ioc->pcie_device_lock + * + * Update ioc->max_shutdown_latency to that NVMe drives RTD3 Entry Latency + * which has reported maximum among all available NVMe drives. + * Minimum max_shutdown_latency will be six seconds. + */ +static void +_scsih_set_nvme_max_shutdown_latency(struct MPT3SAS_ADAPTER *ioc) +{ + struct _pcie_device *pcie_device; + unsigned long flags; + u16 shutdown_latency = IO_UNIT_CONTROL_SHUTDOWN_TIMEOUT; + + spin_lock_irqsave(&ioc->pcie_device_lock, flags); + list_for_each_entry(pcie_device, &ioc->pcie_device_list, list) { + if (pcie_device->shutdown_latency) { + if (shutdown_latency < pcie_device->shutdown_latency) + shutdown_latency = + pcie_device->shutdown_latency; + } + } + ioc->max_shutdown_latency = shutdown_latency; + spin_unlock_irqrestore(&ioc->pcie_device_lock, flags); +} + +/** * _scsih_pcie_device_remove - remove pcie_device from list. * @ioc: per adapter object * @pcie_device: the pcie_device object @@ -1060,6 +1257,7 @@ _scsih_pcie_device_remove(struct MPT3SAS_ADAPTER *ioc, { unsigned long flags; int was_on_pcie_device_list = 0; + u8 update_latency = 0; if (!pcie_device) return; @@ -1079,11 +1277,21 @@ _scsih_pcie_device_remove(struct MPT3SAS_ADAPTER *ioc, list_del_init(&pcie_device->list); was_on_pcie_device_list = 1; } + if (pcie_device->shutdown_latency == ioc->max_shutdown_latency) + update_latency = 1; spin_unlock_irqrestore(&ioc->pcie_device_lock, flags); if (was_on_pcie_device_list) { kfree(pcie_device->serial_number); pcie_device_put(pcie_device); } + + /* + * This device's RTD3 Entry Latency matches IOC's + * max_shutdown_latency. Recalculate IOC's max_shutdown_latency + * from the available drives as current drive is getting removed. + */ + if (update_latency) + _scsih_set_nvme_max_shutdown_latency(ioc); } @@ -1098,6 +1306,7 @@ _scsih_pcie_device_remove_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle) struct _pcie_device *pcie_device; unsigned long flags; int was_on_pcie_device_list = 0; + u8 update_latency = 0; if (ioc->shost_recovery) return; @@ -1110,12 +1319,22 @@ _scsih_pcie_device_remove_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle) was_on_pcie_device_list = 1; pcie_device_put(pcie_device); } + if (pcie_device->shutdown_latency == ioc->max_shutdown_latency) + update_latency = 1; } spin_unlock_irqrestore(&ioc->pcie_device_lock, flags); if (was_on_pcie_device_list) { _scsih_pcie_device_remove_from_sml(ioc, pcie_device); pcie_device_put(pcie_device); } + + /* + * This device's RTD3 Entry Latency matches IOC's + * max_shutdown_latency. Recalculate IOC's max_shutdown_latency + * from the available drives as current drive is getting removed. + */ + if (update_latency) + _scsih_set_nvme_max_shutdown_latency(ioc); } /** @@ -1152,6 +1371,11 @@ _scsih_pcie_device_add(struct MPT3SAS_ADAPTER *ioc, list_add_tail(&pcie_device->list, &ioc->pcie_device_list); spin_unlock_irqrestore(&ioc->pcie_device_lock, flags); + if (pcie_device->access_status == + MPI26_PCIEDEV0_ASTATUS_DEVICE_BLOCKED) { + clear_bit(pcie_device->handle, ioc->pend_os_device_add); + return; + } if (scsi_add_device(ioc->shost, PCIE_CHANNEL, pcie_device->id, 0)) { _scsih_pcie_device_remove(ioc, pcie_device); } else if (!pcie_device->starget) { @@ -1196,7 +1420,9 @@ _scsih_pcie_device_init_add(struct MPT3SAS_ADAPTER *ioc, spin_lock_irqsave(&ioc->pcie_device_lock, flags); pcie_device_get(pcie_device); list_add_tail(&pcie_device->list, &ioc->pcie_device_init_list); - _scsih_determine_boot_device(ioc, pcie_device, PCIE_CHANNEL); + if (pcie_device->access_status != + MPI26_PCIEDEV0_ASTATUS_DEVICE_BLOCKED) + _scsih_determine_boot_device(ioc, pcie_device, PCIE_CHANNEL); spin_unlock_irqrestore(&ioc->pcie_device_lock, flags); } /** @@ -1372,21 +1598,26 @@ out: * mpt3sas_scsih_expander_find_by_sas_address - expander device search * @ioc: per adapter object * @sas_address: sas address + * @port: hba port entry * Context: Calling function should acquire ioc->sas_node_lock. * - * This searches for expander device based on sas_address, then returns the - * sas_node object. + * This searches for expander device based on sas_address & port number, + * then returns the sas_node object. */ struct _sas_node * mpt3sas_scsih_expander_find_by_sas_address(struct MPT3SAS_ADAPTER *ioc, - u64 sas_address) + u64 sas_address, struct hba_port *port) { - struct _sas_node *sas_expander, *r; + struct _sas_node *sas_expander, *r = NULL; + + if (!port) + return r; - r = NULL; list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) { if (sas_expander->sas_address != sas_address) continue; + if (sas_expander->port != port) + continue; r = sas_expander; goto out; } @@ -1433,23 +1664,86 @@ _scsih_is_end_device(u32 device_info) } /** - * _scsih_is_nvme_device - determines if device is an nvme device + * _scsih_is_nvme_pciescsi_device - determines if + * device is an pcie nvme/scsi device * @device_info: bitfield providing information about the device. * Context: none * - * Return: 1 if nvme device. + * Returns 1 if device is pcie device type nvme/scsi. */ static int -_scsih_is_nvme_device(u32 device_info) +_scsih_is_nvme_pciescsi_device(u32 device_info) { - if ((device_info & MPI26_PCIE_DEVINFO_MASK_DEVICE_TYPE) - == MPI26_PCIE_DEVINFO_NVME) + if (((device_info & MPI26_PCIE_DEVINFO_MASK_DEVICE_TYPE) + == MPI26_PCIE_DEVINFO_NVME) || + ((device_info & MPI26_PCIE_DEVINFO_MASK_DEVICE_TYPE) + == MPI26_PCIE_DEVINFO_SCSI)) return 1; else return 0; } /** + * _scsih_scsi_lookup_find_by_target - search for matching channel:id + * @ioc: per adapter object + * @id: target id + * @channel: channel + * Context: This function will acquire ioc->scsi_lookup_lock. + * + * This will search for a matching channel:id in the scsi_lookup array, + * returning 1 if found. + */ +static u8 +_scsih_scsi_lookup_find_by_target(struct MPT3SAS_ADAPTER *ioc, int id, + int channel) +{ + int smid; + struct scsi_cmnd *scmd; + + for (smid = 1; + smid <= ioc->shost->can_queue; smid++) { + scmd = mpt3sas_scsih_scsi_lookup_get(ioc, smid); + if (!scmd) + continue; + if (scmd->device->id == id && + scmd->device->channel == channel) + return 1; + } + return 0; +} + +/** + * _scsih_scsi_lookup_find_by_lun - search for matching channel:id:lun + * @ioc: per adapter object + * @id: target id + * @lun: lun number + * @channel: channel + * Context: This function will acquire ioc->scsi_lookup_lock. + * + * This will search for a matching channel:id:lun in the scsi_lookup array, + * returning 1 if found. + */ +static u8 +_scsih_scsi_lookup_find_by_lun(struct MPT3SAS_ADAPTER *ioc, int id, + unsigned int lun, int channel) +{ + int smid; + struct scsi_cmnd *scmd; + + for (smid = 1; smid <= ioc->shost->can_queue; smid++) { + + scmd = mpt3sas_scsih_scsi_lookup_get(ioc, smid); + if (!scmd) + continue; + if (scmd->device->id == id && + scmd->device->channel == channel && + scmd->device->lun == lun) + return 1; + } + return 0; +} + +/** * mpt3sas_scsih_scsi_lookup_get - returns scmd entry * @ioc: per adapter object * @smid: system request message index @@ -1462,10 +1756,24 @@ mpt3sas_scsih_scsi_lookup_get(struct MPT3SAS_ADAPTER *ioc, u16 smid) { struct scsi_cmnd *scmd = NULL; struct scsiio_tracker *st; + Mpi25SCSIIORequest_t *mpi_request; + u16 tag = smid - 1; if (smid > 0 && smid <= ioc->scsiio_depth - INTERNAL_SCSIIO_CMDS_COUNT) { - u32 unique_tag = smid - 1; + u32 unique_tag = + ioc->io_queue_num[tag] << BLK_MQ_UNIQUE_TAG_BITS | tag; + + mpi_request = mpt3sas_base_get_msg_frame(ioc, smid); + + /* + * If SCSI IO request is outstanding at driver level then + * DevHandle filed must be non-zero. If DevHandle is zero + * then it means that this smid is free at driver level, + * so return NULL. + */ + if (!mpi_request->DevHandle) + return scmd; scmd = scsi_host_find_tag(ioc->shost, unique_tag); if (scmd) { @@ -1497,7 +1805,13 @@ scsih_change_queue_depth(struct scsi_device *sdev, int qdepth) max_depth = shost->can_queue; - /* limit max device queue for SATA to 32 */ + /* + * limit max device queue for SATA to 32 if enable_sdev_max_qd + * is disabled. + */ + if (ioc->enable_sdev_max_qd || ioc->is_gen35_ioc) + goto not_sata; + sas_device_priv_data = sdev->hostdata; if (!sas_device_priv_data) goto not_sata; @@ -1523,7 +1837,31 @@ scsih_change_queue_depth(struct scsi_device *sdev, int qdepth) max_depth = 1; if (qdepth > max_depth) qdepth = max_depth; - return scsi_change_queue_depth(sdev, qdepth); + scsi_change_queue_depth(sdev, qdepth); + sdev_printk(KERN_INFO, sdev, + "qdepth(%d), tagged(%d), scsi_level(%d), cmd_que(%d)\n", + sdev->queue_depth, sdev->tagged_supported, + sdev->scsi_level, ((sdev->inquiry[7] & 2) >> 1)); + return sdev->queue_depth; +} + +/** + * mpt3sas_scsih_change_queue_depth - setting device queue depth + * @sdev: scsi device struct + * @qdepth: requested queue depth + * + * Returns nothing. + */ +void +mpt3sas_scsih_change_queue_depth(struct scsi_device *sdev, int qdepth) +{ + struct Scsi_Host *shost = sdev->host; + struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); + + if (ioc->enable_sdev_max_qd) + qdepth = shost->can_queue; + + scsih_change_queue_depth(sdev, qdepth); } /** @@ -1579,6 +1917,7 @@ scsih_target_alloc(struct scsi_target *starget) if (pcie_device) { sas_target_priv_data->handle = pcie_device->handle; sas_target_priv_data->sas_address = pcie_device->wwid; + sas_target_priv_data->port = NULL; sas_target_priv_data->pcie_dev = pcie_device; pcie_device->starget = starget; pcie_device->id = starget->id; @@ -1596,12 +1935,12 @@ scsih_target_alloc(struct scsi_target *starget) /* sas/sata devices */ spin_lock_irqsave(&ioc->sas_device_lock, flags); rphy = dev_to_rphy(starget->dev.parent); - sas_device = __mpt3sas_get_sdev_by_addr(ioc, - rphy->identify.sas_address); + sas_device = __mpt3sas_get_sdev_by_rphy(ioc, rphy); if (sas_device) { sas_target_priv_data->handle = sas_device->handle; sas_target_priv_data->sas_address = sas_device->sas_address; + sas_target_priv_data->port = sas_device->port; sas_target_priv_data->sas_dev = sas_device; sas_device->starget = starget; sas_device->id = starget->id; @@ -1694,14 +2033,14 @@ scsih_target_destroy(struct scsi_target *starget) } /** - * scsih_slave_alloc - device add routine + * scsih_sdev_init - device add routine * @sdev: scsi device struct * * Return: 0 if ok. Any other return is assumed to be an error and * the device is ignored. */ static int -scsih_slave_alloc(struct scsi_device *sdev) +scsih_sdev_init(struct scsi_device *sdev) { struct Scsi_Host *shost; struct MPT3SAS_ADAPTER *ioc; @@ -1757,7 +2096,8 @@ scsih_slave_alloc(struct scsi_device *sdev) } else if (!(sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME)) { spin_lock_irqsave(&ioc->sas_device_lock, flags); sas_device = __mpt3sas_get_sdev_by_addr(ioc, - sas_target_priv_data->sas_address); + sas_target_priv_data->sas_address, + sas_target_priv_data->port); if (sas_device && (sas_device->starget == NULL)) { sdev_printk(KERN_INFO, sdev, "%s : sas_device->starget set to starget @ %d\n", @@ -1775,11 +2115,11 @@ scsih_slave_alloc(struct scsi_device *sdev) } /** - * scsih_slave_destroy - device destroy routine + * scsih_sdev_destroy - device destroy routine * @sdev: scsi device struct */ static void -scsih_slave_destroy(struct scsi_device *sdev) +scsih_sdev_destroy(struct scsi_device *sdev) { struct MPT3SAS_TARGET *sas_target_priv_data; struct scsi_target *starget; @@ -2098,8 +2438,7 @@ _scsih_get_volume_capabilities(struct MPT3SAS_ADAPTER *ioc, } raid_device->num_pds = num_pds; - sz = offsetof(Mpi2RaidVolPage0_t, PhysDisk) + (num_pds * - sizeof(Mpi2RaidVol0PhysDisk_t)); + sz = struct_size(vol_pg0, PhysDisk, num_pds); vol_pg0 = kzalloc(sz, GFP_KERNEL); if (!vol_pg0) { dfailprintk(ioc, @@ -2165,14 +2504,15 @@ _scsih_enable_tlr(struct MPT3SAS_ADAPTER *ioc, struct scsi_device *sdev) } /** - * scsih_slave_configure - device configure routine. + * scsih_sdev_configure - device configure routine. * @sdev: scsi device struct + * @lim: queue limits * * Return: 0 if ok. Any other return is assumed to be an error and * the device is ignored. */ static int -scsih_slave_configure(struct scsi_device *sdev) +scsih_sdev_configure(struct scsi_device *sdev, struct queue_limits *lim) { struct Scsi_Host *shost = sdev->host; struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); @@ -2277,14 +2617,13 @@ scsih_slave_configure(struct scsi_device *sdev) raid_device->num_pds, ds); if (shost->max_sectors > MPT3SAS_RAID_MAX_SECTORS) { - blk_queue_max_hw_sectors(sdev->request_queue, - MPT3SAS_RAID_MAX_SECTORS); + lim->max_hw_sectors = MPT3SAS_RAID_MAX_SECTORS; sdev_printk(KERN_INFO, sdev, "Set queue's max_sector to: %u\n", MPT3SAS_RAID_MAX_SECTORS); } - scsih_change_queue_depth(sdev, qdepth); + mpt3sas_scsih_change_queue_depth(sdev, qdepth); /* raid transport support */ if (!ioc->is_warpdrive) @@ -2323,7 +2662,7 @@ scsih_slave_configure(struct scsi_device *sdev) return 1; } - qdepth = MPT3SAS_NVME_QUEUE_DEPTH; + qdepth = ioc->max_nvme_qd; ds = "NVMe"; sdev_printk(KERN_INFO, sdev, "%s: handle(0x%04x), wwid(0x%016llx), port(%d)\n", @@ -2343,26 +2682,19 @@ scsih_slave_configure(struct scsi_device *sdev) pcie_device->connector_name); if (pcie_device->nvme_mdts) - blk_queue_max_hw_sectors(sdev->request_queue, - pcie_device->nvme_mdts/512); + lim->max_hw_sectors = pcie_device->nvme_mdts / 512; pcie_device_put(pcie_device); spin_unlock_irqrestore(&ioc->pcie_device_lock, flags); - scsih_change_queue_depth(sdev, qdepth); - /* Enable QUEUE_FLAG_NOMERGES flag, so that IOs won't be - ** merged and can eliminate holes created during merging - ** operation. - **/ - blk_queue_flag_set(QUEUE_FLAG_NOMERGES, - sdev->request_queue); - blk_queue_virt_boundary(sdev->request_queue, - ioc->page_size - 1); + mpt3sas_scsih_change_queue_depth(sdev, qdepth); + lim->virt_boundary_mask = ioc->page_size - 1; return 0; } spin_lock_irqsave(&ioc->sas_device_lock, flags); sas_device = __mpt3sas_get_sdev_by_addr(ioc, - sas_device_priv_data->sas_target->sas_address); + sas_device_priv_data->sas_target->sas_address, + sas_device_priv_data->sas_target->port); if (!sas_device) { spin_unlock_irqrestore(&ioc->sas_device_lock, flags); dfailprintk(ioc, @@ -2374,11 +2706,12 @@ scsih_slave_configure(struct scsi_device *sdev) sas_device->volume_handle = volume_handle; sas_device->volume_wwid = volume_wwid; if (sas_device->device_info & MPI2_SAS_DEVICE_INFO_SSP_TARGET) { - qdepth = MPT3SAS_SAS_QUEUE_DEPTH; + qdepth = (sas_device->port_type > 1) ? + ioc->max_wideport_qd : ioc->max_narrowport_qd; ssp_target = 1; if (sas_device->device_info & MPI2_SAS_DEVICE_INFO_SEP) { - sdev_printk(KERN_WARNING, sdev, + sdev_printk(KERN_INFO, sdev, "set ignore_delay_remove for handle(0x%04x)\n", sas_device_priv_data->sas_target->handle); sas_device_priv_data->ignore_delay_remove = 1; @@ -2386,7 +2719,7 @@ scsih_slave_configure(struct scsi_device *sdev) } else ds = "SSP"; } else { - qdepth = MPT3SAS_SATA_QUEUE_DEPTH; + qdepth = ioc->max_sata_qd; if (sas_device->device_info & MPI2_SAS_DEVICE_INFO_STP_TARGET) ds = "STP"; else if (sas_device->device_info & @@ -2408,7 +2741,7 @@ scsih_slave_configure(struct scsi_device *sdev) _scsih_display_sata_capabilities(ioc, handle, sdev); - scsih_change_queue_depth(sdev, qdepth); + mpt3sas_scsih_change_queue_depth(sdev, qdepth); if (ssp_target) { sas_read_port_mode_page(sdev); @@ -2421,7 +2754,7 @@ scsih_slave_configure(struct scsi_device *sdev) /** * scsih_bios_param - fetch head, sector, cylinder info for a disk * @sdev: scsi device struct - * @bdev: pointer to block device context + * @unused: pointer to gendisk * @capacity: device size (in 512 byte sectors) * @params: three element array to place output: * params[0] number of heads (max 255) @@ -2429,7 +2762,7 @@ scsih_slave_configure(struct scsi_device *sdev) * params[2] number of cylinders */ static int -scsih_bios_param(struct scsi_device *sdev, struct block_device *bdev, +scsih_bios_param(struct scsi_device *sdev, struct gendisk *unused, sector_t capacity, int params[]) { int heads; @@ -2596,9 +2929,101 @@ mpt3sas_scsih_clear_tm_flag(struct MPT3SAS_ADAPTER *ioc, u16 handle) } /** + * scsih_tm_cmd_map_status - map the target reset & LUN reset TM status + * @ioc: per adapter object + * @channel: the channel assigned by the OS + * @id: the id assigned by the OS + * @lun: lun number + * @type: MPI2_SCSITASKMGMT_TASKTYPE__XXX (defined in mpi2_init.h) + * @smid_task: smid assigned to the task + * + * Look whether TM has aborted the timed out SCSI command, if + * TM has aborted the IO then return SUCCESS else return FAILED. + */ +static int +scsih_tm_cmd_map_status(struct MPT3SAS_ADAPTER *ioc, uint channel, + uint id, uint lun, u8 type, u16 smid_task) +{ + + if (smid_task <= ioc->shost->can_queue) { + switch (type) { + case MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET: + if (!(_scsih_scsi_lookup_find_by_target(ioc, + id, channel))) + return SUCCESS; + break; + case MPI2_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET: + case MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET: + if (!(_scsih_scsi_lookup_find_by_lun(ioc, id, + lun, channel))) + return SUCCESS; + break; + default: + return SUCCESS; + } + } else if (smid_task == ioc->scsih_cmds.smid) { + if ((ioc->scsih_cmds.status & MPT3_CMD_COMPLETE) || + (ioc->scsih_cmds.status & MPT3_CMD_NOT_USED)) + return SUCCESS; + } else if (smid_task == ioc->ctl_cmds.smid) { + if ((ioc->ctl_cmds.status & MPT3_CMD_COMPLETE) || + (ioc->ctl_cmds.status & MPT3_CMD_NOT_USED)) + return SUCCESS; + } + + return FAILED; +} + +/** + * scsih_tm_post_processing - post processing of target & LUN reset + * @ioc: per adapter object + * @handle: device handle + * @channel: the channel assigned by the OS + * @id: the id assigned by the OS + * @lun: lun number + * @type: MPI2_SCSITASKMGMT_TASKTYPE__XXX (defined in mpi2_init.h) + * @smid_task: smid assigned to the task + * + * Post processing of target & LUN reset. Due to interrupt latency + * issue it possible that interrupt for aborted IO might not be + * received yet. So before returning failure status, poll the + * reply descriptor pools for the reply of timed out SCSI command. + * Return FAILED status if reply for timed out is not received + * otherwise return SUCCESS. + */ +static int +scsih_tm_post_processing(struct MPT3SAS_ADAPTER *ioc, u16 handle, + uint channel, uint id, uint lun, u8 type, u16 smid_task) +{ + int rc; + + rc = scsih_tm_cmd_map_status(ioc, channel, id, lun, type, smid_task); + if (rc == SUCCESS) + return rc; + + ioc_info(ioc, + "Poll ReplyDescriptor queues for completion of" + " smid(%d), task_type(0x%02x), handle(0x%04x)\n", + smid_task, type, handle); + + /* + * Due to interrupt latency issues, driver may receive interrupt for + * TM first and then for aborted SCSI IO command. So, poll all the + * ReplyDescriptor pools before returning the FAILED status to SML. + */ + mpt3sas_base_mask_interrupts(ioc); + mpt3sas_base_sync_reply_irqs(ioc, 1); + mpt3sas_base_unmask_interrupts(ioc); + + return scsih_tm_cmd_map_status(ioc, channel, id, lun, type, smid_task); +} + +/** * mpt3sas_scsih_issue_tm - main routine for sending tm requests * @ioc: per adapter struct * @handle: device handle + * @channel: the channel assigned by the OS + * @id: the id assigned by the OS * @lun: lun number * @type: MPI2_SCSITASKMGMT_TASKTYPE__XXX (defined in mpi2_init.h) * @smid_task: smid assigned to the task @@ -2615,14 +3040,17 @@ mpt3sas_scsih_clear_tm_flag(struct MPT3SAS_ADAPTER *ioc, u16 handle) * Return: SUCCESS or FAILED. */ int -mpt3sas_scsih_issue_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, u64 lun, - u8 type, u16 smid_task, u16 msix_task, u8 timeout, u8 tr_method) +mpt3sas_scsih_issue_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, uint channel, + uint id, u64 lun, u8 type, u16 smid_task, u16 msix_task, + u8 timeout, u8 tr_method) { Mpi2SCSITaskManagementRequest_t *mpi_request; Mpi2SCSITaskManagementReply_t *mpi_reply; + Mpi25SCSIIORequest_t *request; u16 smid = 0; u32 ioc_state; int rc; + u8 issue_reset = 0; lockdep_assert_held(&ioc->tm_cmds.mutex); @@ -2645,7 +3073,13 @@ mpt3sas_scsih_issue_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, u64 lun, } if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) { - mpt3sas_base_fault_info(ioc, ioc_state & + mpt3sas_print_fault_code(ioc, ioc_state & + MPI2_DOORBELL_DATA_MASK); + rc = mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); + return (!rc) ? SUCCESS : FAILED; + } else if ((ioc_state & MPI2_IOC_STATE_MASK) == + MPI2_IOC_STATE_COREDUMP) { + mpt3sas_print_coredump_info(ioc, ioc_state & MPI2_DOORBELL_DATA_MASK); rc = mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); return (!rc) ? SUCCESS : FAILED; @@ -2668,17 +3102,20 @@ mpt3sas_scsih_issue_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, u64 lun, mpi_request->Function = MPI2_FUNCTION_SCSI_TASK_MGMT; mpi_request->DevHandle = cpu_to_le16(handle); mpi_request->TaskType = type; - mpi_request->MsgFlags = tr_method; + if (type == MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK || + type == MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK) + mpi_request->MsgFlags = tr_method; mpi_request->TaskMID = cpu_to_le16(smid_task); int_to_scsilun(lun, (struct scsi_lun *)mpi_request->LUN); mpt3sas_scsih_set_tm_flag(ioc, handle); init_completion(&ioc->tm_cmds.done); - mpt3sas_base_put_smid_hi_priority(ioc, smid, msix_task); + ioc->put_smid_hi_priority(ioc, smid, msix_task); wait_for_completion_timeout(&ioc->tm_cmds.done, timeout*HZ); if (!(ioc->tm_cmds.status & MPT3_CMD_COMPLETE)) { - if (mpt3sas_base_check_cmd_timeout(ioc, - ioc->tm_cmds.status, mpi_request, - sizeof(Mpi2SCSITaskManagementRequest_t)/4)) { + mpt3sas_check_cmd_timeout(ioc, + ioc->tm_cmds.status, mpi_request, + sizeof(Mpi2SCSITaskManagementRequest_t)/4, issue_reset); + if (issue_reset) { rc = mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); rc = (!rc) ? SUCCESS : FAILED; @@ -2687,7 +3124,7 @@ mpt3sas_scsih_issue_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, u64 lun, } /* sync IRQs in case those were busy during flush. */ - mpt3sas_base_sync_reply_irqs(ioc); + mpt3sas_base_sync_reply_irqs(ioc, 0); if (ioc->tm_cmds.status & MPT3_CMD_REPLY_VALID) { mpt3sas_trigger_master(ioc, MASTER_TRIGGER_TASK_MANAGMENT); @@ -2704,7 +3141,44 @@ mpt3sas_scsih_issue_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, u64 lun, sizeof(Mpi2SCSITaskManagementRequest_t)/4); } } - rc = SUCCESS; + + switch (type) { + case MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK: + rc = SUCCESS; + /* + * If DevHandle filed in smid_task's entry of request pool + * doesn't match with device handle on which this task abort + * TM is received then it means that TM has successfully + * aborted the timed out command. Since smid_task's entry in + * request pool will be memset to zero once the timed out + * command is returned to the SML. If the command is not + * aborted then smid_task’s entry won’t be cleared and it + * will have same DevHandle value on which this task abort TM + * is received and driver will return the TM status as FAILED. + */ + request = mpt3sas_base_get_msg_frame(ioc, smid_task); + if (le16_to_cpu(request->DevHandle) != handle) + break; + + ioc_info(ioc, "Task abort tm failed: handle(0x%04x)," + "timeout(%d) tr_method(0x%x) smid(%d) msix_index(%d)\n", + handle, timeout, tr_method, smid_task, msix_task); + rc = FAILED; + break; + + case MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET: + case MPI2_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET: + case MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET: + rc = scsih_tm_post_processing(ioc, handle, channel, id, lun, + type, smid_task); + break; + case MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK: + rc = SUCCESS; + break; + default: + rc = FAILED; + break; + } out: mpt3sas_scsih_clear_tm_flag(ioc, handle); @@ -2713,14 +3187,14 @@ out: } int mpt3sas_scsih_issue_locked_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, - u64 lun, u8 type, u16 smid_task, u16 msix_task, - u8 timeout, u8 tr_method) + uint channel, uint id, u64 lun, u8 type, u16 smid_task, + u16 msix_task, u8 timeout, u8 tr_method) { int ret; mutex_lock(&ioc->tm_cmds.mutex); - ret = mpt3sas_scsih_issue_tm(ioc, handle, lun, type, smid_task, - msix_task, timeout, tr_method); + ret = mpt3sas_scsih_issue_tm(ioc, handle, channel, id, lun, type, + smid_task, msix_task, timeout, tr_method); mutex_unlock(&ioc->tm_cmds.mutex); return ret; @@ -2825,23 +3299,27 @@ scsih_abort(struct scsi_cmnd *scmd) u8 timeout = 30; struct _pcie_device *pcie_device = NULL; - sdev_printk(KERN_INFO, scmd->device, - "attempting task abort! scmd(%p)\n", scmd); + sdev_printk(KERN_INFO, scmd->device, "attempting task abort!" + "scmd(0x%p), outstanding for %u ms & timeout %u ms\n", + scmd, jiffies_to_msecs(jiffies - scmd->jiffies_at_alloc), + (scsi_cmd_to_rq(scmd)->timeout / HZ) * 1000); _scsih_tm_display_info(ioc, scmd); sas_device_priv_data = scmd->device->hostdata; if (!sas_device_priv_data || !sas_device_priv_data->sas_target || ioc->remove_host) { sdev_printk(KERN_INFO, scmd->device, - "device been deleted! scmd(%p)\n", scmd); + "device been deleted! scmd(0x%p)\n", scmd); scmd->result = DID_NO_CONNECT << 16; - scmd->scsi_done(scmd); + scsi_done(scmd); r = SUCCESS; goto out; } /* check for completed command */ if (st == NULL || st->cb_idx == 0xFF) { + sdev_printk(KERN_INFO, scmd->device, "No reference found at " + "driver, assuming scmd(0x%p) might have completed\n", scmd); scmd->result = DID_RESET << 16; r = SUCCESS; goto out; @@ -2860,16 +3338,18 @@ scsih_abort(struct scsi_cmnd *scmd) handle = sas_device_priv_data->sas_target->handle; pcie_device = mpt3sas_get_pdev_by_handle(ioc, handle); - if (pcie_device && (!ioc->tm_custom_handling)) + if (pcie_device && (!ioc->tm_custom_handling) && + (!(mpt3sas_scsih_is_pcie_scsi_device(pcie_device->device_info)))) timeout = ioc->nvme_abort_timeout; - r = mpt3sas_scsih_issue_locked_tm(ioc, handle, scmd->device->lun, + r = mpt3sas_scsih_issue_locked_tm(ioc, handle, scmd->device->channel, + scmd->device->id, scmd->device->lun, MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK, st->smid, st->msix_io, timeout, 0); /* Command must be cleared after abort */ if (r == SUCCESS && st->cb_idx != 0xFF) r = FAILED; out: - sdev_printk(KERN_INFO, scmd->device, "task abort: %s scmd(%p)\n", + sdev_printk(KERN_INFO, scmd->device, "task abort: %s scmd(0x%p)\n", ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd); if (pcie_device) pcie_device_put(pcie_device); @@ -2898,16 +3378,16 @@ scsih_dev_reset(struct scsi_cmnd *scmd) struct MPT3SAS_TARGET *target_priv_data = starget->hostdata; sdev_printk(KERN_INFO, scmd->device, - "attempting device reset! scmd(%p)\n", scmd); + "attempting device reset! scmd(0x%p)\n", scmd); _scsih_tm_display_info(ioc, scmd); sas_device_priv_data = scmd->device->hostdata; if (!sas_device_priv_data || !sas_device_priv_data->sas_target || ioc->remove_host) { sdev_printk(KERN_INFO, scmd->device, - "device been deleted! scmd(%p)\n", scmd); + "device been deleted! scmd(0x%p)\n", scmd); scmd->result = DID_NO_CONNECT << 16; - scmd->scsi_done(scmd); + scsi_done(scmd); r = SUCCESS; goto out; } @@ -2931,19 +3411,22 @@ scsih_dev_reset(struct scsi_cmnd *scmd) pcie_device = mpt3sas_get_pdev_by_handle(ioc, handle); - if (pcie_device && (!ioc->tm_custom_handling)) { + if (pcie_device && (!ioc->tm_custom_handling) && + (!(mpt3sas_scsih_is_pcie_scsi_device(pcie_device->device_info)))) { tr_timeout = pcie_device->reset_timeout; tr_method = MPI26_SCSITASKMGMT_MSGFLAGS_PROTOCOL_LVL_RST_PCIE; } else tr_method = MPI2_SCSITASKMGMT_MSGFLAGS_LINK_RESET; - r = mpt3sas_scsih_issue_locked_tm(ioc, handle, scmd->device->lun, + + r = mpt3sas_scsih_issue_locked_tm(ioc, handle, scmd->device->channel, + scmd->device->id, scmd->device->lun, MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET, 0, 0, tr_timeout, tr_method); /* Check for busy commands after reset */ - if (r == SUCCESS && atomic_read(&scmd->device->device_busy)) + if (r == SUCCESS && scsi_device_busy(scmd->device)) r = FAILED; out: - sdev_printk(KERN_INFO, scmd->device, "device reset: %s scmd(%p)\n", + sdev_printk(KERN_INFO, scmd->device, "device reset: %s scmd(0x%p)\n", ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd); if (sas_device) @@ -2974,17 +3457,17 @@ scsih_target_reset(struct scsi_cmnd *scmd) struct scsi_target *starget = scmd->device->sdev_target; struct MPT3SAS_TARGET *target_priv_data = starget->hostdata; - starget_printk(KERN_INFO, starget, "attempting target reset! scmd(%p)\n", - scmd); + starget_printk(KERN_INFO, starget, + "attempting target reset! scmd(0x%p)\n", scmd); _scsih_tm_display_info(ioc, scmd); sas_device_priv_data = scmd->device->hostdata; if (!sas_device_priv_data || !sas_device_priv_data->sas_target || ioc->remove_host) { - starget_printk(KERN_INFO, starget, "target been deleted! scmd(%p)\n", - scmd); + starget_printk(KERN_INFO, starget, + "target been deleted! scmd(0x%p)\n", scmd); scmd->result = DID_NO_CONNECT << 16; - scmd->scsi_done(scmd); + scsi_done(scmd); r = SUCCESS; goto out; } @@ -3008,19 +3491,21 @@ scsih_target_reset(struct scsi_cmnd *scmd) pcie_device = mpt3sas_get_pdev_by_handle(ioc, handle); - if (pcie_device && (!ioc->tm_custom_handling)) { + if (pcie_device && (!ioc->tm_custom_handling) && + (!(mpt3sas_scsih_is_pcie_scsi_device(pcie_device->device_info)))) { tr_timeout = pcie_device->reset_timeout; tr_method = MPI26_SCSITASKMGMT_MSGFLAGS_PROTOCOL_LVL_RST_PCIE; } else tr_method = MPI2_SCSITASKMGMT_MSGFLAGS_LINK_RESET; - r = mpt3sas_scsih_issue_locked_tm(ioc, handle, 0, + r = mpt3sas_scsih_issue_locked_tm(ioc, handle, scmd->device->channel, + scmd->device->id, 0, MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, 0, tr_timeout, tr_method); /* Check for busy commands after reset */ if (r == SUCCESS && atomic_read(&starget->target_busy)) r = FAILED; out: - starget_printk(KERN_INFO, starget, "target reset: %s scmd(%p)\n", + starget_printk(KERN_INFO, starget, "target reset: %s scmd(0x%p)\n", ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd); if (sas_device) @@ -3043,7 +3528,7 @@ scsih_host_reset(struct scsi_cmnd *scmd) struct MPT3SAS_ADAPTER *ioc = shost_priv(scmd->device->host); int r, retval; - ioc_info(ioc, "attempting host reset! scmd(%p)\n", scmd); + ioc_info(ioc, "attempting host reset! scmd(0x%p)\n", scmd); scsi_print_command(scmd); if (ioc->is_driver_loading || ioc->remove_host) { @@ -3055,7 +3540,7 @@ scsih_host_reset(struct scsi_cmnd *scmd) retval = mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); r = (retval < 0) ? FAILED : SUCCESS; out: - ioc_info(ioc, "host reset: %s scmd(%p)\n", + ioc_info(ioc, "host reset: %s scmd(0x%p)\n", r == SUCCESS ? "SUCCESS" : "FAILED", scmd); return r; @@ -3145,8 +3630,6 @@ _scsih_error_recovery_delete_devices(struct MPT3SAS_ADAPTER *ioc) { struct fw_event_work *fw_event; - if (ioc->is_driver_loading) - return; fw_event = alloc_fw_event_work(0); if (!fw_event) return; @@ -3184,6 +3667,7 @@ static struct fw_event_work *dequeue_next_fw_event(struct MPT3SAS_ADAPTER *ioc) fw_event = list_first_entry(&ioc->fw_event_list, struct fw_event_work, list); list_del_init(&fw_event->list); + fw_event_work_put(fw_event); } spin_unlock_irqrestore(&ioc->fw_event_lock, flags); @@ -3196,17 +3680,64 @@ static struct fw_event_work *dequeue_next_fw_event(struct MPT3SAS_ADAPTER *ioc) * * Walk the firmware event queue, either killing timers, or waiting * for outstanding events to complete + * + * Context: task, can sleep */ static void _scsih_fw_event_cleanup_queue(struct MPT3SAS_ADAPTER *ioc) { struct fw_event_work *fw_event; - if (list_empty(&ioc->fw_event_list) || - !ioc->firmware_event_thread || in_interrupt()) + if ((list_empty(&ioc->fw_event_list) && !ioc->current_event) || + !ioc->firmware_event_thread) return; + /* + * Set current running event as ignore, so that + * current running event will exit quickly. + * As diag reset has occurred it is of no use + * to process remaining stale event data entries. + */ + if (ioc->shost_recovery && ioc->current_event) + ioc->current_event->ignore = 1; + + ioc->fw_events_cleanup = 1; + while ((fw_event = dequeue_next_fw_event(ioc)) || + (fw_event = ioc->current_event)) { + + /* + * Don't call cancel_work_sync() for current_event + * other than MPT3SAS_REMOVE_UNRESPONDING_DEVICES; + * otherwise we may observe deadlock if current + * hard reset issued as part of processing the current_event. + * + * Orginal logic of cleaning the current_event is added + * for handling the back to back host reset issued by the user. + * i.e. during back to back host reset, driver use to process + * the two instances of MPT3SAS_REMOVE_UNRESPONDING_DEVICES + * event back to back and this made the drives to unregister + * the devices from SML. + */ + + if (fw_event == ioc->current_event && + ioc->current_event->event != + MPT3SAS_REMOVE_UNRESPONDING_DEVICES) { + ioc->current_event = NULL; + continue; + } + + /* + * Driver has to clear ioc->start_scan flag when + * it is cleaning up MPT3SAS_PORT_ENABLE_COMPLETE, + * otherwise scsi_scan_host() API waits for the + * 5 minute timer to expire. If we exit from + * scsi_scan_host() early then we can issue the + * new port enable request as part of current diag reset. + */ + if (fw_event->event == MPT3SAS_PORT_ENABLE_COMPLETE) { + ioc->port_enable_cmds.status |= MPT3_CMD_RESET; + ioc->start_scan = 0; + } - while ((fw_event = dequeue_next_fw_event(ioc))) { /* * Wait on the fw_event to complete. If this returns 1, then * the event was never executed, and we need a put for the @@ -3218,8 +3749,8 @@ _scsih_fw_event_cleanup_queue(struct MPT3SAS_ADAPTER *ioc) if (cancel_work_sync(&fw_event->work)) fw_event_work_put(fw_event); - fw_event_work_put(fw_event); } + ioc->fw_events_cleanup = 0; } /** @@ -3322,22 +3853,26 @@ _scsih_ublock_io_all_device(struct MPT3SAS_ADAPTER *ioc) * _scsih_ublock_io_device - prepare device to be deleted * @ioc: per adapter object * @sas_address: sas address + * @port: hba port entry * * unblock then put device in offline state */ static void -_scsih_ublock_io_device(struct MPT3SAS_ADAPTER *ioc, u64 sas_address) +_scsih_ublock_io_device(struct MPT3SAS_ADAPTER *ioc, + u64 sas_address, struct hba_port *port) { struct MPT3SAS_DEVICE *sas_device_priv_data; struct scsi_device *sdev; shost_for_each_device(sdev, ioc->shost) { sas_device_priv_data = sdev->hostdata; - if (!sas_device_priv_data) + if (!sas_device_priv_data || !sas_device_priv_data->sas_target) continue; if (sas_device_priv_data->sas_target->sas_address != sas_address) continue; + if (sas_device_priv_data->sas_target->port != port) + continue; if (sas_device_priv_data->block) _scsih_internal_device_unblock(sdev, sas_device_priv_data); @@ -3438,7 +3973,8 @@ _scsih_block_io_to_children_attached_to_ex(struct MPT3SAS_ADAPTER *ioc, SAS_END_DEVICE) { spin_lock_irqsave(&ioc->sas_device_lock, flags); sas_device = __mpt3sas_get_sdev_by_addr(ioc, - mpt3sas_port->remote_identify.sas_address); + mpt3sas_port->remote_identify.sas_address, + mpt3sas_port->hba_port); if (sas_device) { set_bit(sas_device->handle, ioc->blocking_handles); @@ -3457,7 +3993,8 @@ _scsih_block_io_to_children_attached_to_ex(struct MPT3SAS_ADAPTER *ioc, SAS_FANOUT_EXPANDER_DEVICE) { expander_sibling = mpt3sas_scsih_expander_find_by_sas_address( - ioc, mpt3sas_port->remote_identify.sas_address); + ioc, mpt3sas_port->remote_identify.sas_address, + mpt3sas_port->hba_port); _scsih_block_io_to_children_attached_to_ex(ioc, expander_sibling); } @@ -3546,6 +4083,7 @@ _scsih_tm_tr_send(struct MPT3SAS_ADAPTER *ioc, u16 handle) struct _tr_list *delayed_tr; u32 ioc_state; u8 tr_method = 0; + struct hba_port *port = NULL; if (ioc->pci_error_recovery) { dewtprintk(ioc, @@ -3574,6 +4112,7 @@ _scsih_tm_tr_send(struct MPT3SAS_ADAPTER *ioc, u16 handle) sas_target_priv_data = sas_device->starget->hostdata; sas_target_priv_data->deleted = 1; sas_address = sas_device->sas_address; + port = sas_device->port; } spin_unlock_irqrestore(&ioc->sas_device_lock, flags); if (!sas_device) { @@ -3586,7 +4125,9 @@ _scsih_tm_tr_send(struct MPT3SAS_ADAPTER *ioc, u16 handle) sas_address = pcie_device->wwid; } spin_unlock_irqrestore(&ioc->pcie_device_lock, flags); - if (pcie_device && (!ioc->tm_custom_handling)) + if (pcie_device && (!ioc->tm_custom_handling) && + (!(mpt3sas_scsih_is_pcie_scsi_device( + pcie_device->device_info)))) tr_method = MPI26_SCSITASKMGMT_MSGFLAGS_PROTOCOL_LVL_RST_PCIE; else @@ -3619,7 +4160,7 @@ _scsih_tm_tr_send(struct MPT3SAS_ADAPTER *ioc, u16 handle) pcie_device->enclosure_level, pcie_device->connector_name)); } - _scsih_ublock_io_device(ioc, sas_address); + _scsih_ublock_io_device(ioc, sas_address, port); sas_target_priv_data->handle = MPT3SAS_INVALID_DEVICE_HANDLE; } @@ -3647,7 +4188,7 @@ _scsih_tm_tr_send(struct MPT3SAS_ADAPTER *ioc, u16 handle) mpi_request->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET; mpi_request->MsgFlags = tr_method; set_bit(handle, ioc->device_remove_in_progress); - mpt3sas_base_put_smid_hi_priority(ioc, smid, 0); + ioc->put_smid_hi_priority(ioc, smid, 0); mpt3sas_trigger_master(ioc, MASTER_TRIGGER_DEVICE_REMOVAL); out: @@ -3743,7 +4284,7 @@ _scsih_tm_tr_complete(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, mpi_request->Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL; mpi_request->Operation = MPI2_SAS_OP_REMOVE_DEVICE; mpi_request->DevHandle = mpi_request_tm->DevHandle; - mpt3sas_base_put_smid_default(ioc, smid_sas_ctrl); + ioc->put_smid_default(ioc, smid_sas_ctrl); return _scsih_check_for_pending_tm(ioc, smid); } @@ -3869,7 +4410,7 @@ _scsih_tm_tr_volume_send(struct MPT3SAS_ADAPTER *ioc, u16 handle) mpi_request->Function = MPI2_FUNCTION_SCSI_TASK_MGMT; mpi_request->DevHandle = cpu_to_le16(handle); mpi_request->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET; - mpt3sas_base_put_smid_hi_priority(ioc, smid, 0); + ioc->put_smid_hi_priority(ioc, smid, 0); } /** @@ -3958,7 +4499,7 @@ _scsih_issue_delayed_event_ack(struct MPT3SAS_ADAPTER *ioc, u16 smid, U16 event, ack_request->EventContext = event_context; ack_request->VF_ID = 0; /* TODO */ ack_request->VP_ID = 0; - mpt3sas_base_put_smid_default(ioc, smid); + ioc->put_smid_default(ioc, smid); } /** @@ -4014,11 +4555,11 @@ _scsih_issue_delayed_sas_io_unit_ctrl(struct MPT3SAS_ADAPTER *ioc, mpi_request->Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL; mpi_request->Operation = MPI2_SAS_OP_REMOVE_DEVICE; mpi_request->DevHandle = cpu_to_le16(handle); - mpt3sas_base_put_smid_default(ioc, smid); + ioc->put_smid_default(ioc, smid); } /** - * _scsih_check_for_pending_internal_cmds - check for pending internal messages + * mpt3sas_check_for_pending_internal_cmds - check for pending internal messages * @ioc: per adapter object * @smid: system request message index * @@ -4419,6 +4960,7 @@ static void _scsih_temp_threshold_events(struct MPT3SAS_ADAPTER *ioc, Mpi2EventDataTemperature_t *event_data) { + u32 doorbell; if (ioc->temp_sensors_count >= event_data->SensorNum) { ioc_err(ioc, "Temperature Threshold flags %s%s%s%s exceeded for Sensor: %d !!!\n", le16_to_cpu(event_data->Status) & 0x1 ? "0 " : " ", @@ -4428,6 +4970,18 @@ _scsih_temp_threshold_events(struct MPT3SAS_ADAPTER *ioc, event_data->SensorNum); ioc_err(ioc, "Current Temp In Celsius: %d\n", event_data->CurrentTemperature); + if (ioc->hba_mpi_version_belonged != MPI2_VERSION) { + doorbell = mpt3sas_base_get_iocstate(ioc, 0); + if ((doorbell & MPI2_IOC_STATE_MASK) == + MPI2_IOC_STATE_FAULT) { + mpt3sas_print_fault_code(ioc, + doorbell & MPI2_DOORBELL_DATA_MASK); + } else if ((doorbell & MPI2_IOC_STATE_MASK) == + MPI2_IOC_STATE_COREDUMP) { + mpt3sas_print_coredump_info(ioc, + doorbell & MPI2_DOORBELL_DATA_MASK); + } + } } } @@ -4473,7 +5027,7 @@ _scsih_flush_running_cmds(struct MPT3SAS_ADAPTER *ioc) scmd->result = DID_NO_CONNECT << 16; else scmd->result = DID_RESET << 16; - scmd->scsi_done(scmd); + scsi_done(scmd); } dtmprintk(ioc, ioc_info(ioc, "completing %d cmds\n", count)); } @@ -4491,48 +5045,34 @@ _scsih_setup_eedp(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd, Mpi25SCSIIORequest_t *mpi_request) { u16 eedp_flags; - unsigned char prot_op = scsi_get_prot_op(scmd); - unsigned char prot_type = scsi_get_prot_type(scmd); Mpi25SCSIIORequest_t *mpi_request_3v = (Mpi25SCSIIORequest_t *)mpi_request; - if (prot_type == SCSI_PROT_DIF_TYPE0 || prot_op == SCSI_PROT_NORMAL) - return; - - if (prot_op == SCSI_PROT_READ_STRIP) + switch (scsi_get_prot_op(scmd)) { + case SCSI_PROT_READ_STRIP: eedp_flags = MPI2_SCSIIO_EEDPFLAGS_CHECK_REMOVE_OP; - else if (prot_op == SCSI_PROT_WRITE_INSERT) + break; + case SCSI_PROT_WRITE_INSERT: eedp_flags = MPI2_SCSIIO_EEDPFLAGS_INSERT_OP; - else + break; + default: return; + } - switch (prot_type) { - case SCSI_PROT_DIF_TYPE1: - case SCSI_PROT_DIF_TYPE2: - - /* - * enable ref/guard checking - * auto increment ref tag - */ - eedp_flags |= MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG | - MPI2_SCSIIO_EEDPFLAGS_CHECK_REFTAG | - MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD; - mpi_request->CDB.EEDP32.PrimaryReferenceTag = - cpu_to_be32(t10_pi_ref_tag(scmd->request)); - break; + if (scmd->prot_flags & SCSI_PROT_GUARD_CHECK) + eedp_flags |= MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD; - case SCSI_PROT_DIF_TYPE3: + if (scmd->prot_flags & SCSI_PROT_REF_CHECK) + eedp_flags |= MPI2_SCSIIO_EEDPFLAGS_CHECK_REFTAG; - /* - * enable guard checking - */ - eedp_flags |= MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD; + if (scmd->prot_flags & SCSI_PROT_REF_INCREMENT) { + eedp_flags |= MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG; - break; + mpi_request->CDB.EEDP32.PrimaryReferenceTag = + cpu_to_be32(scsi_prot_ref_tag(scmd)); } - mpi_request_3v->EEDPBlockSize = - cpu_to_le16(scmd->device->sector_size); + mpi_request_3v->EEDPBlockSize = cpu_to_le16(scsi_prot_interval(scmd)); if (ioc->is_gen35_ioc) eedp_flags |= MPI25_SCSIIO_EEDPFLAGS_APPTAG_DISABLE_MODE; @@ -4563,10 +5103,8 @@ _scsih_eedp_error_handling(struct scsi_cmnd *scmd, u16 ioc_status) ascq = 0x00; break; } - scsi_build_sense_buffer(0, scmd->sense_buffer, ILLEGAL_REQUEST, 0x10, - ascq); - scmd->result = DRIVER_SENSE << 24 | (DID_ABORT << 16) | - SAM_STAT_CHECK_CONDITION; + scsi_build_sense(scmd, 0, ILLEGAL_REQUEST, 0x10, ascq); + set_host_byte(scmd, DID_ABORT); } /** @@ -4587,7 +5125,7 @@ scsih_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *scmd) struct MPT3SAS_DEVICE *sas_device_priv_data; struct MPT3SAS_TARGET *sas_target_priv_data; struct _raid_device *raid_device; - struct request *rq = scmd->request; + struct request *rq = scsi_cmd_to_rq(scmd); int class; Mpi25SCSIIORequest_t *mpi_request; struct _pcie_device *pcie_device = NULL; @@ -4601,13 +5139,13 @@ scsih_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *scmd) sas_device_priv_data = scmd->device->hostdata; if (!sas_device_priv_data || !sas_device_priv_data->sas_target) { scmd->result = DID_NO_CONNECT << 16; - scmd->scsi_done(scmd); + scsi_done(scmd); return 0; } if (!(_scsih_allow_scmd_to_device(ioc, scmd))) { scmd->result = DID_NO_CONNECT << 16; - scmd->scsi_done(scmd); + scsi_done(scmd); return 0; } @@ -4615,9 +5153,22 @@ scsih_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *scmd) /* invalid device handle */ handle = sas_target_priv_data->handle; + + /* + * Avoid error handling escallation when device is disconnected + */ + if (handle == MPT3SAS_INVALID_DEVICE_HANDLE || sas_device_priv_data->block) { + if (scmd->device->host->shost_state == SHOST_RECOVERY && + scmd->cmnd[0] == TEST_UNIT_READY) { + scsi_build_sense(scmd, 0, UNIT_ATTENTION, 0x29, 0x07); + scsi_done(scmd); + return 0; + } + } + if (handle == MPT3SAS_INVALID_DEVICE_HANDLE) { scmd->result = DID_NO_CONNECT << 16; - scmd->scsi_done(scmd); + scsi_done(scmd); return 0; } @@ -4628,7 +5179,7 @@ scsih_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *scmd) } else if (sas_target_priv_data->deleted) { /* device has been deleted */ scmd->result = DID_NO_CONNECT << 16; - scmd->scsi_done(scmd); + scsi_done(scmd); return 0; } else if (sas_target_priv_data->tm_busy || sas_device_priv_data->block) { @@ -4642,11 +5193,8 @@ scsih_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *scmd) * since we're lockless at this point */ do { - if (test_bit(0, &sas_device_priv_data->ata_command_pending)) { - scmd->result = SAM_STAT_BUSY; - scmd->scsi_done(scmd); - return 0; - } + if (test_bit(0, &sas_device_priv_data->ata_command_pending)) + return SCSI_MLQUEUE_DEVICE_BUSY; } while (_scsih_set_satl_pending(scmd, true)); if (scmd->sc_data_direction == DMA_FROM_DEVICE) @@ -4722,12 +5270,12 @@ scsih_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *scmd) if (sas_target_priv_data->flags & MPT_TARGET_FASTPATH_IO) { mpi_request->IoFlags = cpu_to_le16(scmd->cmd_len | MPI25_SCSIIO_IOFLAGS_FAST_PATH); - mpt3sas_base_put_smid_fast_path(ioc, smid, handle); + ioc->put_smid_fast_path(ioc, smid, handle); } else ioc->put_smid_scsi_io(ioc, smid, le16_to_cpu(mpi_request->DevHandle)); } else - mpt3sas_base_put_smid_default(ioc, smid); + ioc->put_smid_default(ioc, smid); return 0; out: @@ -4756,7 +5304,7 @@ _scsih_normalize_sense(char *sense_buffer, struct sense_info *data) } /** - * _scsih_scsi_ioc_info - translated non-succesfull SCSI_IO request + * _scsih_scsi_ioc_info - translated non-successful SCSI_IO request * @ioc: per adapter object * @scmd: pointer to scsi command object * @mpi_reply: reply mf payload returned from firmware @@ -5108,7 +5656,7 @@ _scsih_smart_predicted_fault(struct MPT3SAS_ADAPTER *ioc, u16 handle) /* insert into event log */ sz = offsetof(Mpi2EventNotificationReply_t, EventData) + sizeof(Mpi2EventDataSasDeviceStatusChange_t); - event_reply = kzalloc(sz, GFP_KERNEL); + event_reply = kzalloc(sz, GFP_ATOMIC); if (!event_reply) { ioc_err(ioc, "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); @@ -5198,6 +5746,7 @@ _scsih_io_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) ((ioc_status & MPI2_IOCSTATUS_MASK) != MPI2_IOCSTATUS_SCSI_TASK_TERMINATED)) { st->direct_io = 0; + st->scmd = scmd; memcpy(mpi_request->CDB.CDB32, scmd->cmnd, scmd->cmd_len); mpi_request->DevHandle = cpu_to_le16(sas_device_priv_data->sas_target->handle); @@ -5273,6 +5822,17 @@ _scsih_io_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) scmd->result = DID_TRANSPORT_DISRUPTED << 16; goto out; } + if (log_info == IOC_LOGINFO_SATA_NCQ_FAIL_AFTER_ERR) { + /* + * This is a ATA NCQ command aborted due to another NCQ + * command failure. We must retry this command + * immediately but without incrementing its retry + * counter. + */ + WARN_ON_ONCE(xfer_cnt != 0); + scmd->result = DID_IMM_RETRY << 16; + break; + } if (log_info == 0x31110630) { if (scmd->retries > 2) { scmd->result = DID_NO_CONNECT << 16; @@ -5325,18 +5885,14 @@ _scsih_io_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) else if (!xfer_cnt && scmd->cmnd[0] == REPORT_LUNS) { mpi_reply->SCSIState = MPI2_SCSI_STATE_AUTOSENSE_VALID; mpi_reply->SCSIStatus = SAM_STAT_CHECK_CONDITION; - scmd->result = (DRIVER_SENSE << 24) | - SAM_STAT_CHECK_CONDITION; - scmd->sense_buffer[0] = 0x70; - scmd->sense_buffer[2] = ILLEGAL_REQUEST; - scmd->sense_buffer[12] = 0x20; - scmd->sense_buffer[13] = 0; + scsi_build_sense(scmd, 0, ILLEGAL_REQUEST, + 0x20, 0); } break; case MPI2_IOCSTATUS_SCSI_DATA_OVERRUN: scsi_set_resid(scmd, 0); - /* fall through */ + fallthrough; case MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR: case MPI2_IOCSTATUS_SUCCESS: scmd->result = (DID_OK << 16) | scsi_status; @@ -5377,11 +5933,634 @@ _scsih_io_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) scsi_dma_unmap(scmd); mpt3sas_base_free_smid(ioc, smid); - scmd->scsi_done(scmd); + scsi_done(scmd); return 0; } /** + * _scsih_update_vphys_after_reset - update the Port's + * vphys_list after reset + * @ioc: per adapter object + * + * Returns nothing. + */ +static void +_scsih_update_vphys_after_reset(struct MPT3SAS_ADAPTER *ioc) +{ + u16 sz, ioc_status; + int i; + Mpi2ConfigReply_t mpi_reply; + Mpi2SasIOUnitPage0_t *sas_iounit_pg0 = NULL; + u16 attached_handle; + u64 attached_sas_addr; + u8 found = 0, port_id; + Mpi2SasPhyPage0_t phy_pg0; + struct hba_port *port, *port_next, *mport; + struct virtual_phy *vphy, *vphy_next; + struct _sas_device *sas_device; + + /* + * Mark all the vphys objects as dirty. + */ + list_for_each_entry_safe(port, port_next, + &ioc->port_table_list, list) { + if (!port->vphys_mask) + continue; + list_for_each_entry_safe(vphy, vphy_next, + &port->vphys_list, list) { + vphy->flags |= MPT_VPHY_FLAG_DIRTY_PHY; + } + } + + /* + * Read SASIOUnitPage0 to get each HBA Phy's data. + */ + sz = struct_size(sas_iounit_pg0, PhyData, ioc->sas_hba.num_phys); + sas_iounit_pg0 = kzalloc(sz, GFP_KERNEL); + if (!sas_iounit_pg0) { + ioc_err(ioc, "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + return; + } + if ((mpt3sas_config_get_sas_iounit_pg0(ioc, &mpi_reply, + sas_iounit_pg0, sz)) != 0) + goto out; + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) + goto out; + /* + * Loop over each HBA Phy. + */ + for (i = 0; i < ioc->sas_hba.num_phys; i++) { + /* + * Check whether Phy's Negotiation Link Rate is > 1.5G or not. + */ + if ((sas_iounit_pg0->PhyData[i].NegotiatedLinkRate >> 4) < + MPI2_SAS_NEG_LINK_RATE_1_5) + continue; + /* + * Check whether Phy is connected to SEP device or not, + * if it is SEP device then read the Phy's SASPHYPage0 data to + * determine whether Phy is a virtual Phy or not. if it is + * virtual phy then it is conformed that the attached remote + * device is a HBA's vSES device. + */ + if (!(le32_to_cpu( + sas_iounit_pg0->PhyData[i].ControllerPhyDeviceInfo) & + MPI2_SAS_DEVICE_INFO_SEP)) + continue; + + if ((mpt3sas_config_get_phy_pg0(ioc, &mpi_reply, &phy_pg0, + i))) { + ioc_err(ioc, "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + continue; + } + + if (!(le32_to_cpu(phy_pg0.PhyInfo) & + MPI2_SAS_PHYINFO_VIRTUAL_PHY)) + continue; + /* + * Get the vSES device's SAS Address. + */ + attached_handle = le16_to_cpu( + sas_iounit_pg0->PhyData[i].AttachedDevHandle); + if (_scsih_get_sas_address(ioc, attached_handle, + &attached_sas_addr) != 0) { + ioc_err(ioc, "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + continue; + } + + found = 0; + port = port_next = NULL; + /* + * Loop over each virtual_phy object from + * each port's vphys_list. + */ + list_for_each_entry_safe(port, + port_next, &ioc->port_table_list, list) { + if (!port->vphys_mask) + continue; + list_for_each_entry_safe(vphy, vphy_next, + &port->vphys_list, list) { + /* + * Continue with next virtual_phy object + * if the object is not marked as dirty. + */ + if (!(vphy->flags & MPT_VPHY_FLAG_DIRTY_PHY)) + continue; + + /* + * Continue with next virtual_phy object + * if the object's SAS Address is not equals + * to current Phy's vSES device SAS Address. + */ + if (vphy->sas_address != attached_sas_addr) + continue; + /* + * Enable current Phy number bit in object's + * phy_mask field. + */ + if (!(vphy->phy_mask & (1 << i))) + vphy->phy_mask = (1 << i); + /* + * Get hba_port object from hba_port table + * corresponding to current phy's Port ID. + * if there is no hba_port object corresponding + * to Phy's Port ID then create a new hba_port + * object & add to hba_port table. + */ + port_id = sas_iounit_pg0->PhyData[i].Port; + mport = mpt3sas_get_port_by_id(ioc, port_id, 1); + if (!mport) { + mport = kzalloc( + sizeof(struct hba_port), GFP_KERNEL); + if (!mport) + break; + mport->port_id = port_id; + ioc_info(ioc, + "%s: hba_port entry: %p, port: %d is added to hba_port list\n", + __func__, mport, mport->port_id); + list_add_tail(&mport->list, + &ioc->port_table_list); + } + /* + * If mport & port pointers are not pointing to + * same hba_port object then it means that vSES + * device's Port ID got changed after reset and + * hence move current virtual_phy object from + * port's vphys_list to mport's vphys_list. + */ + if (port != mport) { + if (!mport->vphys_mask) + INIT_LIST_HEAD( + &mport->vphys_list); + mport->vphys_mask |= (1 << i); + port->vphys_mask &= ~(1 << i); + list_move(&vphy->list, + &mport->vphys_list); + sas_device = mpt3sas_get_sdev_by_addr( + ioc, attached_sas_addr, port); + if (sas_device) + sas_device->port = mport; + } + /* + * Earlier while updating the hba_port table, + * it is determined that there is no other + * direct attached device with mport's Port ID, + * Hence mport was marked as dirty. Only vSES + * device has this Port ID, so unmark the mport + * as dirt. + */ + if (mport->flags & HBA_PORT_FLAG_DIRTY_PORT) { + mport->sas_address = 0; + mport->phy_mask = 0; + mport->flags &= + ~HBA_PORT_FLAG_DIRTY_PORT; + } + /* + * Unmark current virtual_phy object as dirty. + */ + vphy->flags &= ~MPT_VPHY_FLAG_DIRTY_PHY; + found = 1; + break; + } + if (found) + break; + } + } +out: + kfree(sas_iounit_pg0); +} + +/** + * _scsih_get_port_table_after_reset - Construct temporary port table + * @ioc: per adapter object + * @port_table: address where port table needs to be constructed + * + * return number of HBA port entries available after reset. + */ +static int +_scsih_get_port_table_after_reset(struct MPT3SAS_ADAPTER *ioc, + struct hba_port *port_table) +{ + u16 sz, ioc_status; + int i, j; + Mpi2ConfigReply_t mpi_reply; + Mpi2SasIOUnitPage0_t *sas_iounit_pg0 = NULL; + u16 attached_handle; + u64 attached_sas_addr; + u8 found = 0, port_count = 0, port_id; + + sz = struct_size(sas_iounit_pg0, PhyData, ioc->sas_hba.num_phys); + sas_iounit_pg0 = kzalloc(sz, GFP_KERNEL); + if (!sas_iounit_pg0) { + ioc_err(ioc, "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + return port_count; + } + + if ((mpt3sas_config_get_sas_iounit_pg0(ioc, &mpi_reply, + sas_iounit_pg0, sz)) != 0) + goto out; + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) + goto out; + for (i = 0; i < ioc->sas_hba.num_phys; i++) { + found = 0; + if ((sas_iounit_pg0->PhyData[i].NegotiatedLinkRate >> 4) < + MPI2_SAS_NEG_LINK_RATE_1_5) + continue; + attached_handle = + le16_to_cpu(sas_iounit_pg0->PhyData[i].AttachedDevHandle); + if (_scsih_get_sas_address( + ioc, attached_handle, &attached_sas_addr) != 0) { + ioc_err(ioc, "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + continue; + } + + for (j = 0; j < port_count; j++) { + port_id = sas_iounit_pg0->PhyData[i].Port; + if (port_table[j].port_id == port_id && + port_table[j].sas_address == attached_sas_addr) { + port_table[j].phy_mask |= (1 << i); + found = 1; + break; + } + } + + if (found) + continue; + + port_id = sas_iounit_pg0->PhyData[i].Port; + port_table[port_count].port_id = port_id; + port_table[port_count].phy_mask = (1 << i); + port_table[port_count].sas_address = attached_sas_addr; + port_count++; + } +out: + kfree(sas_iounit_pg0); + return port_count; +} + +enum hba_port_matched_codes { + NOT_MATCHED = 0, + MATCHED_WITH_ADDR_AND_PHYMASK, + MATCHED_WITH_ADDR_SUBPHYMASK_AND_PORT, + MATCHED_WITH_ADDR_AND_SUBPHYMASK, + MATCHED_WITH_ADDR, +}; + +/** + * _scsih_look_and_get_matched_port_entry - Get matched hba port entry + * from HBA port table + * @ioc: per adapter object + * @port_entry: hba port entry from temporary port table which needs to be + * searched for matched entry in the HBA port table + * @matched_port_entry: save matched hba port entry here + * @count: count of matched entries + * + * return type of matched entry found. + */ +static enum hba_port_matched_codes +_scsih_look_and_get_matched_port_entry(struct MPT3SAS_ADAPTER *ioc, + struct hba_port *port_entry, + struct hba_port **matched_port_entry, int *count) +{ + struct hba_port *port_table_entry, *matched_port = NULL; + enum hba_port_matched_codes matched_code = NOT_MATCHED; + int lcount = 0; + *matched_port_entry = NULL; + + list_for_each_entry(port_table_entry, &ioc->port_table_list, list) { + if (!(port_table_entry->flags & HBA_PORT_FLAG_DIRTY_PORT)) + continue; + + if ((port_table_entry->sas_address == port_entry->sas_address) + && (port_table_entry->phy_mask == port_entry->phy_mask)) { + matched_code = MATCHED_WITH_ADDR_AND_PHYMASK; + matched_port = port_table_entry; + break; + } + + if ((port_table_entry->sas_address == port_entry->sas_address) + && (port_table_entry->phy_mask & port_entry->phy_mask) + && (port_table_entry->port_id == port_entry->port_id)) { + matched_code = MATCHED_WITH_ADDR_SUBPHYMASK_AND_PORT; + matched_port = port_table_entry; + continue; + } + + if ((port_table_entry->sas_address == port_entry->sas_address) + && (port_table_entry->phy_mask & port_entry->phy_mask)) { + if (matched_code == + MATCHED_WITH_ADDR_SUBPHYMASK_AND_PORT) + continue; + matched_code = MATCHED_WITH_ADDR_AND_SUBPHYMASK; + matched_port = port_table_entry; + continue; + } + + if (port_table_entry->sas_address == port_entry->sas_address) { + if (matched_code == + MATCHED_WITH_ADDR_SUBPHYMASK_AND_PORT) + continue; + if (matched_code == MATCHED_WITH_ADDR_AND_SUBPHYMASK) + continue; + matched_code = MATCHED_WITH_ADDR; + matched_port = port_table_entry; + lcount++; + } + } + + *matched_port_entry = matched_port; + if (matched_code == MATCHED_WITH_ADDR) + *count = lcount; + return matched_code; +} + +/** + * _scsih_del_phy_part_of_anther_port - remove phy if it + * is a part of anther port + *@ioc: per adapter object + *@port_table: port table after reset + *@index: hba port entry index + *@port_count: number of ports available after host reset + *@offset: HBA phy bit offset + * + */ +static void +_scsih_del_phy_part_of_anther_port(struct MPT3SAS_ADAPTER *ioc, + struct hba_port *port_table, + int index, u8 port_count, int offset) +{ + struct _sas_node *sas_node = &ioc->sas_hba; + u32 i, found = 0; + + for (i = 0; i < port_count; i++) { + if (i == index) + continue; + + if (port_table[i].phy_mask & (1 << offset)) { + mpt3sas_transport_del_phy_from_an_existing_port( + ioc, sas_node, &sas_node->phy[offset]); + found = 1; + break; + } + } + if (!found) + port_table[index].phy_mask |= (1 << offset); +} + +/** + * _scsih_add_or_del_phys_from_existing_port - add/remove phy to/from + * right port + *@ioc: per adapter object + *@hba_port_entry: hba port table entry + *@port_table: temporary port table + *@index: hba port entry index + *@port_count: number of ports available after host reset + * + */ +static void +_scsih_add_or_del_phys_from_existing_port(struct MPT3SAS_ADAPTER *ioc, + struct hba_port *hba_port_entry, struct hba_port *port_table, + int index, int port_count) +{ + u32 phy_mask, offset = 0; + struct _sas_node *sas_node = &ioc->sas_hba; + + phy_mask = hba_port_entry->phy_mask ^ port_table[index].phy_mask; + + for (offset = 0; offset < ioc->sas_hba.num_phys; offset++) { + if (phy_mask & (1 << offset)) { + if (!(port_table[index].phy_mask & (1 << offset))) { + _scsih_del_phy_part_of_anther_port( + ioc, port_table, index, port_count, + offset); + continue; + } + if (sas_node->phy[offset].phy_belongs_to_port) + mpt3sas_transport_del_phy_from_an_existing_port( + ioc, sas_node, &sas_node->phy[offset]); + mpt3sas_transport_add_phy_to_an_existing_port( + ioc, sas_node, &sas_node->phy[offset], + hba_port_entry->sas_address, + hba_port_entry); + } + } +} + +/** + * _scsih_del_dirty_vphy - delete virtual_phy objects marked as dirty. + * @ioc: per adapter object + * + * Returns nothing. + */ +static void +_scsih_del_dirty_vphy(struct MPT3SAS_ADAPTER *ioc) +{ + struct hba_port *port, *port_next; + struct virtual_phy *vphy, *vphy_next; + + list_for_each_entry_safe(port, port_next, + &ioc->port_table_list, list) { + if (!port->vphys_mask) + continue; + list_for_each_entry_safe(vphy, vphy_next, + &port->vphys_list, list) { + if (vphy->flags & MPT_VPHY_FLAG_DIRTY_PHY) { + drsprintk(ioc, ioc_info(ioc, + "Deleting vphy %p entry from port id: %d\t, Phy_mask 0x%08x\n", + vphy, port->port_id, + vphy->phy_mask)); + port->vphys_mask &= ~vphy->phy_mask; + list_del(&vphy->list); + kfree(vphy); + } + } + if (!port->vphys_mask && !port->sas_address) + port->flags |= HBA_PORT_FLAG_DIRTY_PORT; + } +} + +/** + * _scsih_del_dirty_port_entries - delete dirty port entries from port list + * after host reset + *@ioc: per adapter object + * + */ +static void +_scsih_del_dirty_port_entries(struct MPT3SAS_ADAPTER *ioc) +{ + struct hba_port *port, *port_next; + + list_for_each_entry_safe(port, port_next, + &ioc->port_table_list, list) { + if (!(port->flags & HBA_PORT_FLAG_DIRTY_PORT) || + port->flags & HBA_PORT_FLAG_NEW_PORT) + continue; + + drsprintk(ioc, ioc_info(ioc, + "Deleting port table entry %p having Port: %d\t Phy_mask 0x%08x\n", + port, port->port_id, port->phy_mask)); + list_del(&port->list); + kfree(port); + } +} + +/** + * _scsih_sas_port_refresh - Update HBA port table after host reset + * @ioc: per adapter object + */ +static void +_scsih_sas_port_refresh(struct MPT3SAS_ADAPTER *ioc) +{ + u32 port_count = 0; + struct hba_port *port_table; + struct hba_port *port_table_entry; + struct hba_port *port_entry = NULL; + int i, j, count = 0, lcount = 0; + int ret; + u64 sas_addr; + u8 num_phys; + + drsprintk(ioc, ioc_info(ioc, + "updating ports for sas_host(0x%016llx)\n", + (unsigned long long)ioc->sas_hba.sas_address)); + + mpt3sas_config_get_number_hba_phys(ioc, &num_phys); + if (!num_phys) { + ioc_err(ioc, "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + return; + } + + if (num_phys > ioc->sas_hba.nr_phys_allocated) { + ioc_err(ioc, "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + return; + } + ioc->sas_hba.num_phys = num_phys; + + port_table = kcalloc(ioc->sas_hba.num_phys, + sizeof(struct hba_port), GFP_KERNEL); + if (!port_table) + return; + + port_count = _scsih_get_port_table_after_reset(ioc, port_table); + if (!port_count) + return; + + drsprintk(ioc, ioc_info(ioc, "New Port table\n")); + for (j = 0; j < port_count; j++) + drsprintk(ioc, ioc_info(ioc, + "Port: %d\t Phy_mask 0x%08x\t sas_addr(0x%016llx)\n", + port_table[j].port_id, + port_table[j].phy_mask, port_table[j].sas_address)); + + list_for_each_entry(port_table_entry, &ioc->port_table_list, list) + port_table_entry->flags |= HBA_PORT_FLAG_DIRTY_PORT; + + drsprintk(ioc, ioc_info(ioc, "Old Port table\n")); + port_table_entry = NULL; + list_for_each_entry(port_table_entry, &ioc->port_table_list, list) { + drsprintk(ioc, ioc_info(ioc, + "Port: %d\t Phy_mask 0x%08x\t sas_addr(0x%016llx)\n", + port_table_entry->port_id, + port_table_entry->phy_mask, + port_table_entry->sas_address)); + } + + for (j = 0; j < port_count; j++) { + ret = _scsih_look_and_get_matched_port_entry(ioc, + &port_table[j], &port_entry, &count); + if (!port_entry) { + drsprintk(ioc, ioc_info(ioc, + "No Matched entry for sas_addr(0x%16llx), Port:%d\n", + port_table[j].sas_address, + port_table[j].port_id)); + continue; + } + + switch (ret) { + case MATCHED_WITH_ADDR_SUBPHYMASK_AND_PORT: + case MATCHED_WITH_ADDR_AND_SUBPHYMASK: + _scsih_add_or_del_phys_from_existing_port(ioc, + port_entry, port_table, j, port_count); + break; + case MATCHED_WITH_ADDR: + sas_addr = port_table[j].sas_address; + for (i = 0; i < port_count; i++) { + if (port_table[i].sas_address == sas_addr) + lcount++; + } + + if (count > 1 || lcount > 1) + port_entry = NULL; + else + _scsih_add_or_del_phys_from_existing_port(ioc, + port_entry, port_table, j, port_count); + } + + if (!port_entry) + continue; + + if (port_entry->port_id != port_table[j].port_id) + port_entry->port_id = port_table[j].port_id; + port_entry->flags &= ~HBA_PORT_FLAG_DIRTY_PORT; + port_entry->phy_mask = port_table[j].phy_mask; + } + + port_table_entry = NULL; +} + +/** + * _scsih_alloc_vphy - allocate virtual_phy object + * @ioc: per adapter object + * @port_id: Port ID number + * @phy_num: HBA Phy number + * + * Returns allocated virtual_phy object. + */ +static struct virtual_phy * +_scsih_alloc_vphy(struct MPT3SAS_ADAPTER *ioc, u8 port_id, u8 phy_num) +{ + struct virtual_phy *vphy; + struct hba_port *port; + + port = mpt3sas_get_port_by_id(ioc, port_id, 0); + if (!port) + return NULL; + + vphy = mpt3sas_get_vphy_by_phy(ioc, port, phy_num); + if (!vphy) { + vphy = kzalloc(sizeof(struct virtual_phy), GFP_KERNEL); + if (!vphy) + return NULL; + + if (!port->vphys_mask) + INIT_LIST_HEAD(&port->vphys_list); + + /* + * Enable bit corresponding to HBA phy number on its + * parent hba_port object's vphys_mask field. + */ + port->vphys_mask |= (1 << phy_num); + vphy->phy_mask |= (1 << phy_num); + + list_add_tail(&vphy->list, &port->vphys_list); + + ioc_info(ioc, + "vphy entry: %p, port id: %d, phy:%d is added to port's vphys_list\n", + vphy, port->port_id, phy_num); + } + return vphy; +} + +/** * _scsih_sas_host_refresh - refreshing sas host object contents * @ioc: per adapter object * Context: user @@ -5399,14 +6578,15 @@ _scsih_sas_host_refresh(struct MPT3SAS_ADAPTER *ioc) Mpi2ConfigReply_t mpi_reply; Mpi2SasIOUnitPage0_t *sas_iounit_pg0 = NULL; u16 attached_handle; - u8 link_rate; + u8 link_rate, port_id; + struct hba_port *port; + Mpi2SasPhyPage0_t phy_pg0; dtmprintk(ioc, ioc_info(ioc, "updating handles for sas_host(0x%016llx)\n", (u64)ioc->sas_hba.sas_address)); - sz = offsetof(Mpi2SasIOUnitPage0_t, PhyData) + (ioc->sas_hba.num_phys - * sizeof(Mpi2SasIOUnit0PhyData_t)); + sz = struct_size(sas_iounit_pg0, PhyData, ioc->sas_hba.num_phys); sas_iounit_pg0 = kzalloc(sz, GFP_KERNEL); if (!sas_iounit_pg0) { ioc_err(ioc, "failure at %s:%d/%s()!\n", @@ -5423,15 +6603,94 @@ _scsih_sas_host_refresh(struct MPT3SAS_ADAPTER *ioc) for (i = 0; i < ioc->sas_hba.num_phys ; i++) { link_rate = sas_iounit_pg0->PhyData[i].NegotiatedLinkRate >> 4; if (i == 0) - ioc->sas_hba.handle = le16_to_cpu(sas_iounit_pg0-> - PhyData[0].ControllerDevHandle); + ioc->sas_hba.handle = le16_to_cpu( + sas_iounit_pg0->PhyData[0].ControllerDevHandle); + port_id = sas_iounit_pg0->PhyData[i].Port; + if (!(mpt3sas_get_port_by_id(ioc, port_id, 0))) { + port = kzalloc(sizeof(struct hba_port), GFP_KERNEL); + if (!port) + goto out; + + port->port_id = port_id; + ioc_info(ioc, + "hba_port entry: %p, port: %d is added to hba_port list\n", + port, port->port_id); + if (ioc->shost_recovery) + port->flags = HBA_PORT_FLAG_NEW_PORT; + list_add_tail(&port->list, &ioc->port_table_list); + } + /* + * Check whether current Phy belongs to HBA vSES device or not. + */ + if (le32_to_cpu(sas_iounit_pg0->PhyData[i].ControllerPhyDeviceInfo) & + MPI2_SAS_DEVICE_INFO_SEP && + (link_rate >= MPI2_SAS_NEG_LINK_RATE_1_5)) { + if ((mpt3sas_config_get_phy_pg0(ioc, &mpi_reply, + &phy_pg0, i))) { + ioc_err(ioc, + "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + goto out; + } + if (!(le32_to_cpu(phy_pg0.PhyInfo) & + MPI2_SAS_PHYINFO_VIRTUAL_PHY)) + continue; + /* + * Allocate a virtual_phy object for vSES device, if + * this vSES device is hot added. + */ + if (!_scsih_alloc_vphy(ioc, port_id, i)) + goto out; + ioc->sas_hba.phy[i].hba_vphy = 1; + } + + /* + * Add new HBA phys to STL if these new phys got added as part + * of HBA Firmware upgrade/downgrade operation. + */ + if (!ioc->sas_hba.phy[i].phy) { + if ((mpt3sas_config_get_phy_pg0(ioc, &mpi_reply, + &phy_pg0, i))) { + ioc_err(ioc, "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + continue; + } + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + ioc_err(ioc, "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + continue; + } + ioc->sas_hba.phy[i].phy_id = i; + mpt3sas_transport_add_host_phy(ioc, + &ioc->sas_hba.phy[i], phy_pg0, + ioc->sas_hba.parent_dev); + continue; + } ioc->sas_hba.phy[i].handle = ioc->sas_hba.handle; attached_handle = le16_to_cpu(sas_iounit_pg0->PhyData[i]. AttachedDevHandle); if (attached_handle && link_rate < MPI2_SAS_NEG_LINK_RATE_1_5) link_rate = MPI2_SAS_NEG_LINK_RATE_1_5; + ioc->sas_hba.phy[i].port = + mpt3sas_get_port_by_id(ioc, port_id, 0); mpt3sas_transport_update_links(ioc, ioc->sas_hba.sas_address, - attached_handle, i, link_rate); + attached_handle, i, link_rate, + ioc->sas_hba.phy[i].port); + } + /* + * Clear the phy details if this phy got disabled as part of + * HBA Firmware upgrade/downgrade operation. + */ + for (i = ioc->sas_hba.num_phys; + i < ioc->sas_hba.nr_phys_allocated; i++) { + if (ioc->sas_hba.phy[i].phy && + ioc->sas_hba.phy[i].phy->negotiated_linkrate >= + SAS_LINK_RATE_1_5_GBPS) + mpt3sas_transport_update_links(ioc, + ioc->sas_hba.sas_address, 0, i, + MPI2_SAS_NEG_LINK_RATE_PHY_DISABLED, NULL); } out: kfree(sas_iounit_pg0); @@ -5456,7 +6715,8 @@ _scsih_sas_host_add(struct MPT3SAS_ADAPTER *ioc) u16 ioc_status; u16 sz; u8 device_missing_delay; - u8 num_phys; + u8 num_phys, port_id; + struct hba_port *port; mpt3sas_config_get_number_hba_phys(ioc, &num_phys); if (!num_phys) { @@ -5464,7 +6724,10 @@ _scsih_sas_host_add(struct MPT3SAS_ADAPTER *ioc) __FILE__, __LINE__, __func__); return; } - ioc->sas_hba.phy = kcalloc(num_phys, + + ioc->sas_hba.nr_phys_allocated = max_t(u8, + MPT_MAX_HBA_NUM_PHYS, num_phys); + ioc->sas_hba.phy = kcalloc(ioc->sas_hba.nr_phys_allocated, sizeof(struct _sas_phy), GFP_KERNEL); if (!ioc->sas_hba.phy) { ioc_err(ioc, "failure at %s:%d/%s()!\n", @@ -5474,8 +6737,7 @@ _scsih_sas_host_add(struct MPT3SAS_ADAPTER *ioc) ioc->sas_hba.num_phys = num_phys; /* sas_iounit page 0 */ - sz = offsetof(Mpi2SasIOUnitPage0_t, PhyData) + (ioc->sas_hba.num_phys * - sizeof(Mpi2SasIOUnit0PhyData_t)); + sz = struct_size(sas_iounit_pg0, PhyData, ioc->sas_hba.num_phys); sas_iounit_pg0 = kzalloc(sz, GFP_KERNEL); if (!sas_iounit_pg0) { ioc_err(ioc, "failure at %s:%d/%s()!\n", @@ -5497,8 +6759,7 @@ _scsih_sas_host_add(struct MPT3SAS_ADAPTER *ioc) } /* sas_iounit page 1 */ - sz = offsetof(Mpi2SasIOUnitPage1_t, PhyData) + (ioc->sas_hba.num_phys * - sizeof(Mpi2SasIOUnit1PhyData_t)); + sz = struct_size(sas_iounit_pg1, PhyData, ioc->sas_hba.num_phys); sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL); if (!sas_iounit_pg1) { ioc_err(ioc, "failure at %s:%d/%s()!\n", @@ -5549,8 +6810,40 @@ _scsih_sas_host_add(struct MPT3SAS_ADAPTER *ioc) if (i == 0) ioc->sas_hba.handle = le16_to_cpu(sas_iounit_pg0-> PhyData[0].ControllerDevHandle); + + port_id = sas_iounit_pg0->PhyData[i].Port; + if (!(mpt3sas_get_port_by_id(ioc, port_id, 0))) { + port = kzalloc(sizeof(struct hba_port), GFP_KERNEL); + if (!port) + goto out; + + port->port_id = port_id; + ioc_info(ioc, + "hba_port entry: %p, port: %d is added to hba_port list\n", + port, port->port_id); + list_add_tail(&port->list, + &ioc->port_table_list); + } + + /* + * Check whether current Phy belongs to HBA vSES device or not. + */ + if ((le32_to_cpu(phy_pg0.PhyInfo) & + MPI2_SAS_PHYINFO_VIRTUAL_PHY) && + (phy_pg0.NegotiatedLinkRate >> 4) >= + MPI2_SAS_NEG_LINK_RATE_1_5) { + /* + * Allocate a virtual_phy object for vSES device. + */ + if (!_scsih_alloc_vphy(ioc, port_id, i)) + goto out; + ioc->sas_hba.phy[i].hba_vphy = 1; + } + ioc->sas_hba.phy[i].handle = ioc->sas_hba.handle; ioc->sas_hba.phy[i].phy_id = i; + ioc->sas_hba.phy[i].port = + mpt3sas_get_port_by_id(ioc, port_id, 0); mpt3sas_transport_add_host_phy(ioc, &ioc->sas_hba.phy[i], phy_pg0, ioc->sas_hba.parent_dev); } @@ -5604,6 +6897,7 @@ _scsih_expander_add(struct MPT3SAS_ADAPTER *ioc, u16 handle) int i; unsigned long flags; struct _sas_port *mpt3sas_port = NULL; + u8 port_id; int rc = 0; @@ -5636,10 +6930,13 @@ _scsih_expander_add(struct MPT3SAS_ADAPTER *ioc, u16 handle) __FILE__, __LINE__, __func__); return -1; } + + port_id = expander_pg0.PhysicalPort; if (sas_address_parent != ioc->sas_hba.sas_address) { spin_lock_irqsave(&ioc->sas_node_lock, flags); sas_expander = mpt3sas_scsih_expander_find_by_sas_address(ioc, - sas_address_parent); + sas_address_parent, + mpt3sas_get_port_by_id(ioc, port_id, 0)); spin_unlock_irqrestore(&ioc->sas_node_lock, flags); if (!sas_expander) { rc = _scsih_expander_add(ioc, parent_handle); @@ -5651,7 +6948,7 @@ _scsih_expander_add(struct MPT3SAS_ADAPTER *ioc, u16 handle) spin_lock_irqsave(&ioc->sas_node_lock, flags); sas_address = le64_to_cpu(expander_pg0.SASAddress); sas_expander = mpt3sas_scsih_expander_find_by_sas_address(ioc, - sas_address); + sas_address, mpt3sas_get_port_by_id(ioc, port_id, 0)); spin_unlock_irqrestore(&ioc->sas_node_lock, flags); if (sas_expander) @@ -5669,13 +6966,22 @@ _scsih_expander_add(struct MPT3SAS_ADAPTER *ioc, u16 handle) sas_expander->num_phys = expander_pg0.NumPhys; sas_expander->sas_address_parent = sas_address_parent; sas_expander->sas_address = sas_address; + sas_expander->port = mpt3sas_get_port_by_id(ioc, port_id, 0); + if (!sas_expander->port) { + ioc_err(ioc, "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + rc = -1; + goto out_fail; + } ioc_info(ioc, "expander_add: handle(0x%04x), parent(0x%04x), sas_addr(0x%016llx), phys(%d)\n", handle, parent_handle, (u64)sas_expander->sas_address, sas_expander->num_phys); - if (!sas_expander->num_phys) + if (!sas_expander->num_phys) { + rc = -1; goto out_fail; + } sas_expander->phy = kcalloc(sas_expander->num_phys, sizeof(struct _sas_phy), GFP_KERNEL); if (!sas_expander->phy) { @@ -5687,7 +6993,7 @@ _scsih_expander_add(struct MPT3SAS_ADAPTER *ioc, u16 handle) INIT_LIST_HEAD(&sas_expander->sas_port_list); mpt3sas_port = mpt3sas_transport_port_add(ioc, handle, - sas_address_parent); + sas_address_parent, sas_expander->port); if (!mpt3sas_port) { ioc_err(ioc, "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); @@ -5695,6 +7001,7 @@ _scsih_expander_add(struct MPT3SAS_ADAPTER *ioc, u16 handle) goto out_fail; } sas_expander->parent_dev = &mpt3sas_port->rphy->dev; + sas_expander->rphy = mpt3sas_port->rphy; for (i = 0 ; i < sas_expander->num_phys ; i++) { if ((mpt3sas_config_get_expander_pg1(ioc, &mpi_reply, @@ -5706,6 +7013,8 @@ _scsih_expander_add(struct MPT3SAS_ADAPTER *ioc, u16 handle) } sas_expander->phy[i].handle = handle; sas_expander->phy[i].phy_id = i; + sas_expander->phy[i].port = + mpt3sas_get_port_by_id(ioc, port_id, 0); if ((mpt3sas_transport_add_expander_phy(ioc, &sas_expander->phy[i], expander_pg1, @@ -5733,7 +7042,7 @@ _scsih_expander_add(struct MPT3SAS_ADAPTER *ioc, u16 handle) if (mpt3sas_port) mpt3sas_transport_port_remove(ioc, sas_expander->sas_address, - sas_address_parent); + sas_address_parent, sas_expander->port); kfree(sas_expander); return rc; } @@ -5742,9 +7051,11 @@ _scsih_expander_add(struct MPT3SAS_ADAPTER *ioc, u16 handle) * mpt3sas_expander_remove - removing expander object * @ioc: per adapter object * @sas_address: expander sas_address + * @port: hba port entry */ void -mpt3sas_expander_remove(struct MPT3SAS_ADAPTER *ioc, u64 sas_address) +mpt3sas_expander_remove(struct MPT3SAS_ADAPTER *ioc, u64 sas_address, + struct hba_port *port) { struct _sas_node *sas_expander; unsigned long flags; @@ -5752,9 +7063,12 @@ mpt3sas_expander_remove(struct MPT3SAS_ADAPTER *ioc, u64 sas_address) if (ioc->shost_recovery) return; + if (!port) + return; + spin_lock_irqsave(&ioc->sas_node_lock, flags); sas_expander = mpt3sas_scsih_expander_find_by_sas_address(ioc, - sas_address); + sas_address, port); spin_unlock_irqrestore(&ioc->sas_node_lock, flags); if (sas_expander) _scsih_expander_node_remove(ioc, sas_expander); @@ -5877,7 +7191,7 @@ _scsih_check_device(struct MPT3SAS_ADAPTER *ioc, { Mpi2ConfigReply_t mpi_reply; Mpi2SasDevicePage0_t sas_device_pg0; - struct _sas_device *sas_device; + struct _sas_device *sas_device = NULL; struct _enclosure_node *enclosure_dev = NULL; u32 ioc_status; unsigned long flags; @@ -5885,6 +7199,7 @@ _scsih_check_device(struct MPT3SAS_ADAPTER *ioc, struct scsi_target *starget; struct MPT3SAS_TARGET *sas_target_priv_data; u32 device_info; + struct hba_port *port; if ((mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) @@ -5907,8 +7222,11 @@ _scsih_check_device(struct MPT3SAS_ADAPTER *ioc, spin_lock_irqsave(&ioc->sas_device_lock, flags); sas_address = le64_to_cpu(sas_device_pg0.SASAddress); + port = mpt3sas_get_port_by_id(ioc, sas_device_pg0.PhysicalPort, 0); + if (!port) + goto out_unlock; sas_device = __mpt3sas_get_sdev_by_addr(ioc, - sas_address); + sas_address, port); if (!sas_device) goto out_unlock; @@ -5964,7 +7282,7 @@ _scsih_check_device(struct MPT3SAS_ADAPTER *ioc, goto out_unlock; spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - _scsih_ublock_io_device(ioc, sas_address); + _scsih_ublock_io_device(ioc, sas_address, port); if (sas_device) sas_device_put(sas_device); @@ -5998,6 +7316,7 @@ _scsih_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u32 ioc_status; u64 sas_address; u32 device_info; + u8 port_id; if ((mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) { @@ -6034,8 +7353,9 @@ _scsih_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle, u8 phy_num, sas_device_pg0.AccessStatus)) return -1; + port_id = sas_device_pg0.PhysicalPort; sas_device = mpt3sas_get_sdev_by_addr(ioc, - sas_address); + sas_address, mpt3sas_get_port_by_id(ioc, port_id, 0)); if (sas_device) { clear_bit(handle, ioc->pend_os_device_add); sas_device_put(sas_device); @@ -6076,6 +7396,12 @@ _scsih_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle, u8 phy_num, sas_device->phy = sas_device_pg0.PhyNum; sas_device->fast_path = (le16_to_cpu(sas_device_pg0.Flags) & MPI25_SAS_DEVICE0_FLAGS_FAST_PATH_CAPABLE) ? 1 : 0; + sas_device->port = mpt3sas_get_port_by_id(ioc, port_id, 0); + if (!sas_device->port) { + ioc_err(ioc, "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + goto out; + } if (le16_to_cpu(sas_device_pg0.Flags) & MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID) { @@ -6103,12 +7429,17 @@ _scsih_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle, u8 phy_num, /* get device name */ sas_device->device_name = le64_to_cpu(sas_device_pg0.DeviceName); + sas_device->port_type = sas_device_pg0.MaxPortConnections; + ioc_info(ioc, + "handle(0x%0x) sas_address(0x%016llx) port_type(0x%0x)\n", + handle, sas_device->sas_address, sas_device->port_type); if (ioc->wait_for_discovery_to_complete) _scsih_sas_device_init_add(ioc, sas_device); else _scsih_sas_device_add(ioc, sas_device); +out: sas_device_put(sas_device); return 0; } @@ -6141,7 +7472,8 @@ _scsih_remove_device(struct MPT3SAS_ADAPTER *ioc, if (sas_device->starget && sas_device->starget->hostdata) { sas_target_priv_data = sas_device->starget->hostdata; sas_target_priv_data->deleted = 1; - _scsih_ublock_io_device(ioc, sas_device->sas_address); + _scsih_ublock_io_device(ioc, sas_device->sas_address, + sas_device->port); sas_target_priv_data->handle = MPT3SAS_INVALID_DEVICE_HANDLE; } @@ -6149,7 +7481,8 @@ _scsih_remove_device(struct MPT3SAS_ADAPTER *ioc, if (!ioc->hide_drives) mpt3sas_transport_port_remove(ioc, sas_device->sas_address, - sas_device->sas_address_parent); + sas_device->sas_address_parent, + sas_device->port); ioc_info(ioc, "removing handle(0x%04x), sas_addr(0x%016llx)\n", sas_device->handle, (u64)sas_device->sas_address); @@ -6260,6 +7593,7 @@ _scsih_sas_topology_change_event(struct MPT3SAS_ADAPTER *ioc, u64 sas_address; unsigned long flags; u8 link_rate, prev_link_rate; + struct hba_port *port; Mpi2EventDataSasTopologyChangeList_t *event_data = (Mpi2EventDataSasTopologyChangeList_t *) fw_event->event_data; @@ -6281,6 +7615,7 @@ _scsih_sas_topology_change_event(struct MPT3SAS_ADAPTER *ioc, } parent_handle = le16_to_cpu(event_data->ExpanderDevHandle); + port = mpt3sas_get_port_by_id(ioc, event_data->PhysicalPort, 0); /* handle expander add */ if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_ADDED) @@ -6293,6 +7628,7 @@ _scsih_sas_topology_change_event(struct MPT3SAS_ADAPTER *ioc, if (sas_expander) { sas_address = sas_expander->sas_address; max_phys = sas_expander->num_phys; + port = sas_expander->port; } else if (parent_handle < ioc->sas_hba.num_phys) { sas_address = ioc->sas_hba.sas_address; max_phys = ioc->sas_hba.num_phys; @@ -6335,7 +7671,7 @@ _scsih_sas_topology_change_event(struct MPT3SAS_ADAPTER *ioc, break; mpt3sas_transport_update_links(ioc, sas_address, - handle, phy_number, link_rate); + handle, phy_number, link_rate, port); if (link_rate < MPI2_SAS_NEG_LINK_RATE_1_5) break; @@ -6346,7 +7682,7 @@ _scsih_sas_topology_change_event(struct MPT3SAS_ADAPTER *ioc, if (!test_bit(handle, ioc->pend_os_device_add)) break; - /* fall through */ + fallthrough; case MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED: @@ -6354,7 +7690,7 @@ _scsih_sas_topology_change_event(struct MPT3SAS_ADAPTER *ioc, break; mpt3sas_transport_update_links(ioc, sas_address, - handle, phy_number, link_rate); + handle, phy_number, link_rate, port); _scsih_add_device(ioc, handle, phy_number, 0); @@ -6369,7 +7705,7 @@ _scsih_sas_topology_change_event(struct MPT3SAS_ADAPTER *ioc, /* handle expander removal */ if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING && sas_expander) - mpt3sas_expander_remove(ioc, sas_address); + mpt3sas_expander_remove(ioc, sas_address, port); return 0; } @@ -6443,24 +7779,17 @@ _scsih_sas_device_status_change_event_debug(struct MPT3SAS_ADAPTER *ioc, /** * _scsih_sas_device_status_change_event - handle device status change * @ioc: per adapter object - * @fw_event: The fw_event_work object + * @event_data: The fw event * Context: user. */ static void _scsih_sas_device_status_change_event(struct MPT3SAS_ADAPTER *ioc, - struct fw_event_work *fw_event) + Mpi2EventDataSasDeviceStatusChange_t *event_data) { struct MPT3SAS_TARGET *target_priv_data; struct _sas_device *sas_device; u64 sas_address; unsigned long flags; - Mpi2EventDataSasDeviceStatusChange_t *event_data = - (Mpi2EventDataSasDeviceStatusChange_t *) - fw_event->event_data; - - if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) - _scsih_sas_device_status_change_event_debug(ioc, - event_data); /* In MPI Revision K (0xC), the internal device reset complete was * implemented, so avoid setting tm_busy flag for older firmware. @@ -6477,7 +7806,8 @@ _scsih_sas_device_status_change_event(struct MPT3SAS_ADAPTER *ioc, spin_lock_irqsave(&ioc->sas_device_lock, flags); sas_address = le64_to_cpu(event_data->SASAddress); sas_device = __mpt3sas_get_sdev_by_addr(ioc, - sas_address); + sas_address, + mpt3sas_get_port_by_id(ioc, event_data->PhysicalPort, 0)); if (!sas_device || !sas_device->starget) goto out; @@ -6492,6 +7822,12 @@ _scsih_sas_device_status_change_event(struct MPT3SAS_ADAPTER *ioc, else target_priv_data->tm_busy = 0; + if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) + ioc_info(ioc, + "%s tm_busy flag for handle(0x%04x)\n", + (target_priv_data->tm_busy == 1) ? "Enable" : "Disable", + target_priv_data->handle); + out: if (sas_device) sas_device_put(sas_device); @@ -6526,6 +7862,11 @@ _scsih_check_pcie_access_status(struct MPT3SAS_ADAPTER *ioc, u64 wwid, break; case MPI26_PCIEDEV0_ASTATUS_DEVICE_BLOCKED: desc = "PCIe device blocked"; + ioc_info(ioc, + "Device with Access Status (%s): wwid(0x%016llx), " + "handle(0x%04x)\n ll only be added to the internal list", + desc, (u64)wwid, handle); + rc = 0; break; case MPI26_PCIEDEV0_ASTATUS_MEMORY_SPACE_ACCESS_FAILED: desc = "PCIe device mem space access failed"; @@ -6615,7 +7956,7 @@ _scsih_pcie_device_remove_from_sml(struct MPT3SAS_ADAPTER *ioc, if (pcie_device->starget && pcie_device->starget->hostdata) { sas_target_priv_data = pcie_device->starget->hostdata; sas_target_priv_data->deleted = 1; - _scsih_ublock_io_device(ioc, pcie_device->wwid); + _scsih_ublock_io_device(ioc, pcie_device->wwid, NULL); sas_target_priv_data->handle = MPT3SAS_INVALID_DEVICE_HANDLE; } @@ -6630,7 +7971,8 @@ _scsih_pcie_device_remove_from_sml(struct MPT3SAS_ADAPTER *ioc, pcie_device->enclosure_level, pcie_device->connector_name); - if (pcie_device->starget) + if (pcie_device->starget && (pcie_device->access_status != + MPI26_PCIEDEV0_ASTATUS_DEVICE_BLOCKED)) scsi_remove_target(&pcie_device->starget->dev); dewtprintk(ioc, ioc_info(ioc, "%s: exit: handle(0x%04x), wwid(0x%016llx)\n", @@ -6681,7 +8023,7 @@ _scsih_pcie_check_device(struct MPT3SAS_ADAPTER *ioc, u16 handle) /* check if this is end device */ device_info = le32_to_cpu(pcie_device_pg0.DeviceInfo); - if (!(_scsih_is_nvme_device(device_info))) + if (!(_scsih_is_nvme_pciescsi_device(device_info))) return; wwid = le64_to_cpu(pcie_device_pg0.WWID); @@ -6696,6 +8038,7 @@ _scsih_pcie_check_device(struct MPT3SAS_ADAPTER *ioc, u16 handle) if (unlikely(pcie_device->handle != handle)) { starget = pcie_device->starget; sas_target_priv_data = starget->hostdata; + pcie_device->access_status = pcie_device_pg0.AccessStatus; starget_printk(KERN_INFO, starget, "handle changed from(0x%04x) to (0x%04x)!!!\n", pcie_device->handle, handle); @@ -6735,7 +8078,7 @@ _scsih_pcie_check_device(struct MPT3SAS_ADAPTER *ioc, u16 handle) spin_unlock_irqrestore(&ioc->pcie_device_lock, flags); pcie_device_put(pcie_device); - _scsih_ublock_io_device(ioc, wwid); + _scsih_ublock_io_device(ioc, wwid, NULL); return; } @@ -6790,7 +8133,8 @@ _scsih_pcie_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle) pcie_device_pg0.AccessStatus)) return 0; - if (!(_scsih_is_nvme_device(le32_to_cpu(pcie_device_pg0.DeviceInfo)))) + if (!(_scsih_is_nvme_pciescsi_device(le32_to_cpu + (pcie_device_pg0.DeviceInfo)))) return 0; pcie_device = mpt3sas_get_pdev_by_wwid(ioc, wwid); @@ -6800,6 +8144,31 @@ _scsih_pcie_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle) return 0; } + /* PCIe Device Page 2 contains read-only information about a + * specific NVMe device; therefore, this page is only + * valid for NVMe devices and skip for pcie devices of type scsi. + */ + if (!(mpt3sas_scsih_is_pcie_scsi_device( + le32_to_cpu(pcie_device_pg0.DeviceInfo)))) { + if (mpt3sas_config_get_pcie_device_pg2(ioc, &mpi_reply, + &pcie_device_pg2, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, + handle)) { + ioc_err(ioc, + "failure at %s:%d/%s()!\n", __FILE__, + __LINE__, __func__); + return 0; + } + + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + ioc_err(ioc, + "failure at %s:%d/%s()!\n", __FILE__, + __LINE__, __func__); + return 0; + } + } + pcie_device = kzalloc(sizeof(struct _pcie_device), GFP_KERNEL); if (!pcie_device) { ioc_err(ioc, "failure at %s:%d/%s()!\n", @@ -6811,6 +8180,7 @@ _scsih_pcie_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle) pcie_device->id = ioc->pcie_target_id++; pcie_device->channel = PCIE_CHANNEL; pcie_device->handle = handle; + pcie_device->access_status = pcie_device_pg0.AccessStatus; pcie_device->device_info = le32_to_cpu(pcie_device_pg0.DeviceInfo); pcie_device->wwid = wwid; pcie_device->port_num = pcie_device_pg0.PortNum; @@ -6842,27 +8212,26 @@ _scsih_pcie_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle) le64_to_cpu(enclosure_dev->pg0.EnclosureLogicalID); } /* TODO -- Add device name once FW supports it */ - if (mpt3sas_config_get_pcie_device_pg2(ioc, &mpi_reply, - &pcie_device_pg2, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle)) { - ioc_err(ioc, "failure at %s:%d/%s()!\n", - __FILE__, __LINE__, __func__); - kfree(pcie_device); - return 0; - } - - ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; - if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { - ioc_err(ioc, "failure at %s:%d/%s()!\n", - __FILE__, __LINE__, __func__); - kfree(pcie_device); - return 0; - } - pcie_device->nvme_mdts = - le32_to_cpu(pcie_device_pg2.MaximumDataTransferSize); - if (pcie_device_pg2.ControllerResetTO) - pcie_device->reset_timeout = - pcie_device_pg2.ControllerResetTO; - else + if (!(mpt3sas_scsih_is_pcie_scsi_device( + le32_to_cpu(pcie_device_pg0.DeviceInfo)))) { + pcie_device->nvme_mdts = + le32_to_cpu(pcie_device_pg2.MaximumDataTransferSize); + pcie_device->shutdown_latency = + le16_to_cpu(pcie_device_pg2.ShutdownLatency); + /* + * Set IOC's max_shutdown_latency to drive's RTD3 Entry Latency + * if drive's RTD3 Entry Latency is greater then IOC's + * max_shutdown_latency. + */ + if (pcie_device->shutdown_latency > ioc->max_shutdown_latency) + ioc->max_shutdown_latency = + pcie_device->shutdown_latency; + if (pcie_device_pg2.ControllerResetTO) + pcie_device->reset_timeout = + pcie_device_pg2.ControllerResetTO; + else + pcie_device->reset_timeout = 30; + } else pcie_device->reset_timeout = 30; if (ioc->wait_for_discovery_to_complete) @@ -7042,7 +8411,7 @@ _scsih_pcie_topology_change_event(struct MPT3SAS_ADAPTER *ioc, event_data->PortEntry[i].PortStatus &= 0xF0; event_data->PortEntry[i].PortStatus |= MPI26_EVENT_PCIE_TOPO_PS_DEV_ADDED; - /* fall through */ + fallthrough; case MPI26_EVENT_PCIE_TOPO_PS_DEV_ADDED: if (ioc->shost_recovery) break; @@ -7361,7 +8730,7 @@ _scsih_sas_broadcast_primitive_event(struct MPT3SAS_ADAPTER *ioc, goto out; spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); - r = mpt3sas_scsih_issue_tm(ioc, handle, lun, + r = mpt3sas_scsih_issue_tm(ioc, handle, 0, 0, lun, MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK, st->smid, st->msix_io, 30, 0); if (r == FAILED) { @@ -7402,9 +8771,9 @@ _scsih_sas_broadcast_primitive_event(struct MPT3SAS_ADAPTER *ioc, if (ioc->shost_recovery) goto out_no_lock; - r = mpt3sas_scsih_issue_tm(ioc, handle, sdev->lun, - MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK, st->smid, - st->msix_io, 30, 0); + r = mpt3sas_scsih_issue_tm(ioc, handle, sdev->channel, sdev->id, + sdev->lun, MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK, + st->smid, st->msix_io, 30, 0); if (r == FAILED || st->cb_idx != 0xFF) { sdev_printk(KERN_WARNING, sdev, "mpt3sas_scsih_issue_tm: ABORT_TASK: FAILED : " @@ -7589,14 +8958,13 @@ _scsih_ir_fastpath(struct MPT3SAS_ADAPTER *ioc, u16 handle, u8 phys_disk_num) handle, phys_disk_num)); init_completion(&ioc->scsih_cmds.done); - mpt3sas_base_put_smid_default(ioc, smid); + ioc->put_smid_default(ioc, smid); wait_for_completion_timeout(&ioc->scsih_cmds.done, 10*HZ); if (!(ioc->scsih_cmds.status & MPT3_CMD_COMPLETE)) { - issue_reset = - mpt3sas_base_check_cmd_timeout(ioc, - ioc->scsih_cmds.status, mpi_request, - sizeof(Mpi2RaidActionRequest_t)/4); + mpt3sas_check_cmd_timeout(ioc, + ioc->scsih_cmds.status, mpi_request, + sizeof(Mpi2RaidActionRequest_t)/4, issue_reset); rc = -EFAULT; goto out; } @@ -7879,7 +9247,9 @@ _scsih_sas_pd_add(struct MPT3SAS_ADAPTER *ioc, parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle); if (!_scsih_get_sas_address(ioc, parent_handle, &sas_address)) mpt3sas_transport_update_links(ioc, sas_address, handle, - sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5); + sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5, + mpt3sas_get_port_by_id(ioc, + sas_device_pg0.PhysicalPort, 0)); _scsih_ir_fastpath(ioc, handle, element->PhysDiskNum); _scsih_add_device(ioc, handle, 0, 1); @@ -8185,7 +9555,9 @@ _scsih_sas_ir_physical_disk_event(struct MPT3SAS_ADAPTER *ioc, parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle); if (!_scsih_get_sas_address(ioc, parent_handle, &sas_address)) mpt3sas_transport_update_links(ioc, sas_address, handle, - sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5); + sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5, + mpt3sas_get_port_by_id(ioc, + sas_device_pg0.PhysicalPort, 0)); _scsih_add_device(ioc, handle, 0, 1); @@ -8294,6 +9666,42 @@ _scsih_prep_device_scan(struct MPT3SAS_ADAPTER *ioc) } /** + * _scsih_update_device_qdepth - Update QD during Reset. + * @ioc: per adapter object + * + */ +static void +_scsih_update_device_qdepth(struct MPT3SAS_ADAPTER *ioc) +{ + struct MPT3SAS_DEVICE *sas_device_priv_data; + struct MPT3SAS_TARGET *sas_target_priv_data; + struct _sas_device *sas_device; + struct scsi_device *sdev; + u16 qdepth; + + ioc_info(ioc, "Update devices with firmware reported queue depth\n"); + shost_for_each_device(sdev, ioc->shost) { + sas_device_priv_data = sdev->hostdata; + if (sas_device_priv_data && sas_device_priv_data->sas_target) { + sas_target_priv_data = sas_device_priv_data->sas_target; + sas_device = sas_device_priv_data->sas_target->sas_dev; + if (sas_target_priv_data->flags & MPT_TARGET_FLAGS_PCIE_DEVICE) + qdepth = ioc->max_nvme_qd; + else if (sas_device && + sas_device->device_info & MPI2_SAS_DEVICE_INFO_SSP_TARGET) + qdepth = (sas_device->port_type > 1) ? + ioc->max_wideport_qd : ioc->max_narrowport_qd; + else if (sas_device && + sas_device->device_info & MPI2_SAS_DEVICE_INFO_SATA_DEVICE) + qdepth = ioc->max_sata_qd; + else + continue; + mpt3sas_scsih_change_queue_depth(sdev, qdepth); + } + } +} + +/** * _scsih_mark_responding_sas_device - mark a sas_devices as responding * @ioc: per adapter object * @sas_device_pg0: SAS Device page 0 @@ -8310,6 +9718,8 @@ Mpi2SasDevicePage0_t *sas_device_pg0) struct _sas_device *sas_device = NULL; struct _enclosure_node *enclosure_dev = NULL; unsigned long flags; + struct hba_port *port = mpt3sas_get_port_by_id( + ioc, sas_device_pg0->PhysicalPort, 0); if (sas_device_pg0->EnclosureHandle) { enclosure_dev = @@ -8321,69 +9731,71 @@ Mpi2SasDevicePage0_t *sas_device_pg0) } spin_lock_irqsave(&ioc->sas_device_lock, flags); list_for_each_entry(sas_device, &ioc->sas_device_list, list) { - if ((sas_device->sas_address == le64_to_cpu( - sas_device_pg0->SASAddress)) && (sas_device->slot == - le16_to_cpu(sas_device_pg0->Slot))) { - sas_device->responding = 1; - starget = sas_device->starget; - if (starget && starget->hostdata) { - sas_target_priv_data = starget->hostdata; - sas_target_priv_data->tm_busy = 0; - sas_target_priv_data->deleted = 0; - } else - sas_target_priv_data = NULL; - if (starget) { - starget_printk(KERN_INFO, starget, - "handle(0x%04x), sas_addr(0x%016llx)\n", - le16_to_cpu(sas_device_pg0->DevHandle), - (unsigned long long) - sas_device->sas_address); + if (sas_device->sas_address != le64_to_cpu( + sas_device_pg0->SASAddress)) + continue; + if (sas_device->slot != le16_to_cpu(sas_device_pg0->Slot)) + continue; + if (sas_device->port != port) + continue; + sas_device->responding = 1; + starget = sas_device->starget; + if (starget && starget->hostdata) { + sas_target_priv_data = starget->hostdata; + sas_target_priv_data->tm_busy = 0; + sas_target_priv_data->deleted = 0; + } else + sas_target_priv_data = NULL; + if (starget) { + starget_printk(KERN_INFO, starget, + "handle(0x%04x), sas_addr(0x%016llx)\n", + le16_to_cpu(sas_device_pg0->DevHandle), + (unsigned long long) + sas_device->sas_address); - if (sas_device->enclosure_handle != 0) - starget_printk(KERN_INFO, starget, - "enclosure logical id(0x%016llx)," - " slot(%d)\n", - (unsigned long long) - sas_device->enclosure_logical_id, - sas_device->slot); - } - if (le16_to_cpu(sas_device_pg0->Flags) & - MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID) { - sas_device->enclosure_level = - sas_device_pg0->EnclosureLevel; - memcpy(&sas_device->connector_name[0], - &sas_device_pg0->ConnectorName[0], 4); - } else { - sas_device->enclosure_level = 0; - sas_device->connector_name[0] = '\0'; - } + if (sas_device->enclosure_handle != 0) + starget_printk(KERN_INFO, starget, + "enclosure logical id(0x%016llx), slot(%d)\n", + (unsigned long long) + sas_device->enclosure_logical_id, + sas_device->slot); + } + if (le16_to_cpu(sas_device_pg0->Flags) & + MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID) { + sas_device->enclosure_level = + sas_device_pg0->EnclosureLevel; + memcpy(&sas_device->connector_name[0], + &sas_device_pg0->ConnectorName[0], 4); + } else { + sas_device->enclosure_level = 0; + sas_device->connector_name[0] = '\0'; + } - sas_device->enclosure_handle = - le16_to_cpu(sas_device_pg0->EnclosureHandle); - sas_device->is_chassis_slot_valid = 0; - if (enclosure_dev) { - sas_device->enclosure_logical_id = le64_to_cpu( - enclosure_dev->pg0.EnclosureLogicalID); - if (le16_to_cpu(enclosure_dev->pg0.Flags) & - MPI2_SAS_ENCLS0_FLAGS_CHASSIS_SLOT_VALID) { - sas_device->is_chassis_slot_valid = 1; - sas_device->chassis_slot = - enclosure_dev->pg0.ChassisSlot; - } + sas_device->enclosure_handle = + le16_to_cpu(sas_device_pg0->EnclosureHandle); + sas_device->is_chassis_slot_valid = 0; + if (enclosure_dev) { + sas_device->enclosure_logical_id = le64_to_cpu( + enclosure_dev->pg0.EnclosureLogicalID); + if (le16_to_cpu(enclosure_dev->pg0.Flags) & + MPI2_SAS_ENCLS0_FLAGS_CHASSIS_SLOT_VALID) { + sas_device->is_chassis_slot_valid = 1; + sas_device->chassis_slot = + enclosure_dev->pg0.ChassisSlot; } + } - if (sas_device->handle == le16_to_cpu( - sas_device_pg0->DevHandle)) - goto out; - pr_info("\thandle changed from(0x%04x)!!!\n", - sas_device->handle); - sas_device->handle = le16_to_cpu( - sas_device_pg0->DevHandle); - if (sas_target_priv_data) - sas_target_priv_data->handle = - le16_to_cpu(sas_device_pg0->DevHandle); + if (sas_device->handle == le16_to_cpu( + sas_device_pg0->DevHandle)) goto out; - } + pr_info("\thandle changed from(0x%04x)!!!\n", + sas_device->handle); + sas_device->handle = le16_to_cpu( + sas_device_pg0->DevHandle); + if (sas_target_priv_data) + sas_target_priv_data->handle = + le16_to_cpu(sas_device_pg0->DevHandle); + goto out; } out: spin_unlock_irqrestore(&ioc->sas_device_lock, flags); @@ -8494,6 +9906,8 @@ _scsih_mark_responding_pcie_device(struct MPT3SAS_ADAPTER *ioc, if ((pcie_device->wwid == le64_to_cpu(pcie_device_pg0->WWID)) && (pcie_device->slot == le16_to_cpu( pcie_device_pg0->Slot))) { + pcie_device->access_status = + pcie_device_pg0->AccessStatus; pcie_device->responding = 1; starget = pcie_device->starget; if (starget && starget->hostdata) { @@ -8581,7 +9995,7 @@ _scsih_search_responding_pcie_devices(struct MPT3SAS_ADAPTER *ioc) } handle = le16_to_cpu(pcie_device_pg0.DevHandle); device_info = le32_to_cpu(pcie_device_pg0.DeviceInfo); - if (!(_scsih_is_nvme_device(device_info))) + if (!(_scsih_is_nvme_pciescsi_device(device_info))) continue; _scsih_mark_responding_pcie_device(ioc, &pcie_device_pg0); } @@ -8732,6 +10146,8 @@ _scsih_mark_responding_expander(struct MPT3SAS_ADAPTER *ioc, u16 handle = le16_to_cpu(expander_pg0->DevHandle); u16 enclosure_handle = le16_to_cpu(expander_pg0->EnclosureHandle); u64 sas_address = le64_to_cpu(expander_pg0->SASAddress); + struct hba_port *port = mpt3sas_get_port_by_id( + ioc, expander_pg0->PhysicalPort, 0); if (enclosure_handle) enclosure_dev = @@ -8742,6 +10158,8 @@ _scsih_mark_responding_expander(struct MPT3SAS_ADAPTER *ioc, list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) { if (sas_expander->sas_address != sas_address) continue; + if (sas_expander->port != port) + continue; sas_expander->responding = 1; if (enclosure_dev) { @@ -8781,6 +10199,7 @@ _scsih_search_responding_expanders(struct MPT3SAS_ADAPTER *ioc) u16 ioc_status; u64 sas_address; u16 handle; + u8 port; ioc_info(ioc, "search for expanders: start\n"); @@ -8798,9 +10217,12 @@ _scsih_search_responding_expanders(struct MPT3SAS_ADAPTER *ioc) handle = le16_to_cpu(expander_pg0.DevHandle); sas_address = le64_to_cpu(expander_pg0.SASAddress); - pr_info("\texpander present: handle(0x%04x), sas_addr(0x%016llx)\n", - handle, - (unsigned long long)sas_address); + port = expander_pg0.PhysicalPort; + pr_info( + "\texpander present: handle(0x%04x), sas_addr(0x%016llx), port:%d\n", + handle, (unsigned long long)sas_address, + (ioc->multipath_on_hba ? + port : MULTIPATH_DISABLED_PORT_ID)); _scsih_mark_responding_expander(ioc, &expander_pg0); } @@ -8832,6 +10254,17 @@ _scsih_remove_unresponding_devices(struct MPT3SAS_ADAPTER *ioc) * owner for the reference the list had on any object we prune. */ spin_lock_irqsave(&ioc->sas_device_lock, flags); + + /* + * Clean up the sas_device_init_list list as + * driver goes for fresh scan as part of diag reset. + */ + list_for_each_entry_safe(sas_device, sas_device_next, + &ioc->sas_device_init_list, list) { + list_del_init(&sas_device->list); + sas_device_put(sas_device); + } + list_for_each_entry_safe(sas_device, sas_device_next, &ioc->sas_device_list, list) { if (!sas_device->responding) @@ -8853,6 +10286,16 @@ _scsih_remove_unresponding_devices(struct MPT3SAS_ADAPTER *ioc) ioc_info(ioc, "Removing unresponding devices: pcie end-devices\n"); INIT_LIST_HEAD(&head); spin_lock_irqsave(&ioc->pcie_device_lock, flags); + /* + * Clean up the pcie_device_init_list list as + * driver goes for fresh scan as part of diag reset. + */ + list_for_each_entry_safe(pcie_device, pcie_device_next, + &ioc->pcie_device_init_list, list) { + list_del_init(&pcie_device->list); + pcie_device_put(pcie_device); + } + list_for_each_entry_safe(pcie_device, pcie_device_next, &ioc->pcie_device_list, list) { if (!pcie_device->responding) @@ -8922,7 +10365,8 @@ _scsih_refresh_expander_links(struct MPT3SAS_ADAPTER *ioc, mpt3sas_transport_update_links(ioc, sas_expander->sas_address, le16_to_cpu(expander_pg1.AttachedDevHandle), i, - expander_pg1.NegotiatedLinkRate >> 4); + expander_pg1.NegotiatedLinkRate >> 4, + sas_expander->port); } } @@ -8941,7 +10385,7 @@ _scsih_scan_for_devices_after_reset(struct MPT3SAS_ADAPTER *ioc) Mpi2RaidPhysDiskPage0_t pd_pg0; Mpi2EventIrConfigElement_t element; Mpi2ConfigReply_t mpi_reply; - u8 phys_disk_num; + u8 phys_disk_num, port_id; u16 ioc_status; u16 handle, parent_handle; u64 sas_address; @@ -8971,8 +10415,10 @@ _scsih_scan_for_devices_after_reset(struct MPT3SAS_ADAPTER *ioc) } handle = le16_to_cpu(expander_pg0.DevHandle); spin_lock_irqsave(&ioc->sas_node_lock, flags); + port_id = expander_pg0.PhysicalPort; expander_device = mpt3sas_scsih_expander_find_by_sas_address( - ioc, le64_to_cpu(expander_pg0.SASAddress)); + ioc, le64_to_cpu(expander_pg0.SASAddress), + mpt3sas_get_port_by_id(ioc, port_id, 0)); spin_unlock_irqrestore(&ioc->sas_node_lock, flags); if (expander_device) _scsih_refresh_expander_links(ioc, expander_device, @@ -9031,9 +10477,11 @@ _scsih_scan_for_devices_after_reset(struct MPT3SAS_ADAPTER *ioc) ioc_info(ioc, "\tBEFORE adding phys disk: handle (0x%04x), sas_addr(0x%016llx)\n", handle, (u64)le64_to_cpu(sas_device_pg0.SASAddress)); + port_id = sas_device_pg0.PhysicalPort; mpt3sas_transport_update_links(ioc, sas_address, handle, sas_device_pg0.PhyNum, - MPI2_SAS_NEG_LINK_RATE_1_5); + MPI2_SAS_NEG_LINK_RATE_1_5, + mpt3sas_get_port_by_id(ioc, port_id, 0)); set_bit(handle, ioc->pd_handles); retry_count = 0; /* This will retry adding the end device. @@ -9119,8 +10567,10 @@ _scsih_scan_for_devices_after_reset(struct MPT3SAS_ADAPTER *ioc) if (!(_scsih_is_end_device( le32_to_cpu(sas_device_pg0.DeviceInfo)))) continue; + port_id = sas_device_pg0.PhysicalPort; sas_device = mpt3sas_get_sdev_by_addr(ioc, - le64_to_cpu(sas_device_pg0.SASAddress)); + le64_to_cpu(sas_device_pg0.SASAddress), + mpt3sas_get_port_by_id(ioc, port_id, 0)); if (sas_device) { sas_device_put(sas_device); continue; @@ -9131,7 +10581,8 @@ _scsih_scan_for_devices_after_reset(struct MPT3SAS_ADAPTER *ioc) handle, (u64)le64_to_cpu(sas_device_pg0.SASAddress)); mpt3sas_transport_update_links(ioc, sas_address, handle, - sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5); + sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5, + mpt3sas_get_port_by_id(ioc, port_id, 0)); retry_count = 0; /* This will retry adding the end device. * _scsih_add_device() will decide on retries and @@ -9162,7 +10613,7 @@ _scsih_scan_for_devices_after_reset(struct MPT3SAS_ADAPTER *ioc) break; } handle = le16_to_cpu(pcie_device_pg0.DevHandle); - if (!(_scsih_is_nvme_device( + if (!(_scsih_is_nvme_pciescsi_device( le32_to_cpu(pcie_device_pg0.DeviceInfo)))) continue; pcie_device = mpt3sas_get_pdev_by_wwid(ioc, @@ -9178,12 +10629,13 @@ _scsih_scan_for_devices_after_reset(struct MPT3SAS_ADAPTER *ioc) ioc_info(ioc, "\tAFTER adding pcie end device: handle (0x%04x), wwid(0x%016llx)\n", handle, (u64)le64_to_cpu(pcie_device_pg0.WWID)); } + ioc_info(ioc, "\tpcie devices: pcie end devices complete\n"); ioc_info(ioc, "scan devices: complete\n"); } /** - * mpt3sas_scsih_reset_handler - reset callback handler (for scsih) + * mpt3sas_scsih_pre_reset_handler - reset callback handler (for scsih) * @ioc: per adapter object * * The handler for doing any required cleanup or initialization. @@ -9194,15 +10646,17 @@ void mpt3sas_scsih_pre_reset_handler(struct MPT3SAS_ADAPTER *ioc) } /** - * mpt3sas_scsih_after_reset_handler - reset callback handler (for scsih) + * mpt3sas_scsih_clear_outstanding_scsi_tm_commands - clears outstanding + * scsi & tm cmds. * @ioc: per adapter object * * The handler for doing any required cleanup or initialization. */ void -mpt3sas_scsih_after_reset_handler(struct MPT3SAS_ADAPTER *ioc) +mpt3sas_scsih_clear_outstanding_scsi_tm_commands(struct MPT3SAS_ADAPTER *ioc) { - dtmprintk(ioc, ioc_info(ioc, "%s: MPT3_IOC_AFTER_RESET\n", __func__)); + dtmprintk(ioc, + ioc_info(ioc, "%s: clear outstanding scsi & tm cmds\n", __func__)); if (ioc->scsih_cmds.status & MPT3_CMD_PENDING) { ioc->scsih_cmds.status |= MPT3_CMD_RESET; mpt3sas_base_free_smid(ioc, ioc->scsih_cmds.smid); @@ -9222,7 +10676,7 @@ mpt3sas_scsih_after_reset_handler(struct MPT3SAS_ADAPTER *ioc) } /** - * mpt3sas_scsih_reset_handler - reset callback handler (for scsih) + * mpt3sas_scsih_reset_done_handler - reset callback handler (for scsih) * @ioc: per adapter object * * The handler for doing any required cleanup or initialization. @@ -9231,8 +10685,11 @@ void mpt3sas_scsih_reset_done_handler(struct MPT3SAS_ADAPTER *ioc) { dtmprintk(ioc, ioc_info(ioc, "%s: MPT3_IOC_DONE_RESET\n", __func__)); - if ((!ioc->is_driver_loading) && !(disable_discovery > 0 && - !ioc->sas_hba.num_phys)) { + if (!(disable_discovery > 0 && !ioc->sas_hba.num_phys)) { + if (ioc->multipath_on_hba) { + _scsih_sas_port_refresh(ioc); + _scsih_update_vphys_after_reset(ioc); + } _scsih_prep_device_scan(ioc); _scsih_create_enclosure_list_after_reset(ioc); _scsih_search_responding_sas_devices(ioc); @@ -9252,11 +10709,13 @@ mpt3sas_scsih_reset_done_handler(struct MPT3SAS_ADAPTER *ioc) static void _mpt3sas_fw_work(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work *fw_event) { + ioc->current_event = fw_event; _scsih_fw_event_del_from_list(ioc, fw_event); /* the queue is being flushed so ignore this event */ if (ioc->remove_host || ioc->pci_error_recovery) { fw_event_work_put(fw_event); + ioc->current_event = NULL; return; } @@ -9270,15 +10729,32 @@ _mpt3sas_fw_work(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work *fw_event) while (scsi_host_in_recovery(ioc->shost) || ioc->shost_recovery) { /* - * If we're unloading, bail. Otherwise, this can become - * an infinite loop. + * If we're unloading or cancelling the work, bail. + * Otherwise, this can become an infinite loop. */ - if (ioc->remove_host) + if (ioc->remove_host || ioc->fw_events_cleanup) goto out; ssleep(1); } _scsih_remove_unresponding_devices(ioc); + _scsih_del_dirty_vphy(ioc); + _scsih_del_dirty_port_entries(ioc); + if (ioc->is_gen35_ioc) + _scsih_update_device_qdepth(ioc); _scsih_scan_for_devices_after_reset(ioc); + /* + * If diag reset has occurred during the driver load + * then driver has to complete the driver load operation + * by executing the following items: + *- Register the devices from sas_device_init_list to SML + *- clear is_driver_loading flag, + *- start the watchdog thread. + * In happy driver load path, above things are taken care of when + * driver executes scsih_scan_finished(). + */ + if (ioc->is_driver_loading) + _scsih_complete_devices_scanning(ioc); + _scsih_set_nvme_max_shutdown_latency(ioc); break; case MPT3SAS_PORT_ENABLE_COMPLETE: ioc->start_scan = 0; @@ -9295,7 +10771,10 @@ _mpt3sas_fw_work(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work *fw_event) _scsih_sas_topology_change_event(ioc, fw_event); break; case MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE: - _scsih_sas_device_status_change_event(ioc, fw_event); + if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) + _scsih_sas_device_status_change_event_debug(ioc, + (Mpi2EventDataSasDeviceStatusChange_t *) + fw_event->event_data); break; case MPI2_EVENT_SAS_DISCOVERY: _scsih_sas_discovery_event(ioc, fw_event); @@ -9330,11 +10809,11 @@ _mpt3sas_fw_work(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work *fw_event) break; case MPI2_EVENT_PCIE_TOPOLOGY_CHANGE_LIST: _scsih_pcie_topology_change_event(ioc, fw_event); - return; - break; + break; } out: fw_event_work_put(fw_event); + ioc->current_event = NULL; } /** @@ -9418,11 +10897,23 @@ mpt3sas_scsih_event_callback(struct MPT3SAS_ADAPTER *ioc, u8 msix_index, _scsih_check_topo_delete_events(ioc, (Mpi2EventDataSasTopologyChangeList_t *) mpi_reply->EventData); + /* + * No need to add the topology change list + * event to fw event work queue when + * diag reset is going on. Since during diag + * reset driver scan the devices by reading + * sas device page0's not by processing the + * events. + */ + if (ioc->shost_recovery) + return 1; break; case MPI2_EVENT_PCIE_TOPOLOGY_CHANGE_LIST: _scsih_check_pcie_topo_remove_events(ioc, (Mpi26EventDataPCIeTopologyChangeList_t *) mpi_reply->EventData); + if (ioc->shost_recovery) + return 1; break; case MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST: _scsih_check_ir_config_unhide_events(ioc, @@ -9437,20 +10928,20 @@ mpt3sas_scsih_event_callback(struct MPT3SAS_ADAPTER *ioc, u8 msix_index, case MPI2_EVENT_LOG_ENTRY_ADDED: { Mpi2EventDataLogEntryAdded_t *log_entry; - u32 *log_code; + u32 log_code; if (!ioc->is_warpdrive) break; log_entry = (Mpi2EventDataLogEntryAdded_t *) mpi_reply->EventData; - log_code = (u32 *)log_entry->LogData; + log_code = le32_to_cpu(*(__le32 *)log_entry->LogData); if (le16_to_cpu(log_entry->LogEntryQualifier) != MPT2_WARPDRIVE_LOGENTRY) break; - switch (le32_to_cpu(*log_code)) { + switch (log_code) { case MPT2_WARPDRIVE_LC_SSDT: ioc_warn(ioc, "WarpDrive Warning: IO Throttling has occurred in the WarpDrive subsystem. Check WarpDrive documentation for additional details.\n"); break; @@ -9468,6 +10959,10 @@ mpt3sas_scsih_event_callback(struct MPT3SAS_ADAPTER *ioc, u8 msix_index, break; } case MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE: + _scsih_sas_device_status_change_event(ioc, + (Mpi2EventDataSasDeviceStatusChange_t *) + mpi_reply->EventData); + break; case MPI2_EVENT_IR_OPERATION_STATUS: case MPI2_EVENT_SAS_DISCOVERY: case MPI2_EVENT_SAS_DEVICE_DISCOVERY_ERROR: @@ -9492,7 +10987,8 @@ mpt3sas_scsih_event_callback(struct MPT3SAS_ADAPTER *ioc, u8 msix_index, pr_notice("cannot be powered and devices connected\n"); pr_notice("to this active cable will not be seen\n"); pr_notice("This active cable requires %d mW of power\n", - ActiveCableEventData->ActiveCablePowerRequirement); + le32_to_cpu( + ActiveCableEventData->ActiveCablePowerRequirement)); break; case MPI26_EVENT_ACTIVE_CABLE_DEGRADED: @@ -9541,6 +11037,7 @@ _scsih_expander_node_remove(struct MPT3SAS_ADAPTER *ioc, { struct _sas_port *mpt3sas_port, *next; unsigned long flags; + int port_id; /* remove sibling ports attached to this expander */ list_for_each_entry_safe(mpt3sas_port, next, @@ -9550,21 +11047,27 @@ _scsih_expander_node_remove(struct MPT3SAS_ADAPTER *ioc, if (mpt3sas_port->remote_identify.device_type == SAS_END_DEVICE) mpt3sas_device_remove_by_sas_address(ioc, - mpt3sas_port->remote_identify.sas_address); + mpt3sas_port->remote_identify.sas_address, + mpt3sas_port->hba_port); else if (mpt3sas_port->remote_identify.device_type == SAS_EDGE_EXPANDER_DEVICE || mpt3sas_port->remote_identify.device_type == SAS_FANOUT_EXPANDER_DEVICE) mpt3sas_expander_remove(ioc, - mpt3sas_port->remote_identify.sas_address); + mpt3sas_port->remote_identify.sas_address, + mpt3sas_port->hba_port); } + port_id = sas_expander->port->port_id; + mpt3sas_transport_port_remove(ioc, sas_expander->sas_address, - sas_expander->sas_address_parent); + sas_expander->sas_address_parent, sas_expander->port); - ioc_info(ioc, "expander_remove: handle(0x%04x), sas_addr(0x%016llx)\n", - sas_expander->handle, (unsigned long long) - sas_expander->sas_address); + ioc_info(ioc, + "expander_remove: handle(0x%04x), sas_addr(0x%016llx), port:%d\n", + sas_expander->handle, (unsigned long long) + sas_expander->sas_address, + port_id); spin_lock_irqsave(&ioc->sas_node_lock, flags); list_del(&sas_expander->list); @@ -9575,6 +11078,75 @@ _scsih_expander_node_remove(struct MPT3SAS_ADAPTER *ioc, } /** + * _scsih_nvme_shutdown - NVMe shutdown notification + * @ioc: per adapter object + * + * Sending IoUnitControl request with shutdown operation code to alert IOC that + * the host system is shutting down so that IOC can issue NVMe shutdown to + * NVMe drives attached to it. + */ +static void +_scsih_nvme_shutdown(struct MPT3SAS_ADAPTER *ioc) +{ + Mpi26IoUnitControlRequest_t *mpi_request; + Mpi26IoUnitControlReply_t *mpi_reply; + u16 smid; + + /* are there any NVMe devices ? */ + if (list_empty(&ioc->pcie_device_list)) + return; + + mutex_lock(&ioc->scsih_cmds.mutex); + + if (ioc->scsih_cmds.status != MPT3_CMD_NOT_USED) { + ioc_err(ioc, "%s: scsih_cmd in use\n", __func__); + goto out; + } + + ioc->scsih_cmds.status = MPT3_CMD_PENDING; + + smid = mpt3sas_base_get_smid(ioc, ioc->scsih_cb_idx); + if (!smid) { + ioc_err(ioc, + "%s: failed obtaining a smid\n", __func__); + ioc->scsih_cmds.status = MPT3_CMD_NOT_USED; + goto out; + } + + mpi_request = mpt3sas_base_get_msg_frame(ioc, smid); + ioc->scsih_cmds.smid = smid; + memset(mpi_request, 0, sizeof(Mpi26IoUnitControlRequest_t)); + mpi_request->Function = MPI2_FUNCTION_IO_UNIT_CONTROL; + mpi_request->Operation = MPI26_CTRL_OP_SHUTDOWN; + + init_completion(&ioc->scsih_cmds.done); + ioc->put_smid_default(ioc, smid); + /* Wait for max_shutdown_latency seconds */ + ioc_info(ioc, + "Io Unit Control shutdown (sending), Shutdown latency %d sec\n", + ioc->max_shutdown_latency); + wait_for_completion_timeout(&ioc->scsih_cmds.done, + ioc->max_shutdown_latency*HZ); + + if (!(ioc->scsih_cmds.status & MPT3_CMD_COMPLETE)) { + ioc_err(ioc, "%s: timeout\n", __func__); + goto out; + } + + if (ioc->scsih_cmds.status & MPT3_CMD_REPLY_VALID) { + mpi_reply = ioc->scsih_cmds.reply; + ioc_info(ioc, "Io Unit Control shutdown (complete):" + "ioc_status(0x%04x), loginfo(0x%08x)\n", + le16_to_cpu(mpi_reply->IOCStatus), + le32_to_cpu(mpi_reply->IOCLogInfo)); + } + out: + ioc->scsih_cmds.status = MPT3_CMD_NOT_USED; + mutex_unlock(&ioc->scsih_cmds.mutex); +} + + +/** * _scsih_ir_shutdown - IR shutdown notification * @ioc: per adapter object * @@ -9621,7 +11193,7 @@ _scsih_ir_shutdown(struct MPT3SAS_ADAPTER *ioc) if (!ioc->hide_ir_msg) ioc_info(ioc, "IR shutdown (sending)\n"); init_completion(&ioc->scsih_cmds.done); - mpt3sas_base_put_smid_default(ioc, smid); + ioc->put_smid_default(ioc, smid); wait_for_completion_timeout(&ioc->scsih_cmds.done, 10*HZ); if (!(ioc->scsih_cmds.status & MPT3_CMD_COMPLETE)) { @@ -9643,6 +11215,34 @@ _scsih_ir_shutdown(struct MPT3SAS_ADAPTER *ioc) } /** + * _scsih_get_shost_and_ioc - get shost and ioc + * and verify whether they are NULL or not + * @pdev: PCI device struct + * @shost: address of scsi host pointer + * @ioc: address of HBA adapter pointer + * + * Return zero if *shost and *ioc are not NULL otherwise return error number. + */ +static int +_scsih_get_shost_and_ioc(struct pci_dev *pdev, + struct Scsi_Host **shost, struct MPT3SAS_ADAPTER **ioc) +{ + *shost = pci_get_drvdata(pdev); + if (*shost == NULL) { + dev_err(&pdev->dev, "pdev's driver data is null\n"); + return -ENXIO; + } + + *ioc = shost_priv(*shost); + if (*ioc == NULL) { + dev_err(&pdev->dev, "shost's private data is null\n"); + return -ENXIO; + } + + return 0; +} + +/** * scsih_remove - detach and remove add host * @pdev: PCI device struct * @@ -9650,19 +11250,26 @@ _scsih_ir_shutdown(struct MPT3SAS_ADAPTER *ioc) */ static void scsih_remove(struct pci_dev *pdev) { - struct Scsi_Host *shost = pci_get_drvdata(pdev); - struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); + struct Scsi_Host *shost; + struct MPT3SAS_ADAPTER *ioc; struct _sas_port *mpt3sas_port, *next_port; struct _raid_device *raid_device, *next; struct MPT3SAS_TARGET *sas_target_priv_data; struct _pcie_device *pcie_device, *pcienext; struct workqueue_struct *wq; unsigned long flags; + Mpi2ConfigReply_t mpi_reply; + struct hba_port *port, *port_next; + + if (_scsih_get_shost_and_ioc(pdev, &shost, &ioc)) + return; ioc->remove_host = 1; - mpt3sas_wait_for_commands_to_complete(ioc); - _scsih_flush_running_cmds(ioc); + if (!pci_device_is_present(pdev)) { + mpt3sas_base_pause_mq_polling(ioc); + _scsih_flush_running_cmds(ioc); + } _scsih_fw_event_cleanup_queue(ioc); @@ -9672,9 +11279,16 @@ static void scsih_remove(struct pci_dev *pdev) spin_unlock_irqrestore(&ioc->fw_event_lock, flags); if (wq) destroy_workqueue(wq); - + /* + * Copy back the unmodified ioc page1. so that on next driver load, + * current modified changes on ioc page1 won't take effect. + */ + if (ioc->is_aero_ioc) + mpt3sas_config_set_ioc_pg1(ioc, &mpi_reply, + &ioc->ioc_pg1_copy); /* release all the volumes */ _scsih_ir_shutdown(ioc); + mpt3sas_destroy_debugfs(ioc); sas_remove_host(shost); list_for_each_entry_safe(raid_device, next, &ioc->raid_device_list, list) { @@ -9701,13 +11315,21 @@ static void scsih_remove(struct pci_dev *pdev) if (mpt3sas_port->remote_identify.device_type == SAS_END_DEVICE) mpt3sas_device_remove_by_sas_address(ioc, - mpt3sas_port->remote_identify.sas_address); + mpt3sas_port->remote_identify.sas_address, + mpt3sas_port->hba_port); else if (mpt3sas_port->remote_identify.device_type == SAS_EDGE_EXPANDER_DEVICE || mpt3sas_port->remote_identify.device_type == SAS_FANOUT_EXPANDER_DEVICE) mpt3sas_expander_remove(ioc, - mpt3sas_port->remote_identify.sas_address); + mpt3sas_port->remote_identify.sas_address, + mpt3sas_port->hba_port); + } + + list_for_each_entry_safe(port, port_next, + &ioc->port_table_list, list) { + list_del(&port->list); + kfree(port); } /* free phys attached to the sas_host */ @@ -9718,6 +11340,7 @@ static void scsih_remove(struct pci_dev *pdev) } mpt3sas_base_detach(ioc); + mpt3sas_ctl_release(ioc); spin_lock(&gioc_lock); list_del(&ioc->list); spin_unlock(&gioc_lock); @@ -9731,15 +11354,21 @@ static void scsih_remove(struct pci_dev *pdev) static void scsih_shutdown(struct pci_dev *pdev) { - struct Scsi_Host *shost = pci_get_drvdata(pdev); - struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); + struct Scsi_Host *shost; + struct MPT3SAS_ADAPTER *ioc; struct workqueue_struct *wq; unsigned long flags; + Mpi2ConfigReply_t mpi_reply; + + if (_scsih_get_shost_and_ioc(pdev, &shost, &ioc)) + return; ioc->remove_host = 1; - mpt3sas_wait_for_commands_to_complete(ioc); - _scsih_flush_running_cmds(ioc); + if (!pci_device_is_present(pdev)) { + mpt3sas_base_pause_mq_polling(ioc); + _scsih_flush_running_cmds(ioc); + } _scsih_fw_event_cleanup_queue(ioc); @@ -9749,9 +11378,23 @@ scsih_shutdown(struct pci_dev *pdev) spin_unlock_irqrestore(&ioc->fw_event_lock, flags); if (wq) destroy_workqueue(wq); + /* + * Copy back the unmodified ioc page1 so that on next driver load, + * current modified changes on ioc page1 won't take effect. + */ + if (ioc->is_aero_ioc) + mpt3sas_config_set_ioc_pg1(ioc, &mpi_reply, + &ioc->ioc_pg1_copy); _scsih_ir_shutdown(ioc); - mpt3sas_base_detach(ioc); + _scsih_nvme_shutdown(ioc); + mpt3sas_base_mask_interrupts(ioc); + mpt3sas_base_stop_watchdog(ioc); + ioc->shost_recovery = 1; + mpt3sas_base_make_ioc_ready(ioc, SOFT_RESET); + ioc->shost_recovery = 0; + mpt3sas_base_free_irq(ioc); + mpt3sas_base_disable_msix(ioc); } @@ -9777,6 +11420,7 @@ _scsih_probe_boot_devices(struct MPT3SAS_ADAPTER *ioc) unsigned long flags; int rc; int tid; + struct hba_port *port; /* no Bios, return immediately */ if (!ioc->bios_pg3.BiosVersion) @@ -9799,13 +11443,27 @@ _scsih_probe_boot_devices(struct MPT3SAS_ADAPTER *ioc) if (channel == RAID_CHANNEL) { raid_device = device; + /* + * If this boot vd is already registered with SML then + * no need to register it again as part of device scanning + * after diag reset during driver load operation. + */ + if (raid_device->starget) + return; rc = scsi_add_device(ioc->shost, RAID_CHANNEL, raid_device->id, 0); if (rc) _scsih_raid_device_remove(ioc, raid_device); } else if (channel == PCIE_CHANNEL) { - spin_lock_irqsave(&ioc->pcie_device_lock, flags); pcie_device = device; + /* + * If this boot NVMe device is already registered with SML then + * no need to register it again as part of device scanning + * after diag reset during driver load operation. + */ + if (pcie_device->starget) + return; + spin_lock_irqsave(&ioc->pcie_device_lock, flags); tid = pcie_device->id; list_move_tail(&pcie_device->list, &ioc->pcie_device_list); spin_unlock_irqrestore(&ioc->pcie_device_lock, flags); @@ -9813,24 +11471,36 @@ _scsih_probe_boot_devices(struct MPT3SAS_ADAPTER *ioc) if (rc) _scsih_pcie_device_remove(ioc, pcie_device); } else { - spin_lock_irqsave(&ioc->sas_device_lock, flags); sas_device = device; + /* + * If this boot sas/sata device is already registered with SML + * then no need to register it again as part of device scanning + * after diag reset during driver load operation. + */ + if (sas_device->starget) + return; + spin_lock_irqsave(&ioc->sas_device_lock, flags); handle = sas_device->handle; sas_address_parent = sas_device->sas_address_parent; sas_address = sas_device->sas_address; + port = sas_device->port; list_move_tail(&sas_device->list, &ioc->sas_device_list); spin_unlock_irqrestore(&ioc->sas_device_lock, flags); if (ioc->hide_drives) return; + + if (!port) + return; + if (!mpt3sas_transport_port_add(ioc, handle, - sas_address_parent)) { + sas_address_parent, port)) { _scsih_sas_device_remove(ioc, sas_device); } else if (!sas_device->starget) { if (!ioc->is_driver_loading) { mpt3sas_transport_port_remove(ioc, sas_address, - sas_address_parent); + sas_address_parent, port); _scsih_sas_device_remove(ioc, sas_device); } } @@ -9918,7 +11588,7 @@ _scsih_probe_sas(struct MPT3SAS_ADAPTER *ioc) while ((sas_device = get_next_sas_device(ioc))) { if (!mpt3sas_transport_port_add(ioc, sas_device->handle, - sas_device->sas_address_parent)) { + sas_device->sas_address_parent, sas_device->port)) { _scsih_sas_device_remove(ioc, sas_device); sas_device_put(sas_device); continue; @@ -9932,7 +11602,8 @@ _scsih_probe_sas(struct MPT3SAS_ADAPTER *ioc) if (!ioc->is_driver_loading) { mpt3sas_transport_port_remove(ioc, sas_device->sas_address, - sas_device->sas_address_parent); + sas_device->sas_address_parent, + sas_device->port); _scsih_sas_device_remove(ioc, sas_device); sas_device_put(sas_device); continue; @@ -10011,6 +11682,12 @@ _scsih_probe_pcie(struct MPT3SAS_ADAPTER *ioc) pcie_device_put(pcie_device); continue; } + if (pcie_device->access_status == + MPI26_PCIEDEV0_ASTATUS_DEVICE_BLOCKED) { + pcie_device_make_active(ioc, pcie_device); + pcie_device_put(pcie_device); + continue; + } rc = scsi_add_device(ioc->shost, PCIE_CHANNEL, pcie_device->id, 0); if (rc) { @@ -10087,6 +11764,8 @@ scsih_scan_start(struct Scsi_Host *shost) int rc; if (diag_buffer_enable != -1 && diag_buffer_enable != 0) mpt3sas_enable_diag_buffer(ioc, diag_buffer_enable); + else if (ioc->manu_pg11.HostTraceBufferMaxSizeKB != 0) + mpt3sas_enable_diag_buffer(ioc, 1); if (disable_discovery > 0) return; @@ -10099,6 +11778,25 @@ scsih_scan_start(struct Scsi_Host *shost) } /** + * _scsih_complete_devices_scanning - add the devices to sml and + * complete ioc initialization. + * @ioc: per adapter object + * + * Return nothing. + */ +static void _scsih_complete_devices_scanning(struct MPT3SAS_ADAPTER *ioc) +{ + + if (ioc->wait_for_discovery_to_complete) { + ioc->wait_for_discovery_to_complete = 0; + _scsih_probe_devices(ioc); + } + + mpt3sas_base_start_watchdog(ioc); + ioc->is_driver_loading = 0; +} + +/** * scsih_scan_finished - scsi lld callback for .scan_finished * @shost: SCSI host pointer * @time: elapsed time of the scan in jiffies @@ -10111,6 +11809,8 @@ static int scsih_scan_finished(struct Scsi_Host *shost, unsigned long time) { struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); + u32 ioc_state; + int issue_hard_reset = 0; if (disable_discovery > 0) { ioc->is_driver_loading = 0; @@ -10125,9 +11825,30 @@ scsih_scan_finished(struct Scsi_Host *shost, unsigned long time) return 1; } - if (ioc->start_scan) + if (ioc->start_scan) { + ioc_state = mpt3sas_base_get_iocstate(ioc, 0); + if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) { + mpt3sas_print_fault_code(ioc, ioc_state & + MPI2_DOORBELL_DATA_MASK); + issue_hard_reset = 1; + goto out; + } else if ((ioc_state & MPI2_IOC_STATE_MASK) == + MPI2_IOC_STATE_COREDUMP) { + mpt3sas_base_coredump_info(ioc, ioc_state & + MPI2_DOORBELL_DATA_MASK); + mpt3sas_base_wait_for_coredump_completion(ioc, __func__); + issue_hard_reset = 1; + goto out; + } return 0; + } + if (ioc->port_enable_cmds.status & MPT3_CMD_RESET) { + ioc_info(ioc, + "port enable: aborted due to diag reset\n"); + ioc->port_enable_cmds.status = MPT3_CMD_NOT_USED; + goto out; + } if (ioc->start_scan_failed) { ioc_info(ioc, "port enable: FAILED with (ioc_status=0x%08x)\n", ioc->start_scan_failed); @@ -10139,27 +11860,72 @@ scsih_scan_finished(struct Scsi_Host *shost, unsigned long time) ioc_info(ioc, "port enable: SUCCESS\n"); ioc->port_enable_cmds.status = MPT3_CMD_NOT_USED; + _scsih_complete_devices_scanning(ioc); - if (ioc->wait_for_discovery_to_complete) { - ioc->wait_for_discovery_to_complete = 0; - _scsih_probe_devices(ioc); +out: + if (issue_hard_reset) { + ioc->port_enable_cmds.status = MPT3_CMD_NOT_USED; + if (mpt3sas_base_hard_reset_handler(ioc, SOFT_RESET)) + ioc->is_driver_loading = 0; } - mpt3sas_base_start_watchdog(ioc); - ioc->is_driver_loading = 0; return 1; } +/** + * scsih_map_queues - map reply queues with request queues + * @shost: SCSI host pointer + */ +static void scsih_map_queues(struct Scsi_Host *shost) +{ + struct MPT3SAS_ADAPTER *ioc = + (struct MPT3SAS_ADAPTER *)shost->hostdata; + struct blk_mq_queue_map *map; + int i, qoff, offset; + int nr_msix_vectors = ioc->iopoll_q_start_index; + int iopoll_q_count = ioc->reply_queue_count - nr_msix_vectors; + + if (shost->nr_hw_queues == 1) + return; + + for (i = 0, qoff = 0; i < shost->nr_maps; i++) { + map = &shost->tag_set.map[i]; + map->nr_queues = 0; + offset = 0; + if (i == HCTX_TYPE_DEFAULT) { + map->nr_queues = + nr_msix_vectors - ioc->high_iops_queues; + offset = ioc->high_iops_queues; + } else if (i == HCTX_TYPE_POLL) + map->nr_queues = iopoll_q_count; + + if (!map->nr_queues) + BUG_ON(i == HCTX_TYPE_DEFAULT); + + /* + * 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; + if (i != HCTX_TYPE_POLL) + blk_mq_map_hw_queues(map, &ioc->pdev->dev, offset); + else + blk_mq_map_queues(map); + + qoff += map->nr_queues; + } +} + /* shost template for SAS 2.0 HBA devices */ -static struct scsi_host_template mpt2sas_driver_template = { +static const struct scsi_host_template mpt2sas_driver_template = { .module = THIS_MODULE, .name = "Fusion MPT SAS Host", .proc_name = MPT2SAS_DRIVER_NAME, .queuecommand = scsih_qcmd, .target_alloc = scsih_target_alloc, - .slave_alloc = scsih_slave_alloc, - .slave_configure = scsih_slave_configure, + .sdev_init = scsih_sdev_init, + .sdev_configure = scsih_sdev_configure, .target_destroy = scsih_target_destroy, - .slave_destroy = scsih_slave_destroy, + .sdev_destroy = scsih_sdev_destroy, .scan_finished = scsih_scan_finished, .scan_start = scsih_scan_start, .change_queue_depth = scsih_change_queue_depth, @@ -10173,8 +11939,8 @@ static struct scsi_host_template mpt2sas_driver_template = { .sg_tablesize = MPT2SAS_SG_DEPTH, .max_sectors = 32767, .cmd_per_lun = 7, - .shost_attrs = mpt3sas_host_attrs, - .sdev_attrs = mpt3sas_dev_attrs, + .shost_groups = mpt3sas_host_groups, + .sdev_groups = mpt3sas_dev_groups, .track_queue_depth = 1, .cmd_size = sizeof(struct scsiio_tracker), }; @@ -10188,16 +11954,16 @@ static struct raid_function_template mpt2sas_raid_functions = { }; /* shost template for SAS 3.0 HBA devices */ -static struct scsi_host_template mpt3sas_driver_template = { +static const struct scsi_host_template mpt3sas_driver_template = { .module = THIS_MODULE, .name = "Fusion MPT SAS Host", .proc_name = MPT3SAS_DRIVER_NAME, .queuecommand = scsih_qcmd, .target_alloc = scsih_target_alloc, - .slave_alloc = scsih_slave_alloc, - .slave_configure = scsih_slave_configure, + .sdev_init = scsih_sdev_init, + .sdev_configure = scsih_sdev_configure, .target_destroy = scsih_target_destroy, - .slave_destroy = scsih_slave_destroy, + .sdev_destroy = scsih_sdev_destroy, .scan_finished = scsih_scan_finished, .scan_start = scsih_scan_start, .change_queue_depth = scsih_change_queue_depth, @@ -10210,11 +11976,14 @@ static struct scsi_host_template mpt3sas_driver_template = { .this_id = -1, .sg_tablesize = MPT3SAS_SG_DEPTH, .max_sectors = 32767, - .cmd_per_lun = 7, - .shost_attrs = mpt3sas_host_attrs, - .sdev_attrs = mpt3sas_dev_attrs, + .max_segment_size = 0xffffffff, + .cmd_per_lun = 128, + .shost_groups = mpt3sas_host_groups, + .sdev_groups = mpt3sas_dev_groups, .track_queue_depth = 1, .cmd_size = sizeof(struct scsiio_tracker), + .map_queues = scsih_map_queues, + .mq_poll = mpt3sas_blk_mq_poll, }; /* raid transport support for SAS 3.0 HBA devices */ @@ -10256,7 +12025,8 @@ _scsih_determine_hba_mpi_version(struct pci_dev *pdev) case MPI2_MFGPAGE_DEVID_SAS2308_1: case MPI2_MFGPAGE_DEVID_SAS2308_2: case MPI2_MFGPAGE_DEVID_SAS2308_3: - case MPI2_MFGPAGE_DEVID_SAS2308_MPI_EP: + case MPI2_MFGPAGE_DEVID_SWITCH_MPI_EP: + case MPI2_MFGPAGE_DEVID_SWITCH_MPI_EP_1: return MPI2_VERSION; case MPI25_MFGPAGE_DEVID_SAS3004: case MPI25_MFGPAGE_DEVID_SAS3008: @@ -10282,10 +12052,15 @@ _scsih_determine_hba_mpi_version(struct pci_dev *pdev) case MPI26_MFGPAGE_DEVID_SAS3516_1: case MPI26_MFGPAGE_DEVID_SAS3416: case MPI26_MFGPAGE_DEVID_SAS3616: + case MPI26_ATLAS_PCIe_SWITCH_DEVID: case MPI26_MFGPAGE_DEVID_CFG_SEC_3916: case MPI26_MFGPAGE_DEVID_HARD_SEC_3916: case MPI26_MFGPAGE_DEVID_CFG_SEC_3816: case MPI26_MFGPAGE_DEVID_HARD_SEC_3816: + case MPI26_MFGPAGE_DEVID_INVALID0_3916: + case MPI26_MFGPAGE_DEVID_INVALID1_3916: + case MPI26_MFGPAGE_DEVID_INVALID0_3816: + case MPI26_MFGPAGE_DEVID_INVALID1_3816: return MPI26_VERSION; } return 0; @@ -10305,6 +12080,7 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) struct Scsi_Host *shost = NULL; int rv; u16 hba_mpi_version; + int iopoll_q_count = 0; /* Determine in which MPI version class this pci device belongs */ hba_mpi_version = _scsih_determine_hba_mpi_version(pdev); @@ -10343,13 +12119,20 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) ioc->is_warpdrive = 1; ioc->hide_ir_msg = 1; break; - case MPI2_MFGPAGE_DEVID_SAS2308_MPI_EP: + case MPI2_MFGPAGE_DEVID_SWITCH_MPI_EP: + case MPI2_MFGPAGE_DEVID_SWITCH_MPI_EP_1: ioc->is_mcpu_endpoint = 1; break; default: ioc->mfg_pg10_hide_flag = MFG_PAGE10_EXPOSE_ALL_DISKS; break; } + + if (multipath_on_hba == -1 || multipath_on_hba == 0) + ioc->multipath_on_hba = 0; + else + ioc->multipath_on_hba = 1; + break; case MPI25_VERSION: case MPI26_VERSION: @@ -10371,13 +12154,28 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) case MPI26_MFGPAGE_DEVID_SAS3516_1: case MPI26_MFGPAGE_DEVID_SAS3416: case MPI26_MFGPAGE_DEVID_SAS3616: + case MPI26_ATLAS_PCIe_SWITCH_DEVID: ioc->is_gen35_ioc = 1; break; + case MPI26_MFGPAGE_DEVID_INVALID0_3816: + case MPI26_MFGPAGE_DEVID_INVALID0_3916: + dev_err(&pdev->dev, + "HBA with DeviceId 0x%04x, sub VendorId 0x%04x, sub DeviceId 0x%04x is Invalid", + pdev->device, pdev->subsystem_vendor, + pdev->subsystem_device); + return 1; + case MPI26_MFGPAGE_DEVID_INVALID1_3816: + case MPI26_MFGPAGE_DEVID_INVALID1_3916: + dev_err(&pdev->dev, + "HBA with DeviceId 0x%04x, sub VendorId 0x%04x, sub DeviceId 0x%04x is Tampered", + pdev->device, pdev->subsystem_vendor, + pdev->subsystem_device); + return 1; case MPI26_MFGPAGE_DEVID_CFG_SEC_3816: case MPI26_MFGPAGE_DEVID_CFG_SEC_3916: dev_info(&pdev->dev, "HBA is in Configurable Secure mode\n"); - /* fall through */ + fallthrough; case MPI26_MFGPAGE_DEVID_HARD_SEC_3816: case MPI26_MFGPAGE_DEVID_HARD_SEC_3916: ioc->is_aero_ioc = ioc->is_gen35_ioc = 1; @@ -10396,6 +12194,24 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) ioc->combined_reply_index_count = MPT3_SUP_REPLY_POST_HOST_INDEX_REG_COUNT_G3; } + + switch (ioc->is_gen35_ioc) { + case 0: + if (multipath_on_hba == -1 || multipath_on_hba == 0) + ioc->multipath_on_hba = 0; + else + ioc->multipath_on_hba = 1; + break; + case 1: + if (multipath_on_hba == -1 || multipath_on_hba > 0) + ioc->multipath_on_hba = 1; + else + ioc->multipath_on_hba = 0; + break; + default: + break; + } + break; default: return -ENODEV; @@ -10420,8 +12236,20 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) ioc->tm_sas_control_cb_idx = tm_sas_control_cb_idx; ioc->logging_level = logging_level; ioc->schedule_dead_ioc_flush_running_cmds = &_scsih_flush_running_cmds; + /* Host waits for minimum of six seconds */ + ioc->max_shutdown_latency = IO_UNIT_CONTROL_SHUTDOWN_TIMEOUT; + /* + * Enable MEMORY MOVE support flag. + */ + ioc->drv_support_bitmap |= MPT_DRV_SUPPORT_BITMAP_MEMMOVE; + /* Enable ADDITIONAL QUERY support flag. */ + ioc->drv_support_bitmap |= MPT_DRV_SUPPORT_BITMAP_ADDNLQUERY; + + ioc->enable_sdev_max_qd = enable_sdev_max_qd; + /* misc semaphores and spin locks */ mutex_init(&ioc->reset_in_progress_mutex); + mutex_init(&ioc->hostdiag_unlock_mutex); /* initializing pci_access_mutex lock */ mutex_init(&ioc->pci_access_mutex); spin_lock_init(&ioc->ioc_reset_in_progress_lock); @@ -10447,6 +12275,7 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) INIT_LIST_HEAD(&ioc->delayed_event_ack_list); INIT_LIST_HEAD(&ioc->delayed_tr_volume_list); INIT_LIST_HEAD(&ioc->reply_queue_list); + INIT_LIST_HEAD(&ioc->port_table_list); sprintf(ioc->name, "%s_cm%d", ioc->driver_name, ioc->id); @@ -10479,8 +12308,8 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) } } /* register EEDP capabilities with SCSI layer */ - if (prot_mask > 0) - scsi_host_set_prot(shost, prot_mask); + if (prot_mask >= 0) + scsi_host_set_prot(shost, (prot_mask & 0x07)); else scsi_host_set_prot(shost, SHOST_DIF_TYPE1_PROTECTION | SHOST_DIF_TYPE2_PROTECTION @@ -10489,10 +12318,8 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) scsi_host_set_guard(shost, SHOST_DIX_GUARD_CRC); /* event thread */ - snprintf(ioc->firmware_event_name, sizeof(ioc->firmware_event_name), - "fw_event_%s%d", ioc->driver_name, ioc->id); ioc->firmware_event_thread = alloc_ordered_workqueue( - ioc->firmware_event_name, 0); + "fw_event_%s%d", 0, ioc->driver_name, ioc->id); if (!ioc->firmware_event_thread) { ioc_err(ioc, "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); @@ -10500,6 +12327,11 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto out_thread_fail; } + shost->host_tagset = 0; + + if (ioc->is_gen35_ioc && host_tagset_enable) + shost->host_tagset = 1; + ioc->is_driver_loading = 1; if ((mpt3sas_base_attach(ioc))) { ioc_err(ioc, "failure at %s:%d/%s()!\n", @@ -10522,6 +12354,22 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) } else ioc->hide_drives = 0; + shost->nr_hw_queues = 1; + + if (shost->host_tagset) { + shost->nr_hw_queues = + ioc->reply_queue_count - ioc->high_iops_queues; + + iopoll_q_count = + ioc->reply_queue_count - ioc->iopoll_q_start_index; + + shost->nr_maps = iopoll_q_count ? 3 : 1; + + dev_info(&ioc->pdev->dev, + "Max SCSIIO MPT commands: %d shared with nr_hw_queues = %d\n", + shost->can_queue, shost->nr_hw_queues); + } + rv = scsi_add_host(shost, &pdev->dev); if (rv) { ioc_err(ioc, "failure at %s:%d/%s()!\n", @@ -10530,6 +12378,7 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) } scsi_scan_host(shost); + mpt3sas_setup_debugfs(ioc); return 0; out_add_shost_fail: mpt3sas_base_detach(ioc); @@ -10543,65 +12392,66 @@ out_add_shost_fail: return rv; } -#ifdef CONFIG_PM /** * scsih_suspend - power management suspend main entry point - * @pdev: PCI device struct - * @state: PM state change to (usually PCI_D3) + * @dev: Device struct * * Return: 0 success, anything else error. */ -static int -scsih_suspend(struct pci_dev *pdev, pm_message_t state) +static int __maybe_unused +scsih_suspend(struct device *dev) { - struct Scsi_Host *shost = pci_get_drvdata(pdev); - struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); - pci_power_t device_state; + struct pci_dev *pdev = to_pci_dev(dev); + struct Scsi_Host *shost; + struct MPT3SAS_ADAPTER *ioc; + int rc; + + rc = _scsih_get_shost_and_ioc(pdev, &shost, &ioc); + if (rc) + return rc; mpt3sas_base_stop_watchdog(ioc); - flush_scheduled_work(); scsi_block_requests(shost); - device_state = pci_choose_state(pdev, state); - ioc_info(ioc, "pdev=0x%p, slot=%s, entering operating state [D%d]\n", - pdev, pci_name(pdev), device_state); + _scsih_nvme_shutdown(ioc); + ioc_info(ioc, "pdev=0x%p, slot=%s, entering operating state\n", + pdev, pci_name(pdev)); - pci_save_state(pdev); mpt3sas_base_free_resources(ioc); - pci_set_power_state(pdev, device_state); return 0; } /** * scsih_resume - power management resume main entry point - * @pdev: PCI device struct + * @dev: Device struct * * Return: 0 success, anything else error. */ -static int -scsih_resume(struct pci_dev *pdev) +static int __maybe_unused +scsih_resume(struct device *dev) { - struct Scsi_Host *shost = pci_get_drvdata(pdev); - struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); + struct pci_dev *pdev = to_pci_dev(dev); + struct Scsi_Host *shost; + struct MPT3SAS_ADAPTER *ioc; pci_power_t device_state = pdev->current_state; int r; + r = _scsih_get_shost_and_ioc(pdev, &shost, &ioc); + if (r) + return r; + ioc_info(ioc, "pdev=0x%p, slot=%s, previous operating state [D%d]\n", pdev, pci_name(pdev), device_state); - pci_set_power_state(pdev, PCI_D0); - pci_enable_wake(pdev, PCI_D0, 0); - pci_restore_state(pdev); ioc->pdev = pdev; r = mpt3sas_base_map_resources(ioc); if (r) return r; - + ioc_info(ioc, "Issuing Hard Reset as part of OS Resume\n"); mpt3sas_base_hard_reset_handler(ioc, SOFT_RESET); scsi_unblock_requests(shost); mpt3sas_base_start_watchdog(ioc); return 0; } -#endif /* CONFIG_PM */ /** * scsih_pci_error_detected - Called when a PCI error is detected. @@ -10615,8 +12465,11 @@ scsih_resume(struct pci_dev *pdev) static pci_ers_result_t scsih_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state) { - struct Scsi_Host *shost = pci_get_drvdata(pdev); - struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); + struct Scsi_Host *shost; + struct MPT3SAS_ADAPTER *ioc; + + if (_scsih_get_shost_and_ioc(pdev, &shost, &ioc)) + return PCI_ERS_RESULT_DISCONNECT; ioc_info(ioc, "PCI error: detected callback, state(%d)!!\n", state); @@ -10634,6 +12487,7 @@ scsih_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state) /* Permanent error, prepare for device removal */ ioc->pci_error_recovery = 1; mpt3sas_base_stop_watchdog(ioc); + mpt3sas_base_pause_mq_polling(ioc); _scsih_flush_running_cmds(ioc); return PCI_ERS_RESULT_DISCONNECT; } @@ -10651,10 +12505,13 @@ scsih_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state) static pci_ers_result_t scsih_pci_slot_reset(struct pci_dev *pdev) { - struct Scsi_Host *shost = pci_get_drvdata(pdev); - struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); + struct Scsi_Host *shost; + struct MPT3SAS_ADAPTER *ioc; int rc; + if (_scsih_get_shost_and_ioc(pdev, &shost, &ioc)) + return PCI_ERS_RESULT_DISCONNECT; + ioc_info(ioc, "PCI error: slot reset callback!!\n"); ioc->pci_error_recovery = 0; @@ -10664,6 +12521,7 @@ scsih_pci_slot_reset(struct pci_dev *pdev) if (rc) return PCI_ERS_RESULT_DISCONNECT; + ioc_info(ioc, "Issuing Hard Reset as part of PCI Slot Reset\n"); rc = mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); ioc_warn(ioc, "hard reset: %s\n", @@ -10686,8 +12544,11 @@ scsih_pci_slot_reset(struct pci_dev *pdev) static void scsih_pci_resume(struct pci_dev *pdev) { - struct Scsi_Host *shost = pci_get_drvdata(pdev); - struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); + struct Scsi_Host *shost; + struct MPT3SAS_ADAPTER *ioc; + + if (_scsih_get_shost_and_ioc(pdev, &shost, &ioc)) + return; ioc_info(ioc, "PCI error: resume callback!!\n"); @@ -10702,8 +12563,11 @@ scsih_pci_resume(struct pci_dev *pdev) static pci_ers_result_t scsih_pci_mmio_enabled(struct pci_dev *pdev) { - struct Scsi_Host *shost = pci_get_drvdata(pdev); - struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); + struct Scsi_Host *shost; + struct MPT3SAS_ADAPTER *ioc; + + if (_scsih_get_shost_and_ioc(pdev, &shost, &ioc)) + return PCI_ERS_RESULT_DISCONNECT; ioc_info(ioc, "PCI error: mmio enabled callback!!\n"); @@ -10716,31 +12580,6 @@ scsih_pci_mmio_enabled(struct pci_dev *pdev) return PCI_ERS_RESULT_RECOVERED; } -/** - * scsih__ncq_prio_supp - Check for NCQ command priority support - * @sdev: scsi device struct - * - * This is called when a user indicates they would like to enable - * ncq command priorities. This works only on SATA devices. - */ -bool scsih_ncq_prio_supp(struct scsi_device *sdev) -{ - unsigned char *buf; - bool ncq_prio_supp = false; - - if (!scsi_device_supports_vpd(sdev)) - return ncq_prio_supp; - - buf = kmalloc(SCSI_VPD_PG_LEN, GFP_KERNEL); - if (!buf) - return ncq_prio_supp; - - if (!scsi_get_vpd_page(sdev, 0x89, buf, SCSI_VPD_PG_LEN)) - ncq_prio_supp = (buf[213] >> 4) & 1; - - kfree(buf); - return ncq_prio_supp; -} /* * The pci device ids are defined in mpi/mpi2_cnfg.h. */ @@ -10783,7 +12622,9 @@ static const struct pci_device_id mpt3sas_pci_table[] = { PCI_ANY_ID, PCI_ANY_ID }, { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_3, PCI_ANY_ID, PCI_ANY_ID }, - { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_MPI_EP, + { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SWITCH_MPI_EP, + PCI_ANY_ID, PCI_ANY_ID }, + { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SWITCH_MPI_EP_1, PCI_ANY_ID, PCI_ANY_ID }, /* SSS6200 */ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SSS6200, @@ -10849,6 +12690,18 @@ static const struct pci_device_id mpt3sas_pci_table[] = { { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_HARD_SEC_3916, PCI_ANY_ID, PCI_ANY_ID }, + /* + * Aero SI –> 0x00E0 Invalid, 0x00E3 Tampered + */ + { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_INVALID0_3916, + PCI_ANY_ID, PCI_ANY_ID }, + { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_INVALID1_3916, + PCI_ANY_ID, PCI_ANY_ID }, + + /* Atlas PCIe Switch Management Port */ + { MPI2_MFGPAGE_VENDORID_LSI, MPI26_ATLAS_PCIe_SWITCH_DEVID, + PCI_ANY_ID, PCI_ANY_ID }, + /* Sea SI 0x00E5 Configurable Secure * 0x00E6 Hard Secure */ @@ -10857,17 +12710,33 @@ static const struct pci_device_id mpt3sas_pci_table[] = { { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_HARD_SEC_3816, PCI_ANY_ID, PCI_ANY_ID }, + /* + * ATTO Branded ExpressSAS H12xx GT + */ + { MPI2_MFGPAGE_VENDORID_ATTO, MPI26_MFGPAGE_DEVID_HARD_SEC_3816, + PCI_ANY_ID, PCI_ANY_ID }, + + /* + * Sea SI –> 0x00E4 Invalid, 0x00E7 Tampered + */ + { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_INVALID0_3816, + PCI_ANY_ID, PCI_ANY_ID }, + { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_INVALID1_3816, + PCI_ANY_ID, PCI_ANY_ID }, + {0} /* Terminating entry */ }; MODULE_DEVICE_TABLE(pci, mpt3sas_pci_table); -static struct pci_error_handlers _mpt3sas_err_handler = { +static const struct pci_error_handlers _mpt3sas_err_handler = { .error_detected = scsih_pci_error_detected, .mmio_enabled = scsih_pci_mmio_enabled, .slot_reset = scsih_pci_slot_reset, .resume = scsih_pci_resume, }; +static SIMPLE_DEV_PM_OPS(scsih_pm_ops, scsih_suspend, scsih_resume); + static struct pci_driver mpt3sas_driver = { .name = MPT3SAS_DRIVER_NAME, .id_table = mpt3sas_pci_table, @@ -10875,10 +12744,7 @@ static struct pci_driver mpt3sas_driver = { .remove = scsih_remove, .shutdown = scsih_shutdown, .err_handler = &_mpt3sas_err_handler, -#ifdef CONFIG_PM - .suspend = scsih_suspend, - .resume = scsih_resume, -#endif + .driver.pm = &scsih_pm_ops, }; /** @@ -10928,6 +12794,7 @@ scsih_init(void) tm_sas_control_cb_idx = mpt3sas_base_register_callback_handler( _scsih_sas_control_complete); + mpt3sas_init_debugfs(); return 0; } @@ -10959,6 +12826,7 @@ scsih_exit(void) if (hbas_to_enumerate != 2) raid_class_release(mpt2sas_raid_template); sas_release_transport(mpt3sas_transport_template); + mpt3sas_exit_debugfs(); } /** @@ -11012,8 +12880,10 @@ _mpt3sas_init(void) mpt3sas_ctl_init(hbas_to_enumerate); error = pci_register_driver(&mpt3sas_driver); - if (error) + if (error) { + mpt3sas_ctl_exit(hbas_to_enumerate); scsih_exit(); + } return error; } @@ -11028,10 +12898,10 @@ _mpt3sas_exit(void) pr_info("mpt3sas version %s unloading\n", MPT3SAS_DRIVER_VERSION); - mpt3sas_ctl_exit(hbas_to_enumerate); - pci_unregister_driver(&mpt3sas_driver); + mpt3sas_ctl_exit(hbas_to_enumerate); + scsih_exit(); } diff --git a/drivers/scsi/mpt3sas/mpt3sas_transport.c b/drivers/scsi/mpt3sas/mpt3sas_transport.c index 60ae2d0feb2b..f3400d01cc2a 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_transport.c +++ b/drivers/scsi/mpt3sas/mpt3sas_transport.c @@ -61,9 +61,28 @@ #include "mpt3sas_base.h" /** + * _transport_get_port_id_by_sas_phy - get zone's port id that Phy belong to + * @phy: sas_phy object + * + * Return Port number + */ +static inline u8 +_transport_get_port_id_by_sas_phy(struct sas_phy *phy) +{ + u8 port_id = 0xFF; + struct hba_port *port = phy->hostdata; + + if (port) + port_id = port->port_id; + + return port_id; +} + +/** * _transport_sas_node_find_by_sas_address - sas node search * @ioc: per adapter object * @sas_address: sas address of expander or sas host + * @port: hba port entry * Context: Calling function should acquire ioc->sas_node_lock. * * Search for either hba phys or expander device based on handle, then returns @@ -71,13 +90,56 @@ */ static struct _sas_node * _transport_sas_node_find_by_sas_address(struct MPT3SAS_ADAPTER *ioc, - u64 sas_address) + u64 sas_address, struct hba_port *port) { if (ioc->sas_hba.sas_address == sas_address) return &ioc->sas_hba; else return mpt3sas_scsih_expander_find_by_sas_address(ioc, - sas_address); + sas_address, port); +} + +/** + * _transport_get_port_id_by_rphy - Get Port number from rphy object + * @ioc: per adapter object + * @rphy: sas_rphy object + * + * Returns Port number. + */ +static u8 +_transport_get_port_id_by_rphy(struct MPT3SAS_ADAPTER *ioc, + struct sas_rphy *rphy) +{ + struct _sas_node *sas_expander; + struct _sas_device *sas_device; + unsigned long flags; + u8 port_id = 0xFF; + + if (!rphy) + return port_id; + + if (rphy->identify.device_type == SAS_EDGE_EXPANDER_DEVICE || + rphy->identify.device_type == SAS_FANOUT_EXPANDER_DEVICE) { + spin_lock_irqsave(&ioc->sas_node_lock, flags); + list_for_each_entry(sas_expander, + &ioc->sas_expander_list, list) { + if (sas_expander->rphy == rphy) { + port_id = sas_expander->port->port_id; + break; + } + } + spin_unlock_irqrestore(&ioc->sas_node_lock, flags); + } else if (rphy->identify.device_type == SAS_END_DEVICE) { + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_device = __mpt3sas_get_sdev_by_rphy(ioc, rphy); + if (sas_device) { + port_id = sas_device->port->port_id; + sas_device_put(sas_device); + } + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + } + + return port_id; } /** @@ -104,6 +166,9 @@ _transport_convert_phy_link_rate(u8 link_rate) case MPI25_SAS_NEG_LINK_RATE_12_0: rc = SAS_LINK_RATE_12_0_GBPS; break; + case MPI26_SAS_NEG_LINK_RATE_22_5: + rc = SAS_LINK_RATE_22_5_GBPS; + break; case MPI2_SAS_NEG_LINK_RATE_PHY_DISABLED: rc = SAS_PHY_DISABLED; break; @@ -266,10 +331,10 @@ struct rep_manu_reply { u8 reserved0[2]; u8 sas_format; u8 reserved2[3]; - u8 vendor_id[SAS_EXPANDER_VENDOR_ID_LEN]; - u8 product_id[SAS_EXPANDER_PRODUCT_ID_LEN]; - u8 product_rev[SAS_EXPANDER_PRODUCT_REV_LEN]; - u8 component_vendor_id[SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN]; + u8 vendor_id[SAS_EXPANDER_VENDOR_ID_LEN] __nonstring; + u8 product_id[SAS_EXPANDER_PRODUCT_ID_LEN] __nonstring; + u8 product_rev[SAS_EXPANDER_PRODUCT_REV_LEN] __nonstring; + u8 component_vendor_id[SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN] __nonstring; u16 component_id; u8 component_revision_id; u8 reserved3; @@ -277,10 +342,11 @@ struct rep_manu_reply { }; /** - * transport_expander_report_manufacture - obtain SMP report_manufacture + * _transport_expander_report_manufacture - obtain SMP report_manufacture * @ioc: per adapter object * @sas_address: expander sas address * @edev: the sas_expander_device object + * @port_id: Port ID number * * Fills in the sas_expander_device object when SMP port is created. * @@ -288,7 +354,7 @@ struct rep_manu_reply { */ static int _transport_expander_report_manufacture(struct MPT3SAS_ADAPTER *ioc, - u64 sas_address, struct sas_expander_device *edev) + u64 sas_address, struct sas_expander_device *edev, u8 port_id) { Mpi2SmpPassthroughRequest_t *mpi_request; Mpi2SmpPassthroughReply_t *mpi_reply; @@ -355,7 +421,7 @@ _transport_expander_report_manufacture(struct MPT3SAS_ADAPTER *ioc, memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t)); mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH; - mpi_request->PhysicalPort = 0xFF; + mpi_request->PhysicalPort = port_id; mpi_request->SASAddress = cpu_to_le64(sas_address); mpi_request->RequestDataLength = cpu_to_le16(data_out_sz); psge = &mpi_request->SGL; @@ -367,7 +433,7 @@ _transport_expander_report_manufacture(struct MPT3SAS_ADAPTER *ioc, ioc_info(ioc, "report_manufacture - send to sas_addr(0x%016llx)\n", (u64)sas_address)); init_completion(&ioc->transport_cmds.done); - mpt3sas_base_put_smid_default(ioc, smid); + ioc->put_smid_default(ioc, smid); wait_for_completion_timeout(&ioc->transport_cmds.done, 10*HZ); if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) { @@ -395,17 +461,13 @@ _transport_expander_report_manufacture(struct MPT3SAS_ADAPTER *ioc, goto out; manufacture_reply = data_out + sizeof(struct rep_manu_request); - strncpy(edev->vendor_id, manufacture_reply->vendor_id, - SAS_EXPANDER_VENDOR_ID_LEN); - strncpy(edev->product_id, manufacture_reply->product_id, - SAS_EXPANDER_PRODUCT_ID_LEN); - strncpy(edev->product_rev, manufacture_reply->product_rev, - SAS_EXPANDER_PRODUCT_REV_LEN); + memtostr(edev->vendor_id, manufacture_reply->vendor_id); + memtostr(edev->product_id, manufacture_reply->product_id); + memtostr(edev->product_rev, manufacture_reply->product_rev); edev->level = manufacture_reply->sas_format & 1; if (edev->level) { - strncpy(edev->component_vendor_id, - manufacture_reply->component_vendor_id, - SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN); + memtostr(edev->component_vendor_id, + manufacture_reply->component_vendor_id); tmp = (u8 *)&manufacture_reply->component_id; edev->component_id = tmp[0] << 8 | tmp[1]; edev->component_revision_id = @@ -439,6 +501,7 @@ _transport_delete_port(struct MPT3SAS_ADAPTER *ioc, struct _sas_port *mpt3sas_port) { u64 sas_address = mpt3sas_port->remote_identify.sas_address; + struct hba_port *port = mpt3sas_port->hba_port; enum sas_device_type device_type = mpt3sas_port->remote_identify.device_type; @@ -448,10 +511,11 @@ _transport_delete_port(struct MPT3SAS_ADAPTER *ioc, ioc->logging_level |= MPT_DEBUG_TRANSPORT; if (device_type == SAS_END_DEVICE) - mpt3sas_device_remove_by_sas_address(ioc, sas_address); + mpt3sas_device_remove_by_sas_address(ioc, + sas_address, port); else if (device_type == SAS_EDGE_EXPANDER_DEVICE || device_type == SAS_FANOUT_EXPANDER_DEVICE) - mpt3sas_expander_remove(ioc, sas_address); + mpt3sas_expander_remove(ioc, sas_address, port); ioc->logging_level &= ~MPT_DEBUG_TRANSPORT; } @@ -500,16 +564,17 @@ _transport_add_phy(struct MPT3SAS_ADAPTER *ioc, struct _sas_port *mpt3sas_port, } /** - * _transport_add_phy_to_an_existing_port - adding new phy to existing port + * mpt3sas_transport_add_phy_to_an_existing_port - adding new phy to existing port * @ioc: per adapter object * @sas_node: sas node object (either expander or sas host) * @mpt3sas_phy: mpt3sas per phy object * @sas_address: sas address of device/expander were phy needs to be added to + * @port: hba port entry */ -static void -_transport_add_phy_to_an_existing_port(struct MPT3SAS_ADAPTER *ioc, +void +mpt3sas_transport_add_phy_to_an_existing_port(struct MPT3SAS_ADAPTER *ioc, struct _sas_node *sas_node, struct _sas_phy *mpt3sas_phy, - u64 sas_address) + u64 sas_address, struct hba_port *port) { struct _sas_port *mpt3sas_port; struct _sas_phy *phy_srch; @@ -517,11 +582,16 @@ _transport_add_phy_to_an_existing_port(struct MPT3SAS_ADAPTER *ioc, if (mpt3sas_phy->phy_belongs_to_port == 1) return; + if (!port) + return; + list_for_each_entry(mpt3sas_port, &sas_node->sas_port_list, port_list) { if (mpt3sas_port->remote_identify.sas_address != sas_address) continue; + if (mpt3sas_port->hba_port != port) + continue; list_for_each_entry(phy_srch, &mpt3sas_port->phy_list, port_siblings) { if (phy_srch == mpt3sas_phy) @@ -534,13 +604,13 @@ _transport_add_phy_to_an_existing_port(struct MPT3SAS_ADAPTER *ioc, } /** - * _transport_del_phy_from_an_existing_port - delete phy from existing port + * mpt3sas_transport_del_phy_from_an_existing_port - delete phy from existing port * @ioc: per adapter object * @sas_node: sas node object (either expander or sas host) * @mpt3sas_phy: mpt3sas per phy object */ -static void -_transport_del_phy_from_an_existing_port(struct MPT3SAS_ADAPTER *ioc, +void +mpt3sas_transport_del_phy_from_an_existing_port(struct MPT3SAS_ADAPTER *ioc, struct _sas_node *sas_node, struct _sas_phy *mpt3sas_phy) { struct _sas_port *mpt3sas_port, *next; @@ -556,7 +626,11 @@ _transport_del_phy_from_an_existing_port(struct MPT3SAS_ADAPTER *ioc, if (phy_srch != mpt3sas_phy) continue; - if (mpt3sas_port->num_phys == 1) + /* + * Don't delete port during host reset, + * just delete phy. + */ + if (mpt3sas_port->num_phys == 1 && !ioc->shost_recovery) _transport_delete_port(ioc, mpt3sas_port); else _transport_delete_phy(ioc, mpt3sas_port, @@ -571,21 +645,24 @@ _transport_del_phy_from_an_existing_port(struct MPT3SAS_ADAPTER *ioc, * @ioc: per adapter object * @sas_node: sas node object (either expander or sas host) * @sas_address: sas address of device being added + * @port: hba port entry * * See the explanation above from _transport_delete_duplicate_port */ static void _transport_sanity_check(struct MPT3SAS_ADAPTER *ioc, struct _sas_node *sas_node, - u64 sas_address) + u64 sas_address, struct hba_port *port) { int i; for (i = 0; i < sas_node->num_phys; i++) { if (sas_node->phy[i].remote_identify.sas_address != sas_address) continue; + if (sas_node->phy[i].port != port) + continue; if (sas_node->phy[i].phy_belongs_to_port == 1) - _transport_del_phy_from_an_existing_port(ioc, sas_node, - &sas_node->phy[i]); + mpt3sas_transport_del_phy_from_an_existing_port(ioc, + sas_node, &sas_node->phy[i]); } } @@ -594,6 +671,7 @@ _transport_sanity_check(struct MPT3SAS_ADAPTER *ioc, struct _sas_node *sas_node, * @ioc: per adapter object * @handle: handle of attached device * @sas_address: sas address of parent expander or sas host + * @hba_port: hba port entry * Context: This function will acquire ioc->sas_node_lock. * * Adding new port object to the sas_node->sas_port_list. @@ -602,7 +680,7 @@ _transport_sanity_check(struct MPT3SAS_ADAPTER *ioc, struct _sas_node *sas_node, */ struct _sas_port * mpt3sas_transport_port_add(struct MPT3SAS_ADAPTER *ioc, u16 handle, - u64 sas_address) + u64 sas_address, struct hba_port *hba_port) { struct _sas_phy *mpt3sas_phy, *next; struct _sas_port *mpt3sas_port; @@ -612,6 +690,13 @@ mpt3sas_transport_port_add(struct MPT3SAS_ADAPTER *ioc, u16 handle, struct _sas_device *sas_device = NULL; int i; struct sas_port *port; + struct virtual_phy *vphy = NULL; + + if (!hba_port) { + ioc_err(ioc, "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + return NULL; + } mpt3sas_port = kzalloc(sizeof(struct _sas_port), GFP_KERNEL); @@ -624,7 +709,8 @@ mpt3sas_transport_port_add(struct MPT3SAS_ADAPTER *ioc, u16 handle, INIT_LIST_HEAD(&mpt3sas_port->port_list); INIT_LIST_HEAD(&mpt3sas_port->phy_list); spin_lock_irqsave(&ioc->sas_node_lock, flags); - sas_node = _transport_sas_node_find_by_sas_address(ioc, sas_address); + sas_node = _transport_sas_node_find_by_sas_address(ioc, + sas_address, hba_port); spin_unlock_irqrestore(&ioc->sas_node_lock, flags); if (!sas_node) { @@ -646,16 +732,32 @@ mpt3sas_transport_port_add(struct MPT3SAS_ADAPTER *ioc, u16 handle, goto out_fail; } + mpt3sas_port->hba_port = hba_port; _transport_sanity_check(ioc, sas_node, - mpt3sas_port->remote_identify.sas_address); + mpt3sas_port->remote_identify.sas_address, hba_port); for (i = 0; i < sas_node->num_phys; i++) { if (sas_node->phy[i].remote_identify.sas_address != mpt3sas_port->remote_identify.sas_address) continue; + if (sas_node->phy[i].port != hba_port) + continue; list_add_tail(&sas_node->phy[i].port_siblings, &mpt3sas_port->phy_list); mpt3sas_port->num_phys++; + if (sas_node->handle <= ioc->sas_hba.num_phys) { + if (!sas_node->phy[i].hba_vphy) { + hba_port->phy_mask |= (1 << i); + continue; + } + + vphy = mpt3sas_get_vphy_by_phy(ioc, hba_port, i); + if (!vphy) { + ioc_err(ioc, "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + goto out_fail; + } + } } if (!mpt3sas_port->num_phys) { @@ -664,13 +766,25 @@ mpt3sas_transport_port_add(struct MPT3SAS_ADAPTER *ioc, u16 handle, goto out_fail; } + if (mpt3sas_port->remote_identify.device_type == SAS_END_DEVICE) { + sas_device = mpt3sas_get_sdev_by_addr(ioc, + mpt3sas_port->remote_identify.sas_address, + mpt3sas_port->hba_port); + if (!sas_device) { + ioc_err(ioc, "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + goto out_fail; + } + sas_device->pend_sas_rphy_add = 1; + } + if (!sas_node->parent_dev) { ioc_err(ioc, "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); goto out_fail; } port = sas_port_alloc_num(sas_node->parent_dev); - if ((sas_port_add(port))) { + if (!port || (sas_port_add(port))) { ioc_err(ioc, "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); goto out_fail; @@ -686,32 +800,43 @@ mpt3sas_transport_port_add(struct MPT3SAS_ADAPTER *ioc, u16 handle, mpt3sas_phy->phy_id); sas_port_add_phy(port, mpt3sas_phy->phy); mpt3sas_phy->phy_belongs_to_port = 1; + mpt3sas_phy->port = hba_port; } mpt3sas_port->port = port; - if (mpt3sas_port->remote_identify.device_type == SAS_END_DEVICE) + if (mpt3sas_port->remote_identify.device_type == SAS_END_DEVICE) { rphy = sas_end_device_alloc(port); - else + sas_device->rphy = rphy; + if (sas_node->handle <= ioc->sas_hba.num_phys) { + if (!vphy) + hba_port->sas_address = + sas_device->sas_address; + else + vphy->sas_address = + sas_device->sas_address; + } + } else { rphy = sas_expander_alloc(port, mpt3sas_port->remote_identify.device_type); + if (sas_node->handle <= ioc->sas_hba.num_phys) + hba_port->sas_address = + mpt3sas_port->remote_identify.sas_address; + } - rphy->identify = mpt3sas_port->remote_identify; - - if (mpt3sas_port->remote_identify.device_type == SAS_END_DEVICE) { - sas_device = mpt3sas_get_sdev_by_addr(ioc, - mpt3sas_port->remote_identify.sas_address); - if (!sas_device) { - dfailprintk(ioc, - ioc_info(ioc, "failure at %s:%d/%s()!\n", - __FILE__, __LINE__, __func__)); - goto out_fail; - } - sas_device->pend_sas_rphy_add = 1; + if (!rphy) { + ioc_err(ioc, "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + goto out_delete_port; } + rphy->identify = mpt3sas_port->remote_identify; + if ((sas_rphy_add(rphy))) { ioc_err(ioc, "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); + sas_rphy_free(rphy); + rphy = NULL; + goto out_delete_port; } if (mpt3sas_port->remote_identify.device_type == SAS_END_DEVICE) { @@ -719,11 +844,10 @@ mpt3sas_transport_port_add(struct MPT3SAS_ADAPTER *ioc, u16 handle, sas_device_put(sas_device); } - if ((ioc->logging_level & MPT_DEBUG_TRANSPORT)) - dev_printk(KERN_INFO, &rphy->dev, - "add: handle(0x%04x), sas_addr(0x%016llx)\n", - handle, (unsigned long long) - mpt3sas_port->remote_identify.sas_address); + dev_info(&rphy->dev, + "add: handle(0x%04x), sas_addr(0x%016llx)\n", handle, + (unsigned long long)mpt3sas_port->remote_identify.sas_address); + mpt3sas_port->rphy = rphy; spin_lock_irqsave(&ioc->sas_node_lock, flags); list_add_tail(&mpt3sas_port->port_list, &sas_node->sas_port_list); @@ -736,10 +860,13 @@ mpt3sas_transport_port_add(struct MPT3SAS_ADAPTER *ioc, u16 handle, MPI2_SAS_DEVICE_INFO_FANOUT_EXPANDER) _transport_expander_report_manufacture(ioc, mpt3sas_port->remote_identify.sas_address, - rphy_to_expander_device(rphy)); + rphy_to_expander_device(rphy), hba_port->port_id); return mpt3sas_port; - out_fail: +out_delete_port: + sas_port_delete(port); + +out_fail: list_for_each_entry_safe(mpt3sas_phy, next, &mpt3sas_port->phy_list, port_siblings) list_del(&mpt3sas_phy->port_siblings); @@ -752,6 +879,7 @@ mpt3sas_transport_port_add(struct MPT3SAS_ADAPTER *ioc, u16 handle, * @ioc: per adapter object * @sas_address: sas address of attached device * @sas_address_parent: sas address of parent expander or sas host + * @port: hba port entry * Context: This function will acquire ioc->sas_node_lock. * * Removing object and freeing associated memory from the @@ -759,7 +887,7 @@ mpt3sas_transport_port_add(struct MPT3SAS_ADAPTER *ioc, u16 handle, */ void mpt3sas_transport_port_remove(struct MPT3SAS_ADAPTER *ioc, u64 sas_address, - u64 sas_address_parent) + u64 sas_address_parent, struct hba_port *port) { int i; unsigned long flags; @@ -767,10 +895,15 @@ mpt3sas_transport_port_remove(struct MPT3SAS_ADAPTER *ioc, u64 sas_address, struct _sas_node *sas_node; u8 found = 0; struct _sas_phy *mpt3sas_phy, *next_phy; + struct hba_port *hba_port_next, *hba_port = NULL; + struct virtual_phy *vphy, *vphy_next = NULL; + + if (!port) + return; spin_lock_irqsave(&ioc->sas_node_lock, flags); sas_node = _transport_sas_node_find_by_sas_address(ioc, - sas_address_parent); + sas_address_parent, port); if (!sas_node) { spin_unlock_irqrestore(&ioc->sas_node_lock, flags); return; @@ -779,6 +912,8 @@ mpt3sas_transport_port_remove(struct MPT3SAS_ADAPTER *ioc, u64 sas_address, port_list) { if (mpt3sas_port->remote_identify.sas_address != sas_address) continue; + if (mpt3sas_port->hba_port != port) + continue; found = 1; list_del(&mpt3sas_port->port_list); goto out; @@ -789,6 +924,61 @@ mpt3sas_transport_port_remove(struct MPT3SAS_ADAPTER *ioc, u64 sas_address, return; } + if (sas_node->handle <= ioc->sas_hba.num_phys && + (ioc->multipath_on_hba)) { + if (port->vphys_mask) { + list_for_each_entry_safe(vphy, vphy_next, + &port->vphys_list, list) { + if (vphy->sas_address != sas_address) + continue; + ioc_info(ioc, + "remove vphy entry: %p of port:%p,from %d port's vphys list\n", + vphy, port, port->port_id); + port->vphys_mask &= ~vphy->phy_mask; + list_del(&vphy->list); + kfree(vphy); + } + } + + list_for_each_entry_safe(hba_port, hba_port_next, + &ioc->port_table_list, list) { + if (hba_port != port) + continue; + /* + * Delete hba_port object if + * - hba_port object's sas address matches with current + * removed device's sas address and no vphy's + * associated with it. + * - Current removed device is a vSES device and + * none of the other direct attached device have + * this vSES device's port number (hence hba_port + * object sas_address field will be zero). + */ + if ((hba_port->sas_address == sas_address || + !hba_port->sas_address) && !hba_port->vphys_mask) { + ioc_info(ioc, + "remove hba_port entry: %p port: %d from hba_port list\n", + hba_port, hba_port->port_id); + list_del(&hba_port->list); + kfree(hba_port); + } else if (hba_port->sas_address == sas_address && + hba_port->vphys_mask) { + /* + * Current removed device is a non vSES device + * and a vSES device has the same port number + * as of current device's port number. Hence + * only clear the sas_address filed, don't + * delete the hba_port object. + */ + ioc_info(ioc, + "clearing sas_address from hba_port entry: %p port: %d from hba_port list\n", + hba_port, hba_port->port_id); + port->sas_address = 0; + } + break; + } + } + for (i = 0; i < sas_node->num_phys; i++) { if (sas_node->phy[i].remote_identify.sas_address == sas_address) memset(&sas_node->phy[i].remote_identify, 0 , @@ -800,11 +990,9 @@ mpt3sas_transport_port_remove(struct MPT3SAS_ADAPTER *ioc, u64 sas_address, list_for_each_entry_safe(mpt3sas_phy, next_phy, &mpt3sas_port->phy_list, port_siblings) { if ((ioc->logging_level & MPT_DEBUG_TRANSPORT)) - dev_printk(KERN_INFO, &mpt3sas_port->port->dev, - "remove: sas_addr(0x%016llx), phy(%d)\n", - (unsigned long long) - mpt3sas_port->remote_identify.sas_address, - mpt3sas_phy->phy_id); + ioc_info(ioc, "remove: sas_addr(0x%016llx), phy(%d)\n", + (unsigned long long) mpt3sas_port->remote_identify.sas_address, + mpt3sas_phy->phy_id); mpt3sas_phy->phy_belongs_to_port = 0; if (!ioc->remove_host) sas_port_delete_phy(mpt3sas_port->port, @@ -813,6 +1001,8 @@ mpt3sas_transport_port_remove(struct MPT3SAS_ADAPTER *ioc, u64 sas_address, } if (!ioc->remove_host) sas_port_delete(mpt3sas_port->port); + ioc_info(ioc, "%s: removed: sas_addr(0x%016llx)\n", + __func__, (unsigned long long)sas_address); kfree(mpt3sas_port); } @@ -863,6 +1053,7 @@ mpt3sas_transport_add_host_phy(struct MPT3SAS_ADAPTER *ioc, struct _sas_phy phy_pg0.ProgrammedLinkRate & MPI2_SAS_PRATE_MIN_RATE_MASK); phy->maximum_linkrate = _transport_convert_phy_link_rate( phy_pg0.ProgrammedLinkRate >> 4); + phy->hostdata = mpt3sas_phy->port; if ((sas_phy_add(phy))) { ioc_err(ioc, "failure at %s:%d/%s()!\n", @@ -933,6 +1124,7 @@ mpt3sas_transport_add_expander_phy(struct MPT3SAS_ADAPTER *ioc, struct _sas_phy expander_pg1.ProgrammedLinkRate & MPI2_SAS_PRATE_MIN_RATE_MASK); phy->maximum_linkrate = _transport_convert_phy_link_rate( expander_pg1.ProgrammedLinkRate >> 4); + phy->hostdata = mpt3sas_phy->port; if ((sas_phy_add(phy))) { ioc_err(ioc, "failure at %s:%d/%s()!\n", @@ -960,20 +1152,26 @@ mpt3sas_transport_add_expander_phy(struct MPT3SAS_ADAPTER *ioc, struct _sas_phy * @handle: attached device handle * @phy_number: phy number * @link_rate: new link rate + * @port: hba port entry + * + * Return nothing. */ void mpt3sas_transport_update_links(struct MPT3SAS_ADAPTER *ioc, - u64 sas_address, u16 handle, u8 phy_number, u8 link_rate) + u64 sas_address, u16 handle, u8 phy_number, u8 link_rate, + struct hba_port *port) { unsigned long flags; struct _sas_node *sas_node; struct _sas_phy *mpt3sas_phy; + struct hba_port *hba_port = NULL; if (ioc->shost_recovery || ioc->pci_error_recovery) return; spin_lock_irqsave(&ioc->sas_node_lock, flags); - sas_node = _transport_sas_node_find_by_sas_address(ioc, sas_address); + sas_node = _transport_sas_node_find_by_sas_address(ioc, + sas_address, port); if (!sas_node) { spin_unlock_irqrestore(&ioc->sas_node_lock, flags); return; @@ -985,8 +1183,19 @@ mpt3sas_transport_update_links(struct MPT3SAS_ADAPTER *ioc, if (handle && (link_rate >= MPI2_SAS_NEG_LINK_RATE_1_5)) { _transport_set_identify(ioc, handle, &mpt3sas_phy->remote_identify); - _transport_add_phy_to_an_existing_port(ioc, sas_node, - mpt3sas_phy, mpt3sas_phy->remote_identify.sas_address); + if ((sas_node->handle <= ioc->sas_hba.num_phys) && + (ioc->multipath_on_hba)) { + list_for_each_entry(hba_port, + &ioc->port_table_list, list) { + if (hba_port->sas_address == sas_address && + hba_port == port) + hba_port->phy_mask |= + (1 << mpt3sas_phy->phy_id); + } + } + mpt3sas_transport_add_phy_to_an_existing_port(ioc, sas_node, + mpt3sas_phy, mpt3sas_phy->remote_identify.sas_address, + port); } else memset(&mpt3sas_phy->remote_identify, 0 , sizeof(struct sas_identify)); @@ -1121,7 +1330,7 @@ _transport_get_expander_phy_error_log(struct MPT3SAS_ADAPTER *ioc, memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t)); mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH; - mpi_request->PhysicalPort = 0xFF; + mpi_request->PhysicalPort = _transport_get_port_id_by_sas_phy(phy); mpi_request->VF_ID = 0; /* TODO */ mpi_request->VP_ID = 0; mpi_request->SASAddress = cpu_to_le64(phy->identify.sas_address); @@ -1139,7 +1348,7 @@ _transport_get_expander_phy_error_log(struct MPT3SAS_ADAPTER *ioc, (u64)phy->identify.sas_address, phy->number)); init_completion(&ioc->transport_cmds.done); - mpt3sas_base_put_smid_default(ioc, smid); + ioc->put_smid_default(ioc, smid); wait_for_completion_timeout(&ioc->transport_cmds.done, 10*HZ); if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) { @@ -1211,10 +1420,13 @@ _transport_get_linkerrors(struct sas_phy *phy) unsigned long flags; Mpi2ConfigReply_t mpi_reply; Mpi2SasPhyPage1_t phy_pg1; + struct hba_port *port = phy->hostdata; + int port_id = port->port_id; spin_lock_irqsave(&ioc->sas_node_lock, flags); if (_transport_sas_node_find_by_sas_address(ioc, - phy->identify.sas_address) == NULL) { + phy->identify.sas_address, + mpt3sas_get_port_by_id(ioc, port_id, 0)) == NULL) { spin_unlock_irqrestore(&ioc->sas_node_lock, flags); return -EINVAL; } @@ -1264,8 +1476,7 @@ _transport_get_enclosure_identifier(struct sas_rphy *rphy, u64 *identifier) int rc; spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = __mpt3sas_get_sdev_by_addr(ioc, - rphy->identify.sas_address); + sas_device = __mpt3sas_get_sdev_by_rphy(ioc, rphy); if (sas_device) { *identifier = sas_device->enclosure_logical_id; rc = 0; @@ -1294,8 +1505,7 @@ _transport_get_bay_identifier(struct sas_rphy *rphy) int rc; spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = __mpt3sas_get_sdev_by_addr(ioc, - rphy->identify.sas_address); + sas_device = __mpt3sas_get_sdev_by_rphy(ioc, rphy); if (sas_device) { rc = sas_device->slot; sas_device_put(sas_device); @@ -1416,7 +1626,7 @@ _transport_expander_phy_control(struct MPT3SAS_ADAPTER *ioc, memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t)); mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH; - mpi_request->PhysicalPort = 0xFF; + mpi_request->PhysicalPort = _transport_get_port_id_by_sas_phy(phy); mpi_request->VF_ID = 0; /* TODO */ mpi_request->VP_ID = 0; mpi_request->SASAddress = cpu_to_le64(phy->identify.sas_address); @@ -1434,7 +1644,7 @@ _transport_expander_phy_control(struct MPT3SAS_ADAPTER *ioc, (u64)phy->identify.sas_address, phy->number, phy_operation)); init_completion(&ioc->transport_cmds.done); - mpt3sas_base_put_smid_default(ioc, smid); + ioc->put_smid_default(ioc, smid); wait_for_completion_timeout(&ioc->transport_cmds.done, 10*HZ); if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) { @@ -1498,11 +1708,14 @@ _transport_phy_reset(struct sas_phy *phy, int hard_reset) struct MPT3SAS_ADAPTER *ioc = phy_to_ioc(phy); Mpi2SasIoUnitControlReply_t mpi_reply; Mpi2SasIoUnitControlRequest_t mpi_request; + struct hba_port *port = phy->hostdata; + int port_id = port->port_id; unsigned long flags; spin_lock_irqsave(&ioc->sas_node_lock, flags); if (_transport_sas_node_find_by_sas_address(ioc, - phy->identify.sas_address) == NULL) { + phy->identify.sas_address, + mpt3sas_get_port_by_id(ioc, port_id, 0)) == NULL) { spin_unlock_irqrestore(&ioc->sas_node_lock, flags); return -EINVAL; } @@ -1555,10 +1768,13 @@ _transport_phy_enable(struct sas_phy *phy, int enable) int rc = 0; unsigned long flags; int i, discovery_active; + struct hba_port *port = phy->hostdata; + int port_id = port->port_id; spin_lock_irqsave(&ioc->sas_node_lock, flags); if (_transport_sas_node_find_by_sas_address(ioc, - phy->identify.sas_address) == NULL) { + phy->identify.sas_address, + mpt3sas_get_port_by_id(ioc, port_id, 0)) == NULL) { spin_unlock_irqrestore(&ioc->sas_node_lock, flags); return -EINVAL; } @@ -1573,8 +1789,7 @@ _transport_phy_enable(struct sas_phy *phy, int enable) /* handle hba phys */ /* read sas_iounit page 0 */ - sz = offsetof(Mpi2SasIOUnitPage0_t, PhyData) + (ioc->sas_hba.num_phys * - sizeof(Mpi2SasIOUnit0PhyData_t)); + sz = struct_size(sas_iounit_pg0, PhyData, ioc->sas_hba.num_phys); sas_iounit_pg0 = kzalloc(sz, GFP_KERNEL); if (!sas_iounit_pg0) { ioc_err(ioc, "failure at %s:%d/%s()!\n", @@ -1614,8 +1829,7 @@ _transport_phy_enable(struct sas_phy *phy, int enable) } /* read sas_iounit page 1 */ - sz = offsetof(Mpi2SasIOUnitPage1_t, PhyData) + (ioc->sas_hba.num_phys * - sizeof(Mpi2SasIOUnit1PhyData_t)); + sz = struct_size(sas_iounit_pg1, PhyData, ioc->sas_hba.num_phys); sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL); if (!sas_iounit_pg1) { ioc_err(ioc, "failure at %s:%d/%s()!\n", @@ -1692,10 +1906,13 @@ _transport_phy_speed(struct sas_phy *phy, struct sas_phy_linkrates *rates) int i; int rc = 0; unsigned long flags; + struct hba_port *port = phy->hostdata; + int port_id = port->port_id; spin_lock_irqsave(&ioc->sas_node_lock, flags); if (_transport_sas_node_find_by_sas_address(ioc, - phy->identify.sas_address) == NULL) { + phy->identify.sas_address, + mpt3sas_get_port_by_id(ioc, port_id, 0)) == NULL) { spin_unlock_irqrestore(&ioc->sas_node_lock, flags); return -EINVAL; } @@ -1722,8 +1939,7 @@ _transport_phy_speed(struct sas_phy *phy, struct sas_phy_linkrates *rates) /* handle hba phys */ /* sas_iounit page 1 */ - sz = offsetof(Mpi2SasIOUnitPage1_t, PhyData) + (ioc->sas_hba.num_phys * - sizeof(Mpi2SasIOUnit1PhyData_t)); + sz = struct_size(sas_iounit_pg1, PhyData, ioc->sas_hba.num_phys); sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL); if (!sas_iounit_pg1) { ioc_err(ioc, "failure at %s:%d/%s()!\n", @@ -1897,7 +2113,7 @@ _transport_smp_handler(struct bsg_job *job, struct Scsi_Host *shost, memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t)); mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH; - mpi_request->PhysicalPort = 0xFF; + mpi_request->PhysicalPort = _transport_get_port_id_by_rphy(ioc, rphy); mpi_request->SASAddress = (rphy) ? cpu_to_le64(rphy->identify.sas_address) : cpu_to_le64(ioc->sas_hba.sas_address); @@ -1911,7 +2127,7 @@ _transport_smp_handler(struct bsg_job *job, struct Scsi_Host *shost, ioc_info(ioc, "%s: sending smp request\n", __func__)); init_completion(&ioc->transport_cmds.done); - mpt3sas_base_put_smid_default(ioc, smid); + ioc->put_smid_default(ioc, smid); wait_for_completion_timeout(&ioc->transport_cmds.done, 10*HZ); if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) { diff --git a/drivers/scsi/mpt3sas/mpt3sas_trigger_diag.c b/drivers/scsi/mpt3sas/mpt3sas_trigger_diag.c index 6ac453fd5937..d9b7d0ee25b0 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_trigger_diag.c +++ b/drivers/scsi/mpt3sas/mpt3sas_trigger_diag.c @@ -113,19 +113,54 @@ mpt3sas_process_trigger_data(struct MPT3SAS_ADAPTER *ioc, struct SL_WH_TRIGGERS_EVENT_DATA_T *event_data) { u8 issue_reset = 0; + u32 *trig_data = (u32 *)&event_data->u.master; dTriggerDiagPrintk(ioc, ioc_info(ioc, "%s: enter\n", __func__)); /* release the diag buffer trace */ if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & MPT3_DIAG_BUFFER_IS_RELEASED) == 0) { - dTriggerDiagPrintk(ioc, - ioc_info(ioc, "%s: release trace diag buffer\n", - __func__)); + /* + * add a log message so that user knows which event caused + * the release + */ + ioc_info(ioc, + "%s: Releasing the trace buffer. Trigger_Type 0x%08x, Data[0] 0x%08x, Data[1] 0x%08x\n", + __func__, event_data->trigger_type, + trig_data[0], trig_data[1]); mpt3sas_send_diag_release(ioc, MPI2_DIAG_BUF_TYPE_TRACE, &issue_reset); } + ioc->htb_rel.buffer_rel_condition = MPT3_DIAG_BUFFER_REL_TRIGGER; + if (event_data) { + ioc->htb_rel.trigger_type = event_data->trigger_type; + switch (event_data->trigger_type) { + case MPT3SAS_TRIGGER_SCSI: + memcpy(&ioc->htb_rel.trigger_info_dwords, + &event_data->u.scsi, + sizeof(struct SL_WH_SCSI_TRIGGER_T)); + break; + case MPT3SAS_TRIGGER_MPI: + memcpy(&ioc->htb_rel.trigger_info_dwords, + &event_data->u.mpi, + sizeof(struct SL_WH_MPI_TRIGGER_T)); + break; + case MPT3SAS_TRIGGER_MASTER: + ioc->htb_rel.trigger_info_dwords[0] = + event_data->u.master.MasterData; + break; + case MPT3SAS_TRIGGER_EVENT: + memcpy(&ioc->htb_rel.trigger_info_dwords, + &event_data->u.event, + sizeof(struct SL_WH_EVENT_TRIGGER_T)); + break; + default: + ioc_err(ioc, "%d - Is not a valid Trigger type\n", + event_data->trigger_type); + break; + } + } _mpt3sas_raise_sigio(ioc, event_data); dTriggerDiagPrintk(ioc, ioc_info(ioc, "%s: exit\n", @@ -195,9 +230,14 @@ mpt3sas_trigger_master(struct MPT3SAS_ADAPTER *ioc, u32 trigger_bitmask) event_data.u.master.MasterData = trigger_bitmask; if (trigger_bitmask & MASTER_TRIGGER_FW_FAULT || - trigger_bitmask & MASTER_TRIGGER_ADAPTER_RESET) + trigger_bitmask & MASTER_TRIGGER_ADAPTER_RESET) { + ioc->htb_rel.trigger_type = MPT3SAS_TRIGGER_MASTER; + ioc->htb_rel.trigger_info_dwords[0] = trigger_bitmask; + if (ioc->reset_from_user) + ioc->htb_rel.trigger_info_dwords[1] = + MPT_DIAG_RESET_ISSUED_BY_USER; _mpt3sas_raise_sigio(ioc, &event_data); - else + } else mpt3sas_send_trigger_data_event(ioc, &event_data); out: diff --git a/drivers/scsi/mpt3sas/mpt3sas_trigger_diag.h b/drivers/scsi/mpt3sas/mpt3sas_trigger_diag.h index 6586a463bea9..405eada2669d 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_trigger_diag.h +++ b/drivers/scsi/mpt3sas/mpt3sas_trigger_diag.h @@ -69,7 +69,7 @@ #define MASTER_TRIGGER_TASK_MANAGMENT (0x00000004) #define MASTER_TRIGGER_DEVICE_REMOVAL (0x00000008) -/* fake firmware event for tigger */ +/* fake firmware event for trigger */ #define MPI3_EVENT_DIAGNOSTIC_TRIGGER_FIRED (0x6E) /** diff --git a/drivers/scsi/mpt3sas/mpt3sas_trigger_pages.h b/drivers/scsi/mpt3sas/mpt3sas_trigger_pages.h new file mode 100644 index 000000000000..edb8fe709089 --- /dev/null +++ b/drivers/scsi/mpt3sas/mpt3sas_trigger_pages.h @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* + * This is the Fusion MPT base driver providing common API layer interface + * to store diag trigger values into persistent driver triggers pages + * for MPT (Message Passing Technology) based controllers. + * + * Copyright (C) 2020 Broadcom Inc. + * + * Authors: Broadcom Inc. + * Sreekanth Reddy <sreekanth.reddy@broadcom.com> + * + * Send feedback to : MPT-FusionLinux.pdl@broadcom.com) + */ + +#include "mpi/mpi2_cnfg.h" + +#ifndef MPI2_TRIGGER_PAGES_H +#define MPI2_TRIGGER_PAGES_H + +#define MPI2_CONFIG_EXTPAGETYPE_DRIVER_PERSISTENT_TRIGGER (0xE0) +#define MPI26_DRIVER_TRIGGER_PAGE0_PAGEVERSION (0x01) +typedef struct _MPI26_CONFIG_PAGE_DRIVER_TRIGGER_0 { + MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ + U16 TriggerFlags; /* 0x08 */ + U16 Reserved0xA; /* 0x0A */ + U32 Reserved0xC[61]; /* 0x0C */ +} _MPI26_CONFIG_PAGE_DRIVER_TRIGGER_0, Mpi26DriverTriggerPage0_t; + +/* Trigger Flags */ +#define MPI26_DRIVER_TRIGGER0_FLAG_MASTER_TRIGGER_VALID (0x0001) +#define MPI26_DRIVER_TRIGGER0_FLAG_MPI_EVENT_TRIGGER_VALID (0x0002) +#define MPI26_DRIVER_TRIGGER0_FLAG_SCSI_SENSE_TRIGGER_VALID (0x0004) +#define MPI26_DRIVER_TRIGGER0_FLAG_LOGINFO_TRIGGER_VALID (0x0008) + +#define MPI26_DRIVER_TRIGGER_PAGE1_PAGEVERSION (0x01) +typedef struct _MPI26_DRIVER_MASTER_TRIGGER_ENTRY { + U32 MasterTriggerFlags; +} MPI26_DRIVER_MASTER_TRIGGER_ENTRY; + +#define MPI26_MAX_MASTER_TRIGGERS (1) +typedef struct _MPI26_CONFIG_PAGE_DRIVER_TRIGGER_1 { + MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ + U16 NumMasterTrigger; /* 0x08 */ + U16 Reserved0xA; /* 0x0A */ + MPI26_DRIVER_MASTER_TRIGGER_ENTRY MasterTriggers[MPI26_MAX_MASTER_TRIGGERS]; /* 0x0C */ +} MPI26_CONFIG_PAGE_DRIVER_TRIGGER_1, Mpi26DriverTriggerPage1_t; + +#define MPI26_DRIVER_TRIGGER_PAGE2_PAGEVERSION (0x01) +typedef struct _MPI26_DRIVER_MPI_EVENT_TRIGGER_ENTRY { + U16 MPIEventCode; /* 0x00 */ + U16 MPIEventCodeSpecific; /* 0x02 */ +} MPI26_DRIVER_MPI_EVENT_TRIGGER_ENTRY; + +#define MPI26_MAX_MPI_EVENT_TRIGGERS (20) +typedef struct _MPI26_CONFIG_PAGE_DRIVER_TRIGGER_2 { + MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ + U16 NumMPIEventTrigger; /* 0x08 */ + U16 Reserved0xA; /* 0x0A */ + MPI26_DRIVER_MPI_EVENT_TRIGGER_ENTRY MPIEventTriggers[MPI26_MAX_MPI_EVENT_TRIGGERS]; /* 0x0C */ +} MPI26_CONFIG_PAGE_DRIVER_TRIGGER_2, Mpi26DriverTriggerPage2_t; + +#define MPI26_DRIVER_TRIGGER_PAGE3_PAGEVERSION (0x01) +typedef struct _MPI26_DRIVER_SCSI_SENSE_TRIGGER_ENTRY { + U8 ASCQ; /* 0x00 */ + U8 ASC; /* 0x01 */ + U8 SenseKey; /* 0x02 */ + U8 Reserved; /* 0x03 */ +} MPI26_DRIVER_SCSI_SENSE_TRIGGER_ENTRY; + +#define MPI26_MAX_SCSI_SENSE_TRIGGERS (20) +typedef struct _MPI26_CONFIG_PAGE_DRIVER_TRIGGER_3 { + MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ + U16 NumSCSISenseTrigger; /* 0x08 */ + U16 Reserved0xA; /* 0x0A */ + MPI26_DRIVER_SCSI_SENSE_TRIGGER_ENTRY SCSISenseTriggers[MPI26_MAX_SCSI_SENSE_TRIGGERS]; /* 0x0C */ +} MPI26_CONFIG_PAGE_DRIVER_TRIGGER_3, Mpi26DriverTriggerPage3_t; + +#define MPI26_DRIVER_TRIGGER_PAGE4_PAGEVERSION (0x01) +typedef struct _MPI26_DRIVER_IOCSTATUS_LOGINFO_TRIGGER_ENTRY { + U16 IOCStatus; /* 0x00 */ + U16 Reserved; /* 0x02 */ + U32 LogInfo; /* 0x04 */ +} MPI26_DRIVER_IOCSTATUS_LOGINFO_TRIGGER_ENTRY; + +#define MPI26_MAX_LOGINFO_TRIGGERS (20) +typedef struct _MPI26_CONFIG_PAGE_DRIVER_TRIGGER_4 { + MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ + U16 NumIOCStatusLogInfoTrigger; /* 0x08 */ + U16 Reserved0xA; /* 0x0A */ + MPI26_DRIVER_IOCSTATUS_LOGINFO_TRIGGER_ENTRY IOCStatusLoginfoTriggers[MPI26_MAX_LOGINFO_TRIGGERS]; /* 0x0C */ +} MPI26_CONFIG_PAGE_DRIVER_TRIGGER_4, Mpi26DriverTriggerPage4_t; + +#endif diff --git a/drivers/scsi/mpt3sas/mpt3sas_warpdrive.c b/drivers/scsi/mpt3sas/mpt3sas_warpdrive.c index cc07ba41f507..2b04f0852dec 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_warpdrive.c +++ b/drivers/scsi/mpt3sas/mpt3sas_warpdrive.c @@ -42,7 +42,7 @@ #include <linux/module.h> #include <linux/errno.h> #include <linux/types.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include "mpt3sas_base.h" @@ -141,8 +141,7 @@ mpt3sas_init_warpdrive_properties(struct MPT3SAS_ADAPTER *ioc, return; } - sz = offsetof(Mpi2RaidVolPage0_t, PhysDisk) + (num_pds * - sizeof(Mpi2RaidVol0PhysDisk_t)); + sz = struct_size(vol_pg0, PhysDisk, num_pds); vol_pg0 = kzalloc(sz, GFP_KERNEL); if (!vol_pg0) { ioc_info(ioc, "WarpDrive : Direct IO is disabled Memory allocation failure for RVPG0\n"); |
