Index: mpi/mpi2.h =================================================================== --- mpi/mpi2.h +++ mpi/mpi2.h @@ -44,7 +44,7 @@ * scatter/gather formats. * Creation Date: June 21, 2006 * - * mpi2.h Version: 02.00.42 + * mpi2.h Version: 02.00.46 * * 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 @@ -132,7 +132,8 @@ * Bumped MPI2_HEADER_VERSION_UNIT. * 03-16-15 02.00.37 Updated for MPI v2.6. * Bumped MPI2_HEADER_VERSION_UNIT. - * Added Scratchpad registers to + * Added Scratchpad registers and + * AtomicRequestDescriptorPost register to * MPI2_SYSTEM_INTERFACE_REGS. * Added MPI2_DIAG_SBR_RELOAD. * Added MPI2_IOCSTATUS_INSUFFICIENT_POWER. @@ -142,6 +143,14 @@ * Added V7 HostDiagnostic register defines * 12-15-15 02.00.41 Bumped MPI_HEADER_VERSION_UNIT * 01-01-16 02.00.42 Bumped MPI_HEADER_VERSION_UNIT + * 04-05-16 02.00.43 Modified MPI26_DIAG_BOOT_DEVICE_SELECT defines + * to be unique within first 32 characters. + * Removed AHCI support. + * Removed SOP support. + * Bumped MPI2_HEADER_VERSION_UNIT. + * 04-10-16 02.00.44 Bumped MPI2_HEADER_VERSION_UNIT. + * 07-06-16 02.00.45 Bumped MPI2_HEADER_VERSION_UNIT. + * 09-02-16 02.00.46 Bumped MPI2_HEADER_VERSION_UNIT. * -------------------------------------------------------------------------- */ @@ -185,7 +194,7 @@ /* Unit and Dev versioning for this MPI header set */ -#define MPI2_HEADER_VERSION_UNIT (0x2A) +#define MPI2_HEADER_VERSION_UNIT (0x2E) #define MPI2_HEADER_VERSION_DEV (0x00) #define MPI2_HEADER_VERSION_UNIT_MASK (0xFF00) #define MPI2_HEADER_VERSION_UNIT_SHIFT (8) @@ -245,7 +254,8 @@ U32 Scratchpad[4]; /* 0xB0 */ U32 RequestDescriptorPostLow; /* 0xC0 */ U32 RequestDescriptorPostHigh; /* 0xC4 */ - U32 Reserved7[14]; /* 0xC8 */ + U32 AtomicRequestDescriptorPost;/* 0xC8 */ /* MPI v2.6 and later; reserved in earlier versions */ + U32 Reserved7[13]; /* 0xCC */ } MPI2_SYSTEM_INTERFACE_REGS, MPI2_POINTER PTR_MPI2_SYSTEM_INTERFACE_REGS, Mpi2SystemInterfaceRegs_t, MPI2_POINTER pMpi2SystemInterfaceRegs_t; @@ -293,10 +303,11 @@ #define MPI2_DIAG_BOOT_DEVICE_SELECT_HCDW (0x00000800) /* Defines for V7A/V7R HostDiagnostic Register */ -#define MPI26_DIAG_BOOT_DEVICE_SELECT_FLASH64 (0x00000000) -#define MPI26_DIAG_BOOT_DEVICE_SELECT_HCDW64 (0x00000800) -#define MPI26_DIAG_BOOT_DEVICE_SELECT_FLASH32 (0x00001000) -#define MPI26_DIAG_BOOT_DEVICE_SELECT_HCDW32 (0x00001800) +#define MPI26_DIAG_BOOT_DEVICE_SEL_64FLASH (0x00000000) +#define MPI26_DIAG_BOOT_DEVICE_SEL_64HCDW (0x00000800) +#define MPI26_DIAG_BOOT_DEVICE_SEL_32FLASH (0x00001000) +#define MPI26_DIAG_BOOT_DEVICE_SEL_32HCDW (0x00001800) + #define MPI2_DIAG_CLEAR_FLASH_BAD_SIG (0x00000400) #define MPI2_DIAG_FORCE_HCB_ON_RESET (0x00000200) #define MPI2_DIAG_HCB_MODE (0x00000100) @@ -379,6 +390,7 @@ */ #define MPI2_REQUEST_DESCRIPTOR_POST_LOW_OFFSET (0x000000C0) #define MPI2_REQUEST_DESCRIPTOR_POST_HIGH_OFFSET (0x000000C4) +#define MPI26_ATOMIC_REQUEST_DESCRIPTOR_POST_OFFSET (0x000000C8) /* Hard Reset delay timings */ @@ -415,6 +427,7 @@ #define MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE (0x08) #define MPI2_REQ_DESCRIPT_FLAGS_RAID_ACCELERATOR (0x0A) #define MPI25_REQ_DESCRIPT_FLAGS_FAST_PATH_SCSI_IO (0x0C) +#define MPI26_REQ_DESCRIPT_FLAGS_PCIE_ENCAPSULATED (0x10) #define MPI2_REQ_DESCRIPT_FLAGS_IOC_FIFO_MARKER (0x01) @@ -482,6 +495,14 @@ MPI2_POINTER pMpi25FastPathSCSIIORequestDescriptor_t; +/* PCIe Encapsulated Request Descriptor */ +typedef MPI2_SCSI_IO_REQUEST_DESCRIPTOR + MPI26_PCIE_ENCAPSULATED_REQUEST_DESCRIPTOR, + MPI2_POINTER PTR_MPI26_PCIE_ENCAPSULATED_REQUEST_DESCRIPTOR, + Mpi26PCIeEncapsulatedRequestDescriptor_t, + MPI2_POINTER pMpi26PCIeEncapsulatedRequestDescriptor_t; + + /* union of Request Descriptors */ typedef union _MPI2_REQUEST_DESCRIPTOR_UNION { @@ -491,11 +512,35 @@ MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR SCSITarget; MPI2_RAID_ACCEL_REQUEST_DESCRIPTOR RAIDAccelerator; MPI25_FP_SCSI_IO_REQUEST_DESCRIPTOR FastPathSCSIIO; + MPI26_PCIE_ENCAPSULATED_REQUEST_DESCRIPTOR PCIeEncapsulated; U64 Words; } MPI2_REQUEST_DESCRIPTOR_UNION, MPI2_POINTER PTR_MPI2_REQUEST_DESCRIPTOR_UNION, Mpi2RequestDescriptorUnion_t, MPI2_POINTER pMpi2RequestDescriptorUnion_t; +/* Atomic Request Descriptors */ + +/* + * All Atomic Request Descriptors have the same format, so the following + * structure is used for all Atomic Request Descriptors: + * Atomic Default Request Descriptor + * Atomic High Priority Request Descriptor + * Atomic SCSI IO Request Descriptor + * Atomic SCSI Target Request Descriptor + * Atomic RAID Accelerator Request Descriptor + * Atomic Fast Path SCSI IO Request Descriptor + * Atomic PCIe Encapsulated Request Descriptor + */ + +/* Atomic Request Descriptor */ +typedef struct _MPI26_ATOMIC_REQUEST_DESCRIPTOR +{ + U8 RequestFlags; /* 0x00 */ + U8 MSIxIndex; /* 0x01 */ + U16 SMID; /* 0x02 */ +} MPI26_ATOMIC_REQUEST_DESCRIPTOR, + MPI2_POINTER PTR_MPI26_ATOMIC_REQUEST_DESCRIPTOR, + Mpi26AtomicRequestDescriptor_t, MPI2_POINTER pMpi26AtomicRequestDescriptor_t; /* for the RequestFlags field, use the same defines as MPI2_DEFAULT_REQUEST_DESCRIPTOR */ @@ -520,6 +565,7 @@ #define MPI2_RPY_DESCRIPT_FLAGS_TARGET_COMMAND_BUFFER (0x03) #define MPI2_RPY_DESCRIPT_FLAGS_RAID_ACCELERATOR_SUCCESS (0x05) #define MPI25_RPY_DESCRIPT_FLAGS_FAST_PATH_SCSI_IO_SUCCESS (0x06) +#define MPI26_RPY_DESCRIPT_FLAGS_PCIE_ENCAPSULATED_SUCCESS (0x08) #define MPI2_RPY_DESCRIPT_FLAGS_UNUSED (0x0F) /* values for marking a reply descriptor as unused */ @@ -607,6 +653,14 @@ MPI2_POINTER pMpi25FastPathSCSIIOSuccessReplyDescriptor_t; +/* PCIe Encapsulated Success Reply Descriptor */ +typedef MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR + MPI26_PCIE_ENCAPSULATED_SUCCESS_REPLY_DESCRIPTOR, + MPI2_POINTER PTR_MPI26_PCIE_ENCAPSULATED_SUCCESS_REPLY_DESCRIPTOR, + Mpi26PCIeEncapsulatedSuccessReplyDescriptor_t, + MPI2_POINTER pMpi26PCIeEncapsulatedSuccessReplyDescriptor_t; + + /* union of Reply Descriptors */ typedef union _MPI2_REPLY_DESCRIPTORS_UNION { @@ -617,6 +671,7 @@ MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR TargetCommandBuffer; MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR RAIDAcceleratorSuccess; MPI25_FP_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR FastPathSCSIIOSuccess; + MPI26_PCIE_ENCAPSULATED_SUCCESS_REPLY_DESCRIPTOR PCIeEncapsulatedSuccess; U64 Words; } MPI2_REPLY_DESCRIPTORS_UNION, MPI2_POINTER PTR_MPI2_REPLY_DESCRIPTORS_UNION, Mpi2ReplyDescriptorsUnion_t, MPI2_POINTER pMpi2ReplyDescriptorsUnion_t; @@ -659,6 +714,7 @@ #define MPI2_FUNCTION_HOST_BASED_DISCOVERY_ACTION (0x2F) /* Host Based Discovery Action */ #define MPI2_FUNCTION_PWR_MGMT_CONTROL (0x30) /* Power Management Control */ #define MPI2_FUNCTION_SEND_HOST_MESSAGE (0x31) /* Send Host Message */ +#define MPI2_FUNCTION_NVME_ENCAPSULATED (0x33) /* NVMe Encapsulated (MPI v2.6) */ #define MPI2_FUNCTION_MIN_PRODUCT_SPECIFIC (0xF0) /* beginning of product-specific range */ #define MPI2_FUNCTION_MAX_PRODUCT_SPECIFIC (0xFF) /* end of product-specific range */ @@ -1232,6 +1288,8 @@ #define MPI26_IEEE_SGE_FLAGS_NSF_MASK (0x1C) #define MPI26_IEEE_SGE_FLAGS_NSF_MPI_IEEE (0x00) +#define MPI26_IEEE_SGE_FLAGS_NSF_NVME_PRP (0x08) +#define MPI26_IEEE_SGE_FLAGS_NSF_NVME_SGL (0x10) /* Data Location Address Space */ Index: mpi/mpi2_cnfg.h =================================================================== --- mpi/mpi2_cnfg.h +++ mpi/mpi2_cnfg.h @@ -42,7 +42,7 @@ * Title: MPI Configuration messages and pages * Creation Date: November 10, 2006 * - * mpi2_cnfg.h Version: 02.00.35 + * mpi2_cnfg.h Version: 02.00.39 * * 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 @@ -223,9 +223,38 @@ * Flags field to IO Unit Page 7. * Added IO Unit Page 11. * Added new SAS Phy Event codes + * Added PCIe configuration pages. + * 03-19-15 02.00.32 Fixed PCIe Link Config page structure names to be + * unique in first 32 characters. * 05-25-15 02.00.33 Added more defines for the BiosOptions field of * MPI2_CONFIG_PAGE_BIOS_1. + * 08-25-15 02.00.34 Added PCIe Device Page 2 SGL format capability. * 12-18-15 02.00.35 Added SATADeviceWaitTime to SAS IO Unit Page 4. + * 01-21-16 02.00.36 Added/modified MPI2_MFGPAGE_DEVID_SAS defines. + * Added Link field to PCIe Link Pages + * Added EnclosureLevel and ConnectorName to PCIe + * Device Page 0. + * Added define for PCIE IoUnit page 1 max rate shift. + * Added comment for reserved ExtPageTypes. + * Added SAS 4 22.5 gbs speed support. + * Added PCIe 4 16.0 GT/sec speec support. + * Removed AHCI support. + * Removed SOP support. + * Added NegotiatedLinkRate and NegotiatedPortWidth to + * PCIe device page 0. + * 04-10-16 02.00.37 Fixed MPI2_MFGPAGE_DEVID_SAS3616/3708 defines + * 07-01-16 02.00.38 Added Manufacturing page 7 Connector types. + * Changed declaration of ConnectorName in PCIe DevicePage0 + * to match SAS DevicePage 0. + * Added SATADeviceWaitTime to IO Unit Page 11. + * Added MPI26_MFGPAGE_DEVID_SAS4008 + * Added x16 PCIe width to IO Unit Page 7 + * Added LINKFLAGS to control SRIS in PCIe IO Unit page 1 + * phy data. + * Added InitStatus to PCIe IO Unit Page 1 header. + * 09-01-16 02.00.39 Added MPI26_CONFIG_PAGE_ENCLOSURE_0 and related defines. + * Added MPI26_ENCLOS_PGAD_FORM_GET_NEXT_HANDLE and + * MPI26_ENCLOS_PGAD_FORM_HANDLE page address formats. * -------------------------------------------------------------------------- */ @@ -310,6 +339,12 @@ #define MPI2_CONFIG_EXTPAGETYPE_SAS_PORT (0x18) #define MPI2_CONFIG_EXTPAGETYPE_ETHERNET (0x19) #define MPI2_CONFIG_EXTPAGETYPE_EXT_MANUFACTURING (0x1A) +#define MPI2_CONFIG_EXTPAGETYPE_PCIE_IO_UNIT (0x1B) /* MPI v2.6 and later */ +#define MPI2_CONFIG_EXTPAGETYPE_PCIE_SWITCH (0x1C) /* MPI v2.6 and later */ +#define MPI2_CONFIG_EXTPAGETYPE_PCIE_DEVICE (0x1D) /* MPI v2.6 and later */ +#define MPI2_CONFIG_EXTPAGETYPE_PCIE_LINK (0x1E) /* MPI v2.6 and later */ +/* Product specific reserved values 0xE0 - 0xEF */ +/* Vendor specific reserved values 0xF0 - 0xFF */ /***************************************************************************** @@ -377,6 +412,12 @@ #define MPI2_SAS_ENCLOS_PGAD_HANDLE_MASK (0x0000FFFF) +/* Enclosure PageAddress format */ +#define MPI26_ENCLOS_PGAD_FORM_MASK (0xF0000000) +#define MPI26_ENCLOS_PGAD_FORM_GET_NEXT_HANDLE (0x00000000) +#define MPI26_ENCLOS_PGAD_FORM_HANDLE (0x10000000) + +#define MPI26_ENCLOS_PGAD_HANDLE_MASK (0x0000FFFF) /* RAID Configuration PageAddress format */ #define MPI2_RAID_PGAD_FORM_MASK (0xF0000000) @@ -403,6 +444,33 @@ #define MPI2_ETHERNET_PGAD_IF_NUMBER_MASK (0x000000FF) +/* PCIe Switch PageAddress format */ +#define MPI26_PCIE_SWITCH_PGAD_FORM_MASK (0xF0000000) +#define MPI26_PCIE_SWITCH_PGAD_FORM_GET_NEXT_HNDL (0x00000000) +#define MPI26_PCIE_SWITCH_PGAD_FORM_HNDL_PORTNUM (0x10000000) +#define MPI26_PCIE_SWITCH_EXPAND_PGAD_FORM_HNDL (0x20000000) + +#define MPI26_PCIE_SWITCH_PGAD_HANDLE_MASK (0x0000FFFF) +#define MPI26_PCIE_SWITCH_PGAD_PORTNUM_MASK (0x00FF0000) +#define MPI26_PCIE_SWITCH_PGAD_PORTNUM_SHIFT (16) + + +/* PCIe Device PageAddress format */ +#define MPI26_PCIE_DEVICE_PGAD_FORM_MASK (0xF0000000) +#define MPI26_PCIE_DEVICE_PGAD_FORM_GET_NEXT_HANDLE (0x00000000) +#define MPI26_PCIE_DEVICE_PGAD_FORM_HANDLE (0x20000000) + +#define MPI26_PCIE_DEVICE_PGAD_HANDLE_MASK (0x0000FFFF) + +/* PCIe Link PageAddress format */ +#define MPI26_PCIE_LINK_PGAD_FORM_MASK (0xF0000000) +#define MPI26_PCIE_LINK_PGAD_FORM_GET_NEXT_LINK (0x00000000) +#define MPI26_PCIE_LINK_PGAD_FORM_LINK_NUM (0x10000000) + +#define MPI26_PCIE_DEVICE_PGAD_LINKNUM_MASK (0x000000FF) + + + /**************************************************************************** * Configuration messages ****************************************************************************/ @@ -518,6 +586,20 @@ #define MPI26_MFGPAGE_DEVID_SAS3324_3 (0x00C2) #define MPI26_MFGPAGE_DEVID_SAS3324_4 (0x00C3) +#define MPI26_MFGPAGE_DEVID_SAS3516 (0x00AA) +#define MPI26_MFGPAGE_DEVID_SAS3516_1 (0x00AB) +#define MPI26_MFGPAGE_DEVID_SAS3416 (0x00AC) +#define MPI26_MFGPAGE_DEVID_SAS3508 (0x00AD) +#define MPI26_MFGPAGE_DEVID_SAS3508_1 (0x00AE) +#define MPI26_MFGPAGE_DEVID_SAS3408 (0x00AF) + +#define MPI26_MFGPAGE_DEVID_SAS3716 (0x00D0) +#define MPI26_MFGPAGE_DEVID_SAS3616 (0x00D1) +#define MPI26_MFGPAGE_DEVID_SAS3708 (0x00D2) + +#define MPI26_MFGPAGE_DEVID_SAS4008 (0x00A1) + + /* Manufacturing Page 0 */ typedef struct _MPI2_CONFIG_PAGE_MAN_0 @@ -755,6 +837,12 @@ #define MPI2_MANPAGE7_PINOUT_SFF_8644_8X (0x0B) #define MPI2_MANPAGE7_PINOUT_SFF_8644_16X (0x0C) #define MPI2_MANPAGE7_PINOUT_SFF_8436 (0x0D) +#define MPI2_MANPAGE7_PINOUT_SFF_8088_A (0x0E) +#define MPI2_MANPAGE7_PINOUT_SFF_8643_16i (0x0F) +#define MPI2_MANPAGE7_PINOUT_SFF_8654_4i (0x10) +#define MPI2_MANPAGE7_PINOUT_SFF_8654_8i (0x11) +#define MPI2_MANPAGE7_PINOUT_SFF_8611_4i (0x12) +#define MPI2_MANPAGE7_PINOUT_SFF_8611_8i (0x13) /* defines for the Location field */ #define MPI2_MANPAGE7_LOCATION_UNKNOWN (0x01) @@ -1017,11 +1105,13 @@ #define MPI2_IOUNITPAGE7_PCIE_WIDTH_X2 (0x02) #define MPI2_IOUNITPAGE7_PCIE_WIDTH_X4 (0x04) #define MPI2_IOUNITPAGE7_PCIE_WIDTH_X8 (0x08) +#define MPI2_IOUNITPAGE7_PCIE_WIDTH_X16 (0x10) /* defines for IO Unit Page 7 PCIeSpeed field */ #define MPI2_IOUNITPAGE7_PCIE_SPEED_2_5_GBPS (0x00) #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) /* defines for IO Unit Page 7 ProcessorState field */ #define MPI2_IOUNITPAGE7_PSTATE_MASK_SECOND (0x0000000F) @@ -1079,6 +1169,7 @@ /* defines for IO Unit Page 7 Flags field */ #define MPI2_IOUNITPAGE7_FLAG_CABLE_POWER_EXC (0x01) + /* IO Unit Page 8 */ #define MPI2_IOUNIT8_NUM_THRESHOLDS (4) @@ -1228,7 +1319,7 @@ U32 Reserved3; /* 0x1C */ U32 Reserved4; /* 0x20 */ U8 BootDeviceWaitTime; /* 0x24 */ - U8 Reserved5; /* 0x25 */ + U8 SATADeviceWaitTime; /* 0x25 */ U16 Reserved6; /* 0x26 */ U8 NumPhys; /* 0x28 */ U8 PEInitialSpinupDelay; /* 0x29 */ @@ -1249,9 +1340,6 @@ - - - /**************************************************************************** * IOC Config Pages ****************************************************************************/ @@ -1968,6 +2056,7 @@ #define MPI2_SAS_NEG_LINK_RATE_3_0 (0x09) #define MPI2_SAS_NEG_LINK_RATE_6_0 (0x0A) #define MPI25_SAS_NEG_LINK_RATE_12_0 (0x0B) +#define MPI26_SAS_NEG_LINK_RATE_22_5 (0x0C) /* values for AttachedPhyInfo fields */ @@ -2035,12 +2124,14 @@ #define MPI2_SAS_PRATE_MAX_RATE_3_0 (0x90) #define MPI2_SAS_PRATE_MAX_RATE_6_0 (0xA0) #define MPI25_SAS_PRATE_MAX_RATE_12_0 (0xB0) +#define MPI26_SAS_PRATE_MAX_RATE_22_5 (0xC0) #define MPI2_SAS_PRATE_MIN_RATE_MASK (0x0F) #define MPI2_SAS_PRATE_MIN_RATE_NOT_PROGRAMMABLE (0x00) #define MPI2_SAS_PRATE_MIN_RATE_1_5 (0x08) #define MPI2_SAS_PRATE_MIN_RATE_3_0 (0x09) #define MPI2_SAS_PRATE_MIN_RATE_6_0 (0x0A) #define MPI25_SAS_PRATE_MIN_RATE_12_0 (0x0B) +#define MPI26_SAS_PRATE_MIN_RATE_22_5 (0x0C) /* values for SAS HwLinkRate fields */ @@ -2049,11 +2140,13 @@ #define MPI2_SAS_HWRATE_MAX_RATE_3_0 (0x90) #define MPI2_SAS_HWRATE_MAX_RATE_6_0 (0xA0) #define MPI25_SAS_HWRATE_MAX_RATE_12_0 (0xB0) +#define MPI26_SAS_HWRATE_MAX_RATE_22_5 (0xC0) #define MPI2_SAS_HWRATE_MIN_RATE_MASK (0x0F) #define MPI2_SAS_HWRATE_MIN_RATE_1_5 (0x08) #define MPI2_SAS_HWRATE_MIN_RATE_3_0 (0x09) #define MPI2_SAS_HWRATE_MIN_RATE_6_0 (0x0A) #define MPI25_SAS_HWRATE_MIN_RATE_12_0 (0x0B) +#define MPI26_SAS_HWRATE_MIN_RATE_22_5 (0x0C) @@ -2227,11 +2320,13 @@ #define MPI2_SASIOUNIT1_MAX_RATE_3_0 (0x90) #define MPI2_SASIOUNIT1_MAX_RATE_6_0 (0xA0) #define MPI25_SASIOUNIT1_MAX_RATE_12_0 (0xB0) +#define MPI26_SASIOUNIT1_MAX_RATE_22_5 (0xC0) #define MPI2_SASIOUNIT1_MIN_RATE_MASK (0x0F) #define MPI2_SASIOUNIT1_MIN_RATE_1_5 (0x08) #define MPI2_SASIOUNIT1_MIN_RATE_3_0 (0x09) #define MPI2_SASIOUNIT1_MIN_RATE_6_0 (0x0A) #define MPI25_SASIOUNIT1_MIN_RATE_12_0 (0x0B) +#define MPI26_SASIOUNIT1_MIN_RATE_22_5 (0x0C) /* see mpi2_sas.h for values for SAS IO Unit Page 1 ControllerPhyDeviceInfo values */ @@ -2718,7 +2813,6 @@ #define MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID (0x0002) #define MPI2_SAS_DEVICE0_FLAGS_DEVICE_PRESENT (0x0001) - /* SAS Device Page 1 */ typedef struct _MPI2_CONFIG_PAGE_SAS_DEV_1 @@ -2885,7 +2979,6 @@ #define MPI2_SASPHY3_EVENT_CODE_HOTPLUG_TIMEOUT (0xD0) #define MPI2_SASPHY3_EVENT_CODE_MISALIGNED_MUX_PRIMITIVE (0xD1) #define MPI2_SASPHY3_EVENT_CODE_RX_AIP (0xD2) - /* Following codes are product specific and in MPI v2.6 and later */ #define MPI2_SASPHY3_EVENT_CODE_LCARB_WAIT_TIME (0xD3) #define MPI2_SASPHY3_EVENT_CODE_RCVD_CONN_RESP_WAIT_TIME (0xD4) @@ -2898,7 +2991,6 @@ #define MPI2_SASPHY3_EVENT_CODE_SATA_RX_START_RECEIVE (0xDB) #define MPI2_SASPHY3_EVENT_CODE_SMP_RX_START_RECEIVE (0xDC) - /* values for the CounterType field */ #define MPI2_SASPHY3_COUNTER_TYPE_WRAPPING (0x00) #define MPI2_SASPHY3_COUNTER_TYPE_SATURATING (0x01) @@ -2989,7 +3081,7 @@ * SAS Enclosure Config Pages ****************************************************************************/ -/* SAS Enclosure Page 0 */ +/* SAS Enclosure Page 0, Enclosure Page 0 */ typedef struct _MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0 { @@ -3007,7 +3099,10 @@ U32 Reserved4; /* 0x24 */ } MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0, - Mpi2SasEnclosurePage0_t, MPI2_POINTER pMpi2SasEnclosurePage0_t; + Mpi2SasEnclosurePage0_t, MPI2_POINTER pMpi2SasEnclosurePage0_t, + MPI26_CONFIG_PAGE_ENCLOSURE_0, + MPI2_POINTER PTR_MPI26_CONFIG_PAGE_ENCLOSURE_0, + Mpi26EnclosurePage0_t, MPI2_POINTER pMpi26EnclosurePage0_t; #define MPI2_SASENCLOSURE0_PAGEVERSION (0x04) @@ -3021,6 +3116,17 @@ #define MPI2_SAS_ENCLS0_FLAGS_MNG_SES_ENCLOSURE (0x0004) #define MPI2_SAS_ENCLS0_FLAGS_MNG_IOC_GPIO (0x0005) +#define MPI26_ENCLOSURE0_PAGEVERSION (0x04) + +/* Values for Enclosure Page 0 Flags field */ +#define MPI26_ENCLS0_FLAGS_ENCL_LEVEL_VALID (0x0010) +#define MPI26_ENCLS0_FLAGS_MNG_MASK (0x000F) +#define MPI26_ENCLS0_FLAGS_MNG_UNKNOWN (0x0000) +#define MPI26_ENCLS0_FLAGS_MNG_IOC_SES (0x0001) +#define MPI26_ENCLS0_FLAGS_MNG_IOC_SGPIO (0x0002) +#define MPI26_ENCLS0_FLAGS_MNG_EXP_SGPIO (0x0003) +#define MPI26_ENCLS0_FLAGS_MNG_SES_ENCLOSURE (0x0004) +#define MPI26_ENCLS0_FLAGS_MNG_IOC_GPIO (0x0005) /**************************************************************************** * Log Config Page @@ -3300,5 +3406,424 @@ /* PageVersion should be provided by product-specific code */ + +/**************************************************************************** +* values for fields used by several types of PCIe Config Pages +****************************************************************************/ + +/* values for NegotiatedLinkRates fields */ +#define MPI26_PCIE_NEG_LINK_RATE_MASK_PHYSICAL (0x0F) +/* link rates used for Negotiated Physical Link Rate */ +#define MPI26_PCIE_NEG_LINK_RATE_UNKNOWN (0x00) +#define MPI26_PCIE_NEG_LINK_RATE_PHY_DISABLED (0x01) +#define MPI26_PCIE_NEG_LINK_RATE_2_5 (0x02) +#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) + + +/**************************************************************************** +* PCIe IO Unit Config Pages (MPI v2.6 and later) +****************************************************************************/ + +/* PCIe IO Unit Page 0 */ + +typedef struct _MPI26_PCIE_IO_UNIT0_PHY_DATA +{ + U8 Link; /* 0x00 */ + U8 LinkFlags; /* 0x01 */ + U8 PhyFlags; /* 0x02 */ + U8 NegotiatedLinkRate; /* 0x03 */ + U32 ControllerPhyDeviceInfo;/* 0x04 */ + U16 AttachedDevHandle; /* 0x08 */ + U16 ControllerDevHandle; /* 0x0A */ + U32 EnumerationStatus; /* 0x0C */ + U32 Reserved1; /* 0x10 */ +} MPI26_PCIE_IO_UNIT0_PHY_DATA, MPI2_POINTER PTR_MPI26_PCIE_IO_UNIT0_PHY_DATA, + Mpi26PCIeIOUnit0PhyData_t, MPI2_POINTER pMpi26PCIeIOUnit0PhyData_t; + +/* + * Host code (drivers, BIOS, utilities, etc.) should leave this define set to + * one and check the value returned for NumPhys at runtime. + */ +#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 */ + U32 Reserved1; /* 0x08 */ + U8 NumPhys; /* 0x0C */ + U8 InitStatus; /* 0x0D */ + U16 Reserved3; /* 0x0E */ + MPI26_PCIE_IO_UNIT0_PHY_DATA PhyData[MPI26_PCIE_IOUNIT0_PHY_MAX]; /* 0x10 */ +} MPI26_CONFIG_PAGE_PIOUNIT_0, + MPI2_POINTER PTR_MPI26_CONFIG_PAGE_PIOUNIT_0, + Mpi26PCIeIOUnitPage0_t, MPI2_POINTER pMpi26PCIeIOUnitPage0_t; + +#define MPI26_PCIEIOUNITPAGE0_PAGEVERSION (0x00) + +/* values for PCIe IO Unit Page 0 LinkFlags */ +#define MPI26_PCIEIOUNIT0_LINKFLAGS_ENUMERATION_IN_PROGRESS (0x08) + +/* values for PCIe IO Unit Page 0 PhyFlags */ +#define MPI26_PCIEIOUNIT0_PHYFLAGS_PHY_DISABLED (0x08) + +/* use MPI26_PCIE_NEG_LINK_RATE_ defines for the NegotiatedLinkRate field */ + +/* see mpi2_pci.h for values for PCIe IO Unit Page 0 ControllerPhyDeviceInfo values */ + +/* values for PCIe IO Unit Page 0 EnumerationStatus */ +#define MPI26_PCIEIOUNIT0_ES_MAX_SWITCHES_EXCEEDED (0x40000000) +#define MPI26_PCIEIOUNIT0_ES_MAX_DEVICES_EXCEEDED (0x20000000) + + +/* PCIe IO Unit Page 1 */ + +typedef struct _MPI26_PCIE_IO_UNIT1_PHY_DATA +{ + U8 Link; /* 0x00 */ + U8 LinkFlags; /* 0x01 */ + U8 PhyFlags; /* 0x02 */ + U8 MaxMinLinkRate; /* 0x03 */ + U32 ControllerPhyDeviceInfo; /* 0x04 */ + U32 Reserved1; /* 0x08 */ +} MPI26_PCIE_IO_UNIT1_PHY_DATA, MPI2_POINTER PTR_MPI26_PCIE_IO_UNIT1_PHY_DATA, + Mpi26PCIeIOUnit1PhyData_t, MPI2_POINTER pMpi26PCIeIOUnit1PhyData_t; + +/* values for LinkFlags */ +#define MPI26_PCIEIOUNIT1_LINKFLAGS_DIS_SRIS (0x00) +#define MPI26_PCIEIOUNIT1_LINKFLAGS_EN_SRIS (0x01) + +/* + * Host code (drivers, BIOS, utilities, etc.) should leave this define set to + * one and check the value returned for NumPhys at runtime. + */ +#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 */ + U16 ControlFlags; /* 0x08 */ + U16 Reserved; /* 0x0A */ + U16 AdditionalControlFlags; /* 0x0C */ + U16 NVMeMaxQueueDepth; /* 0x0E */ + U8 NumPhys; /* 0x10 */ + U8 Reserved1; /* 0x11 */ + U16 Reserved2; /* 0x12 */ + MPI26_PCIE_IO_UNIT1_PHY_DATA PhyData[MPI26_PCIE_IOUNIT1_PHY_MAX];/* 0x14 */ +} MPI26_CONFIG_PAGE_PIOUNIT_1, + MPI2_POINTER PTR_MPI26_CONFIG_PAGE_PIOUNIT_1, + Mpi26PCIeIOUnitPage1_t, MPI2_POINTER pMpi26PCIeIOUnitPage1_t; + +#define MPI26_PCIEIOUNITPAGE1_PAGEVERSION (0x00) + +/* values for PCIe IO Unit Page 1 PhyFlags */ +#define MPI26_PCIEIOUNIT1_PHYFLAGS_PHY_DISABLE (0x08) +#define MPI26_PCIEIOUNIT1_PHYFLAGS_ENDPOINT_ONLY (0x01) + +/* values for PCIe IO Unit Page 1 MaxMinLinkRate */ +#define MPI26_PCIEIOUNIT1_MAX_RATE_MASK (0xF0) +#define MPI26_PCIEIOUNIT1_MAX_RATE_SHIFT (4) +#define MPI26_PCIEIOUNIT1_MAX_RATE_2_5 (0x20) +#define MPI26_PCIEIOUNIT1_MAX_RATE_5_0 (0x30) +#define MPI26_PCIEIOUNIT1_MAX_RATE_8_0 (0x40) +#define MPI26_PCIEIOUNIT1_MAX_RATE_16_0 (0x50) + +/* see mpi2_pci.h for values for PCIe IO Unit Page 0 ControllerPhyDeviceInfo values */ + + +/**************************************************************************** +* PCIe Switch Config Pages (MPI v2.6 and later) +****************************************************************************/ + +/* PCIe Switch Page 0 */ + +typedef struct _MPI26_CONFIG_PAGE_PSWITCH_0 +{ + MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ + U8 PhysicalPort; /* 0x08 */ + U8 Reserved1; /* 0x09 */ + U16 Reserved2; /* 0x0A */ + U16 DevHandle; /* 0x0C */ + U16 ParentDevHandle; /* 0x0E */ + U8 NumPorts; /* 0x10 */ + U8 PCIeLevel; /* 0x11 */ + U16 Reserved3; /* 0x12 */ + U32 Reserved4; /* 0x14 */ + U32 Reserved5; /* 0x18 */ + U32 Reserved6; /* 0x1C */ +} MPI26_CONFIG_PAGE_PSWITCH_0, MPI2_POINTER PTR_MPI26_CONFIG_PAGE_PSWITCH_0, + Mpi26PCIeSwitchPage0_t, MPI2_POINTER pMpi26PCIeSwitchPage0_t; + +#define MPI26_PCIESWITCH0_PAGEVERSION (0x00) + + +/* PCIe Switch Page 1 */ + +typedef struct _MPI26_CONFIG_PAGE_PSWITCH_1 +{ + MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ + U8 PhysicalPort; /* 0x08 */ + U8 Reserved1; /* 0x09 */ + U16 Reserved2; /* 0x0A */ + U8 NumPorts; /* 0x0C */ + U8 PortNum; /* 0x0D */ + U16 AttachedDevHandle; /* 0x0E */ + U16 SwitchDevHandle; /* 0x10 */ + U8 NegotiatedPortWidth; /* 0x12 */ + U8 NegotiatedLinkRate; /* 0x13 */ + U32 Reserved4; /* 0x14 */ + U32 Reserved5; /* 0x18 */ +} MPI26_CONFIG_PAGE_PSWITCH_1, MPI2_POINTER PTR_MPI26_CONFIG_PAGE_PSWITCH_1, + Mpi26PCIeSwitchPage1_t, MPI2_POINTER pMpi26PCIeSwitchPage1_t; + +#define MPI26_PCIESWITCH1_PAGEVERSION (0x00) + +/* use MPI26_PCIE_NEG_LINK_RATE_ defines for the NegotiatedLinkRate field */ + + +/**************************************************************************** +* PCIe Device Config Pages (MPI v2.6 and later) +****************************************************************************/ + +/* PCIe Device Page 0 */ + +typedef struct _MPI26_CONFIG_PAGE_PCIEDEV_0 +{ + MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ + U16 Slot; /* 0x08 */ + U16 EnclosureHandle; /* 0x0A */ + U64 WWID; /* 0x0C */ + U16 ParentDevHandle; /* 0x14 */ + U8 PortNum; /* 0x16 */ + U8 AccessStatus; /* 0x17 */ + U16 DevHandle; /* 0x18 */ + U8 PhysicalPort; /* 0x1A */ + U8 Reserved1; /* 0x1B */ + U32 DeviceInfo; /* 0x1C */ + U32 Flags; /* 0x20 */ + U8 SupportedLinkRates; /* 0x24 */ + U8 MaxPortWidth; /* 0x25 */ + U8 NegotiatedPortWidth; /* 0x26 */ + U8 NegotiatedLinkRate; /* 0x27 */ + U8 EnclosureLevel; /* 0x28 */ + U8 Reserved2; /* 0x29 */ + U16 Reserved3; /* 0x2A */ + U8 ConnectorName[4]; /* 0x2C */ + U32 Reserved4; /* 0x30 */ + U32 Reserved5; /* 0x34 */ +} MPI26_CONFIG_PAGE_PCIEDEV_0, MPI2_POINTER PTR_MPI26_CONFIG_PAGE_PCIEDEV_0, + Mpi26PCIeDevicePage0_t, MPI2_POINTER pMpi26PCIeDevicePage0_t; + +#define MPI26_PCIEDEVICE0_PAGEVERSION (0x01) + +/* values for PCIe Device Page 0 AccessStatus field */ +#define MPI26_PCIEDEV0_ASTATUS_NO_ERRORS (0x00) +#define MPI26_PCIEDEV0_ASTATUS_NEEDS_INITIALIZATION (0x04) +#define MPI26_PCIEDEV0_ASTATUS_CAPABILITY_FAILED (0x02) +#define MPI26_PCIEDEV0_ASTATUS_DEVICE_BLOCKED (0x07) +#define MPI26_PCIEDEV0_ASTATUS_MEMORY_SPACE_ACCESS_FAILED (0x08) +#define MPI26_PCIEDEV0_ASTATUS_UNSUPPORTED_DEVICE (0x09) +#define MPI26_PCIEDEV0_ASTATUS_MSIX_REQUIRED (0x0A) +#define MPI26_PCIEDEV0_ASTATUS_UNKNOWN (0x10) + +#define MPI26_PCIEDEV0_ASTATUS_NVME_READY_TIMEOUT (0x30) +#define MPI26_PCIEDEV0_ASTATUS_NVME_DEVCFG_UNSUPPORTED (0x31) +#define MPI26_PCIEDEV0_ASTATUS_NVME_IDENTIFY_FAILED (0x32) +#define MPI26_PCIEDEV0_ASTATUS_NVME_QCONFIG_FAILED (0x33) +#define MPI26_PCIEDEV0_ASTATUS_NVME_QCREATION_FAILED (0x34) +#define MPI26_PCIEDEV0_ASTATUS_NVME_EVENTCFG_FAILED (0x35) +#define MPI26_PCIEDEV0_ASTATUS_NVME_GET_FEATURE_STAT_FAILED (0x36) +#define MPI26_PCIEDEV0_ASTATUS_NVME_IDLE_TIMEOUT (0x37) +#define MPI26_PCIEDEV0_ASTATUS_NVME_FAILURE_STATUS (0x38) + +#define MPI26_PCIEDEV0_ASTATUS_INIT_FAIL_MAX (0x3F) + +/* see mpi2_pci.h for the MPI26_PCIE_DEVINFO_ defines used for the DeviceInfo field */ + +/* values for PCIe Device Page 0 Flags field */ +#define MPI26_PCIEDEV0_FLAGS_UNAUTHORIZED_DEVICE (0x8000) +#define MPI26_PCIEDEV0_FLAGS_ENABLED_FAST_PATH (0x4000) +#define MPI26_PCIEDEV0_FLAGS_FAST_PATH_CAPABLE (0x2000) +#define MPI26_PCIEDEV0_FLAGS_ASYNCHRONOUS_NOTIFICATION (0x0400) +#define MPI26_PCIEDEV0_FLAGS_ATA_SW_PRESERVATION (0x0200) +#define MPI26_PCIEDEV0_FLAGS_UNSUPPORTED_DEVICE (0x0100) +#define MPI26_PCIEDEV0_FLAGS_ATA_48BIT_LBA_SUPPORTED (0x0080) +#define MPI26_PCIEDEV0_FLAGS_ATA_SMART_SUPPORTED (0x0040) +#define MPI26_PCIEDEV0_FLAGS_ATA_NCQ_SUPPORTED (0x0020) +#define MPI26_PCIEDEV0_FLAGS_ATA_FUA_SUPPORTED (0x0010) +#define MPI26_PCIEDEV0_FLAGS_ENCL_LEVEL_VALID (0x0002) +#define MPI26_PCIEDEV0_FLAGS_DEVICE_PRESENT (0x0001) + +/* values for PCIe Device Page 0 SupportedLinkRates field */ +#define MPI26_PCIEDEV0_LINK_RATE_16_0_SUPPORTED (0x08) +#define MPI26_PCIEDEV0_LINK_RATE_8_0_SUPPORTED (0x04) +#define MPI26_PCIEDEV0_LINK_RATE_5_0_SUPPORTED (0x02) +#define MPI26_PCIEDEV0_LINK_RATE_2_5_SUPPORTED (0x01) + +/* use MPI26_PCIE_NEG_LINK_RATE_ defines for the NegotiatedLinkRate field */ + + +/* PCIe Device Page 2 */ + +typedef struct _MPI26_CONFIG_PAGE_PCIEDEV_2 +{ + MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ + U16 DevHandle; /* 0x08 */ + U16 Reserved1; /* 0x0A */ + U32 MaximumDataTransferSize;/* 0x0C */ + U32 Capabilities; /* 0x10 */ + U32 Reserved2; /* 0x14 */ +} MPI26_CONFIG_PAGE_PCIEDEV_2, MPI2_POINTER PTR_MPI26_CONFIG_PAGE_PCIEDEV_2, + Mpi26PCIeDevicePage2_t, MPI2_POINTER pMpi26PCIeDevicePage2_t; + +#define MPI26_PCIEDEVICE2_PAGEVERSION (0x00) + +/* defines for PCIe Device Page 2 Capabilities field */ +#define MPI26_PCIEDEV2_CAP_SGL_FORMAT (0x00000004) +#define MPI26_PCIEDEV2_CAP_BIT_BUCKET_SUPPORT (0x00000002) +#define MPI26_PCIEDEV2_CAP_SGL_SUPPORT (0x00000001) + + +/**************************************************************************** +* PCIe Link Config Pages (MPI v2.6 and later) +****************************************************************************/ + +/* PCIe Link Page 1 */ + +typedef struct _MPI26_CONFIG_PAGE_PCIELINK_1 +{ + MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ + U8 Link; /* 0x08 */ + U8 Reserved1; /* 0x09 */ + U16 Reserved2; /* 0x0A */ + U32 CorrectableErrorCount; /* 0x0C */ + U16 NonFatalErrorCount; /* 0x10 */ + U16 Reserved3; /* 0x12 */ + U16 FatalErrorCount; /* 0x14 */ + U16 Reserved4; /* 0x16 */ +} MPI26_CONFIG_PAGE_PCIELINK_1, MPI2_POINTER PTR_MPI26_CONFIG_PAGE_PCIELINK_1, + Mpi26PcieLinkPage1_t, MPI2_POINTER pMpi26PcieLinkPage1_t; + +#define MPI26_PCIELINK1_PAGEVERSION (0x00) + +/* PCIe Link Page 2 */ + +typedef struct _MPI26_PCIELINK2_LINK_EVENT +{ + U8 LinkEventCode; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U16 Reserved2; /* 0x02 */ + U32 LinkEventInfo; /* 0x04 */ +} MPI26_PCIELINK2_LINK_EVENT, MPI2_POINTER PTR_MPI26_PCIELINK2_LINK_EVENT, + Mpi26PcieLink2LinkEvent_t, MPI2_POINTER pMpi26PcieLink2LinkEvent_t; + +/* use MPI26_PCIELINK3_EVTCODE_ for the LinkEventCode field */ + + +/* + * Host code (drivers, BIOS, utilities, etc.) should leave this define set to + * one and check the value returned for NumLinkEvents at runtime. + */ +#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 */ + U8 Link; /* 0x08 */ + U8 Reserved1; /* 0x09 */ + U16 Reserved2; /* 0x0A */ + U8 NumLinkEvents; /* 0x0C */ + U8 Reserved3; /* 0x0D */ + U16 Reserved4; /* 0x0E */ + MPI26_PCIELINK2_LINK_EVENT LinkEvent[MPI26_PCIELINK2_LINK_EVENT_MAX]; /* 0x10 */ +} MPI26_CONFIG_PAGE_PCIELINK_2, MPI2_POINTER PTR_MPI26_CONFIG_PAGE_PCIELINK_2, + Mpi26PcieLinkPage2_t, MPI2_POINTER pMpi26PcieLinkPage2_t; + +#define MPI26_PCIELINK2_PAGEVERSION (0x00) + + +/* PCIe Link Page 3 */ + +typedef struct _MPI26_PCIELINK3_LINK_EVENT_CONFIG +{ + U8 LinkEventCode; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U16 Reserved2; /* 0x02 */ + U8 CounterType; /* 0x04 */ + U8 ThresholdWindow; /* 0x05 */ + U8 TimeUnits; /* 0x06 */ + U8 Reserved3; /* 0x07 */ + U32 EventThreshold; /* 0x08 */ + U16 ThresholdFlags; /* 0x0C */ + U16 Reserved4; /* 0x0E */ +} MPI26_PCIELINK3_LINK_EVENT_CONFIG, MPI2_POINTER PTR_MPI26_PCIELINK3_LINK_EVENT_CONFIG, + Mpi26PcieLink3LinkEventConfig_t, MPI2_POINTER pMpi26PcieLink3LinkEventConfig_t; + +/* values for LinkEventCode field */ +#define MPI26_PCIELINK3_EVTCODE_NO_EVENT (0x00) +#define MPI26_PCIELINK3_EVTCODE_CORRECTABLE_ERROR_RECEIVED (0x01) +#define MPI26_PCIELINK3_EVTCODE_NON_FATAL_ERROR_RECEIVED (0x02) +#define MPI26_PCIELINK3_EVTCODE_FATAL_ERROR_RECEIVED (0x03) +#define MPI26_PCIELINK3_EVTCODE_DATA_LINK_ERROR_DETECTED (0x04) +#define MPI26_PCIELINK3_EVTCODE_TRANSACTION_LAYER_ERROR_DETECTED (0x05) +#define MPI26_PCIELINK3_EVTCODE_TLP_ECRC_ERROR_DETECTED (0x06) +#define MPI26_PCIELINK3_EVTCODE_POISONED_TLP (0x07) +#define MPI26_PCIELINK3_EVTCODE_RECEIVED_NAK_DLLP (0x08) +#define MPI26_PCIELINK3_EVTCODE_SENT_NAK_DLLP (0x09) +#define MPI26_PCIELINK3_EVTCODE_LTSSM_RECOVERY_STATE (0x0A) +#define MPI26_PCIELINK3_EVTCODE_LTSSM_RXL0S_STATE (0x0B) +#define MPI26_PCIELINK3_EVTCODE_LTSSM_TXL0S_STATE (0x0C) +#define MPI26_PCIELINK3_EVTCODE_LTSSM_L1_STATE (0x0D) +#define MPI26_PCIELINK3_EVTCODE_LTSSM_DISABLED_STATE (0x0E) +#define MPI26_PCIELINK3_EVTCODE_LTSSM_HOT_RESET_STATE (0x0F) +#define MPI26_PCIELINK3_EVTCODE_SYSTEM_ERROR (0x10) +#define MPI26_PCIELINK3_EVTCODE_DECODE_ERROR (0x11) +#define MPI26_PCIELINK3_EVTCODE_DISPARITY_ERROR (0x12) + +/* values for the CounterType field */ +#define MPI26_PCIELINK3_COUNTER_TYPE_WRAPPING (0x00) +#define MPI26_PCIELINK3_COUNTER_TYPE_SATURATING (0x01) +#define MPI26_PCIELINK3_COUNTER_TYPE_PEAK_VALUE (0x02) + +/* values for the TimeUnits field */ +#define MPI26_PCIELINK3_TM_UNITS_10_MICROSECONDS (0x00) +#define MPI26_PCIELINK3_TM_UNITS_100_MICROSECONDS (0x01) +#define MPI26_PCIELINK3_TM_UNITS_1_MILLISECOND (0x02) +#define MPI26_PCIELINK3_TM_UNITS_10_MILLISECONDS (0x03) + +/* values for the ThresholdFlags field */ +#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. + */ +#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 */ + U8 Link; /* 0x08 */ + U8 Reserved1; /* 0x09 */ + U16 Reserved2; /* 0x0A */ + U8 NumLinkEvents; /* 0x0C */ + U8 Reserved3; /* 0x0D */ + U16 Reserved4; /* 0x0E */ + MPI26_PCIELINK3_LINK_EVENT_CONFIG LinkEventConfig[MPI26_PCIELINK3_LINK_EVENT_MAX]; /* 0x10 */ +} MPI26_CONFIG_PAGE_PCIELINK_3, MPI2_POINTER PTR_MPI26_CONFIG_PAGE_PCIELINK_3, + Mpi26PcieLinkPage3_t, MPI2_POINTER pMpi26PcieLinkPage3_t; + +#define MPI26_PCIELINK3_PAGEVERSION (0x00) + + #endif Index: mpi/mpi2_hbd.h =================================================================== --- mpi/mpi2_hbd.h +++ mpi/mpi2_hbd.h @@ -42,7 +42,7 @@ * Title: MPI Host Based Discovery messages and structures * Creation Date: October 21, 2009 * - * mpi2_hbd.h Version: 02.00.03 + * mpi2_hbd.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 @@ -59,6 +59,7 @@ * HBD Action request, replaced by AdditionalInfo field. * 11-18-11 02.00.02 Incorporating additions for MPI v2.5. * 11-18-14 02.00.03 Updated copyright information. + * 02-17-16 02.00.04 Added SAS 4 22.5 gbs speed support. * -------------------------------------------------------------------------- */ @@ -129,6 +130,7 @@ #define MPI2_HBD_MAX_RATE_3_0 (0x09) #define MPI2_HBD_MAX_RATE_6_0 (0x0A) #define MPI25_HBD_MAX_RATE_12_0 (0x0B) +#define MPI26_HBD_MAX_RATE_22_5 (0x0C) /* Host Based Discovery Action Reply Message */ Index: mpi/mpi2_history.txt =================================================================== --- mpi/mpi2_history.txt +++ mpi/mpi2_history.txt @@ -41,24 +41,25 @@ All rights reserved. --------------------------------------- - Header Set Release Version: 02.00.42 - Header Set Release Date: 01-04-16 + Header Set Release Version: 02.00.46 + Header Set Release Date: 09-07-16 --------------------------------------- Filename Current version Prior version ---------- --------------- ------------- - mpi2.h 02.00.42 02.00.41 - mpi2_cnfg.h 02.00.35 02.00.34 - mpi2_init.h 02.00.20 02.00.19 - mpi2_ioc.h 02.00.27 02.00.27 + mpi2.h 02.00.46 02.00.45 + mpi2_cnfg.h 02.00.39 02.00.38 + mpi2_init.h 02.00.21 02.00.21 + mpi2_ioc.h 02.00.30 02.00.29 mpi2_raid.h 02.00.11 02.00.11 mpi2_sas.h 02.00.10 02.00.10 mpi2_targ.h 02.00.09 02.00.09 - mpi2_tool.h 02.00.13 02.00.13 + mpi2_tool.h 02.00.14 02.00.13 mpi2_type.h 02.00.01 02.00.01 mpi2_ra.h 02.00.01 02.00.01 - mpi2_hbd.h 02.00.03 02.00.03 - mpi2_history.txt 02.00.41 02.00.40 + mpi2_hbd.h 02.00.04 02.00.04 + mpi2_pci.h 02.00.02 02.00.02 + mpi2_history.txt 02.00.43 02.00.43 * Date Version Description @@ -141,7 +142,8 @@ * Bumped MPI2_HEADER_VERSION_UNIT. * 03-16-15 02.00.37 Updated for MPI v2.6. * Bumped MPI2_HEADER_VERSION_UNIT. - * Added Scratchpad registers to + * Added Scratchpad registers and + * AtomicRequestDescriptorPost register to * MPI2_SYSTEM_INTERFACE_REGS. * Added MPI2_DIAG_SBR_RELOAD. * Added MPI2_IOCSTATUS_INSUFFICIENT_POWER. @@ -151,6 +153,14 @@ * Added V7 HostDiagnostic register defines * 12-15-15 02.00.41 Bumped MPI_HEADER_VERSION_UNIT * 01-04-16 02.00.42 Bumped MPI_HEADER_VERSION_UNIT + * 04-05-16 02.00.43 Modified MPI26_DIAG_BOOT_DEVICE_SELECT defines + * to be unique within first 32 characters. + * Removed AHCI support. + * Removed SOP support. + * Bumped MPI2_HEADER_VERSION_UNIT. + * 04-10-16 02.00.44 Bumped MPI2_HEADER_VERSION_UNIT. + * 07-06-16 02.00.45 Bumped MPI2_HEADER_VERSION_UNIT. + * 09-02-16 02.00.46 Bumped MPI2_HEADER_VERSION_UNIT. * -------------------------------------------------------------------------- mpi2_cnfg.h @@ -323,9 +333,38 @@ * Flags field to IO Unit Page 7. * Added IO Unit Page 11. * Added new SAS Phy Event codes + * Added PCIe configuration pages. + * 03-19-15 02.00.32 Fixed PCIe Link Config page structure names to be + * unique in first 32 characters. * 05-25-15 02.00.33 Added more defines for the BiosOptions field of * MPI2_CONFIG_PAGE_BIOS_1. + * 08-25-15 02.00.34 Added PCIe Device Page 2 SGL format capability. * 12-18-15 02.00.35 Added SATADeviceWaitTime to SAS IO Unit Page 4. + * 01-21-16 02.00.36 Added/modified MPI2_MFGPAGE_DEVID_SAS defines. + * Added Link field to PCIe Link Pages + * Added EnclosureLevel and ConnectorName to PCIe + * Device Page 0. + * Added define for PCIE IoUnit page 1 max rate shift. + * Added comment for reserved ExtPageTypes. + * Added SAS 4 22.5 gbs speed support. + * Added PCIe 4 16.0 GT/sec speec support. + * Removed AHCI support. + * Removed SOP support. + * Added NegotiatedLinkRate and NegotiatedPortWidth to + * PCIe device page 0. + * 04-10-16 02.00.37 Fixed MPI2_MFGPAGE_DEVID_SAS3616/3708 defines + * 07-01-16 02.00.38 Added Manufacturing page 7 Connector types. + * Changed declaration of ConnectorName in PCIe DevicePage0 + * to match SAS DevicePage 0. + * Added SATADeviceWaitTime to IO Unit Page 11. + * Added MPI26_MFGPAGE_DEVID_SAS4008 + * Added x16 PCIe width to IO Unit Page 7 + * Added LINKFLAGS to control SRIS in PCIe IO Unit page 1 + * phy data. + * Added InitStatus to PCIe IO Unit Page 1 header. + * 09-01-16 02.00.39 Added MPI26_CONFIG_PAGE_ENCLOSURE_0 and related defines. + * Added MPI26_ENCLOS_PGAD_FORM_GET_NEXT_HANDLE and + * MPI26_ENCLOS_PGAD_FORM_HANDLE page address formats. * -------------------------------------------------------------------------- mpi2_init.h @@ -365,6 +404,8 @@ * 08-26-15 02.00.18 Added SCSITASKMGMT_MSGFLAGS for Target Reset. * 12-18-15 02.00.19 Added EEDPObservedValue added to SCSI IO Reply message. * 01-04-16 02.00.20 Modified EEDP reported values in SCSI IO Reply message. + * 01-21-16 02.00.21 Modified MPI26_SCSITASKMGMT_MSGFLAGS_PCIE* defines to + * be unique within first 32 characters. * -------------------------------------------------------------------------- mpi2_ioc.h @@ -491,9 +532,30 @@ * MPI26_EVENT_DATA_PCIE_LINK_COUNTER. * Added MPI26_CTRL_OP_SHUTDOWN. * Added MPI26_CTRL_OP_LINK_CLEAR_ERROR_LOG - * Added MPI26_FW_HEADER_PID_FAMILY_3324_SAS + * Added MPI26_FW_HEADER_PID_FAMILY_3324_SAS and + * MPI26_FW_HEADER_PID_FAMILY_3516_SAS. * 08-25-15 02.00.27 Added IC ARCH Class based signature defines. - * -------------------------------------------------------------------------- + * Added MPI26_EVENT_PCIE_ENUM_ES_RESOURCES_EXHAUSTED event. + * Added ConigurationFlags field to IOCInit message to + * support NVMe SGL format control. + * Added PCIe SRIOV support. + * 02-17-16 02.00.28 Added SAS 4 22.5 gbs speed support. + * Added PCIe 4 16.0 GT/sec speec support. + * Removed AHCI support. + * Removed SOP support. + * 07-01-16 02.00.29 Added Archclass for 4008 product. + * Added IOCException MPI2_IOCFACTS_EXCEPT_PCIE_DISABLED. + * 08-23-16 02.00.30 Added new defines for the ImageType field of FWDownload + * Request Message. + * Added new defines for the ImageType field of FWUpload + * Request Message. + * Added new values for the RegionType field in the Layout + * Data sections of the FLASH Layout Extended Image Data. + * Added new defines for the ReasonCode field of + * Active Cable Exception Event. + * Added MPI2_EVENT_ENCL_DEVICE_STATUS_CHANGE and + * MPI26_EVENT_DATA_ENCL_DEV_STATUS_CHANGE. + * -------------------------------------------------------------------------- mpi2_raid.h * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. @@ -581,6 +643,8 @@ * 08-19-13 02.00.11 Added MPI2_TOOLBOX_TEXT_DISPLAY_TOOL and related info. * 01-08-14 02.00.12 Added MPI2_TOOLBOX_CLEAN_BIT26_PRODUCT_SPECIFIC. * 11-18-14 02.00.13 Updated copyright information. + * 08-25-16 02.00.14 Added new values for the Flags field of Toolbox Clean + * Tool Request Message. * -------------------------------------------------------------------------- mpi2_type.h @@ -599,24 +663,33 @@ * HBD Action request, replaced by AdditionalInfo field. * 11-18-11 02.00.02 Incorporating additions for MPI v2.5. * 11-18-14 02.00.03 Updated copyright information. + * 02-17-16 02.00.04 Added SAS 4 22.5 gbs speed support. * -------------------------------------------------------------------------- +mpi2_pci.h + * 03-16-15 02.00.00 Initial version. + * 02-17-16 02.00.01 Removed AHCI support. + * Removed SOP support. + * 07-01-16 02.00.02 Added MPI26_NVME_FLAGS_FORCE_ADMIN_ERR_RESP to + * NVME Encapsulated Request. + * -------------------------------------------------------------------------- mpi2_history.txt Parts list history -Filename 02.00.42 ----------- -------- -mpi2.h 02.00.42 -mpi2_cnfg.h 02.00.35 -mpi2_init.h 02.00.20 -mpi2_ioc.h 02.00.27 -mpi2_raid.h 02.00.11 -mpi2_sas.h 02.00.10 -mpi2_targ.h 02.00.09 -mpi2_tool.h 02.00.13 -mpi2_type.h 02.00.01 -mpi2_ra.h 02.00.01 -mpi2_hbd.h 02.00.03 +Filename 02.00.46 02.00.45 02.00.44 02.00.43 02.00.42 +---------- -------- -------- -------- -------- -------- +mpi2.h 02.00.46 02.00.45 02.00.44 02.00.43 02.00.42 +mpi2_cnfg.h 02.00.39 02.00.38 02.00.37 02.00.36 02.00.35 +mpi2_init.h 02.00.21 02.00.21 02.00.21 02.00.21 02.00.20 +mpi2_ioc.h 02.00.30 02.00.29 02.00.28 02.00.28 02.00.27 +mpi2_raid.h 02.00.11 02.00.11 02.00.11 02.00.11 02.00.11 +mpi2_sas.h 02.00.10 02.00.10 02.00.10 02.00.10 02.00.10 +mpi2_targ.h 02.00.09 02.00.09 02.00.09 02.00.09 02.00.09 +mpi2_tool.h 02.00.14 02.00.13 02.00.13 02.00.13 02.00.13 +mpi2_type.h 02.00.01 02.00.01 02.00.01 02.00.01 02.00.01 +mpi2_ra.h 02.00.01 02.00.01 02.00.01 02.00.01 02.00.01 +mpi2_hbd.h 02.00.04 02.00.04 02.00.04 02.00.04 02.00.03 +mpi2_pci.h 02.00.02 02.00.02 02.00.01 02.00.01 02.00.00 Filename 02.00.41 02.00.40 02.00.39 02.00.38 02.00.37 02.00.36 ---------- -------- -------- -------- -------- -------- -------- @@ -631,6 +704,7 @@ mpi2_type.h 02.00.01 02.00.01 02.00.01 02.00.01 02.00.01 02.00.01 mpi2_ra.h 02.00.01 02.00.01 02.00.01 02.00.01 02.00.01 02.00.01 mpi2_hbd.h 02.00.03 02.00.03 02.00.03 02.00.03 02.00.03 02.00.03 +mpi2_pci.h 02.00.00 02.00.00 02.00.00 02.00.00 02.00.00 Filename 02.00.35 02.00.34 02.00.33 02.00.32 02.00.31 02.00.30 ---------- -------- -------- -------- -------- -------- -------- Index: mpi/mpi2_init.h =================================================================== --- mpi/mpi2_init.h +++ mpi/mpi2_init.h @@ -42,7 +42,7 @@ * Title: MPI SCSI initiator mode messages and structures * Creation Date: June 23, 2006 * - * mpi2_init.h Version: 02.00.20 + * mpi2_init.h Version: 02.00.21 * * 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 @@ -62,7 +62,7 @@ * 05-21-08 02.00.05 Fixed typo in name of Mpi2SepRequest_t. * 10-02-08 02.00.06 Removed Untagged and No Disconnect values from SCSI IO * Control field Task Attribute flags. - * Moved LUN field defines to mpi2.h becasue they are + * Moved LUN field defines to mpi2.h because they are * common to many structures. * 05-06-09 02.00.07 Changed task management type of Query Unit Attention to * Query Asynchronous Event. @@ -90,6 +90,8 @@ * 08-26-15 02.00.18 Added SCSITASKMGMT_MSGFLAGS for Target Reset. * 12-18-15 02.00.19 Added EEDPObservedValue added to SCSI IO Reply message. * 01-04-16 02.00.20 Modified EEDP reported values in SCSI IO Reply message. + * 01-21-16 02.00.21 Modified MPI26_SCSITASKMGMT_MSGFLAGS_PCIE* defines to + * be unique within first 32 characters. * -------------------------------------------------------------------------- */ @@ -491,12 +493,13 @@ #define MPI2_SCSITASKMGMT_TASKTYPE_QRY_UNIT_ATTENTION (MPI2_SCSITASKMGMT_TASKTYPE_QRY_ASYNC_EVENT) /* MsgFlags bits */ - -#define MPI2_SCSITASKMGMT_MSGFLAGS_MASK_TARGET_RESET (0x18) -#define MPI2_SCSITASKMGMT_MSGFLAGS_LINK_RESET (0x00) +#define MPI2_SCSITASKMGMT_MSGFLAGS_MASK_TARGET_RESET (0x18) +#define MPI26_SCSITASKMGMT_MSGFLAGS_HOT_RESET_PCIE (0x00) +#define MPI2_SCSITASKMGMT_MSGFLAGS_LINK_RESET (0x00) #define MPI2_SCSITASKMGMT_MSGFLAGS_DO_NOT_SEND_TASK_IU (0x01) -#define MPI2_SCSITASKMGMT_MSGFLAGS_NEXUS_RESET_SRST (0x08) -#define MPI2_SCSITASKMGMT_MSGFLAGS_SAS_HARD_LINK_RESET (0x10) +#define MPI2_SCSITASKMGMT_MSGFLAGS_NEXUS_RESET_SRST (0x08) +#define MPI2_SCSITASKMGMT_MSGFLAGS_SAS_HARD_LINK_RESET (0x10) +#define MPI26_SCSITASKMGMT_MSGFLAGS_PROTOCOL_LVL_RST_PCIE (0x18) /* SCSI Task Management Reply Message */ Index: mpi/mpi2_ioc.h =================================================================== --- mpi/mpi2_ioc.h +++ mpi/mpi2_ioc.h @@ -42,7 +42,7 @@ * Title: MPI IOC, Port, Event, FW Download, and FW Upload messages * Creation Date: October 11, 2006 * - * mpi2_ioc.h Version: 02.00.27 + * mpi2_ioc.h Version: 02.00.30 * * 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 @@ -177,9 +177,29 @@ * MPI26_EVENT_DATA_PCIE_LINK_COUNTER. * Added MPI26_CTRL_OP_SHUTDOWN. * Added MPI26_CTRL_OP_LINK_CLEAR_ERROR_LOG - * Added MPI26_FW_HEADER_PID_FAMILY_3324_SAS - * 08-25-15 02.00.27 Added IC ARCH Class based signature defines - * + * Added MPI26_FW_HEADER_PID_FAMILY_3324_SAS and + * MPI26_FW_HEADER_PID_FAMILY_3516_SAS. + * 08-25-15 02.00.27 Added IC ARCH Class based signature defines. + * Added MPI26_EVENT_PCIE_ENUM_ES_RESOURCES_EXHAUSTED event. + * Added ConigurationFlags field to IOCInit message to + * support NVMe SGL format control. + * Added PCIe SRIOV support. + * 02-17-16 02.00.28 Added SAS 4 22.5 gbs speed support. + * Added PCIe 4 16.0 GT/sec speec support. + * Removed AHCI support. + * Removed SOP support. + * 07-01-16 02.00.29 Added Archclass for 4008 product. + * Added IOCException MPI2_IOCFACTS_EXCEPT_PCIE_DISABLED + * 08-23-16 02.00.30 Added new defines for the ImageType field of FWDownload + * Request Message. + * Added new defines for the ImageType field of FWUpload + * Request Message. + * Added new values for the RegionType field in the Layout + * Data sections of the FLASH Layout Extended Image Data. + * Added new defines for the ReasonCode field of + * Active Cable Exception Event. + * Added MPI2_EVENT_ENCL_DEVICE_STATUS_CHANGE and + * MPI26_EVENT_DATA_ENCL_DEV_STATUS_CHANGE. * -------------------------------------------------------------------------- */ @@ -251,6 +271,9 @@ #define MPI2_IOCINIT_HDRVERSION_DEV_MASK (0x00FF) #define MPI2_IOCINIT_HDRVERSION_DEV_SHIFT (0) +/* ConfigurationFlags */ +#define MPI26_IOCINIT_CFGFLAGS_NVME_SGL_FORMAT (0x0001) + /* minimum depth for a Reply Descriptor Post Queue */ #define MPI2_RDPQ_DEPTH_MIN (16) @@ -363,6 +386,7 @@ #define MPI2_IOCFACTS_HDRVERSION_DEV_SHIFT (0) /* IOCExceptions */ +#define MPI2_IOCFACTS_EXCEPT_PCIE_DISABLED (0x0400) #define MPI2_IOCFACTS_EXCEPT_PARTIAL_MEMORY_FAILURE (0x0200) #define MPI2_IOCFACTS_EXCEPT_IR_FOREIGN_CONFIG_MAX (0x0100) @@ -383,6 +407,8 @@ /* ProductID field uses MPI2_FW_HEADER_PID_ */ /* IOCCapabilities */ +#define MPI26_IOCFACTS_CAPABILITY_PCIE_SRIOV (0x00100000) +#define MPI26_IOCFACTS_CAPABILITY_ATOMIC_REQ (0x00080000) #define MPI2_IOCFACTS_CAPABILITY_RDPQ_ARRAY_CAPABLE (0x00040000) #define MPI25_IOCFACTS_CAPABILITY_FAST_PATH_CAPABLE (0x00020000) #define MPI2_IOCFACTS_CAPABILITY_HOST_BASED_DISCOVERY (0x00010000) @@ -400,6 +426,7 @@ #define MPI2_IOCFACTS_CAPABILITY_TASK_SET_FULL_HANDLING (0x00000004) /* ProtocolFlags */ +#define MPI2_IOCFACTS_PROTOCOL_NVME_DEVICES (0x0008) /* MPI v2.6 and later */ #define MPI2_IOCFACTS_PROTOCOL_SCSI_INITIATOR (0x0002) #define MPI2_IOCFACTS_PROTOCOL_SCSI_TARGET (0x0001) @@ -452,6 +479,7 @@ #define MPI2_PORTFACTS_PORTTYPE_ISCSI (0x20) #define MPI2_PORTFACTS_PORTTYPE_SAS_PHYSICAL (0x30) #define MPI2_PORTFACTS_PORTTYPE_SAS_VIRTUAL (0x31) +#define MPI2_PORTFACTS_PORTTYPE_TRI_MODE (0x40) /* MPI v2.6 and later */ /**************************************************************************** @@ -564,6 +592,7 @@ #define MPI2_EVENT_SAS_INIT_TABLE_OVERFLOW (0x0019) #define MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST (0x001C) #define MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE (0x001D) +#define MPI2_EVENT_ENCL_DEVICE_STATUS_CHANGE (0x001D) /* MPI v2.6 and later */ #define MPI2_EVENT_IR_VOLUME (0x001E) #define MPI2_EVENT_IR_PHYSICAL_DISK (0x001F) #define MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST (0x0020) @@ -576,6 +605,10 @@ #define MPI2_EVENT_TEMP_THRESHOLD (0x0027) #define MPI2_EVENT_HOST_MESSAGE (0x0028) #define MPI2_EVENT_POWER_PERFORMANCE_CHANGE (0x0029) +#define MPI2_EVENT_PCIE_DEVICE_STATUS_CHANGE (0x0030) /* MPI v2.6 and later */ +#define MPI2_EVENT_PCIE_ENUMERATION (0x0031) /* MPI v2.6 and later */ +#define MPI2_EVENT_PCIE_TOPOLOGY_CHANGE_LIST (0x0032) /* MPI v2.6 and later */ +#define MPI2_EVENT_PCIE_LINK_COUNTER (0x0033) /* MPI v2.6 and later */ #define MPI2_EVENT_ACTIVE_CABLE_EXCEPTION (0x0034) /* MPI v2.6 and later */ #define MPI2_EVENT_MIN_PRODUCT_SPECIFIC (0x006E) #define MPI2_EVENT_MAX_PRODUCT_SPECIFIC (0x007F) @@ -688,11 +721,9 @@ MPI2_POINTER pMpi26EventDataActiveCableExcept_t; /* defines for ReasonCode field */ -#define MPI26_EVENT_ACTIVE_CABLE_INSUFFICIENT_POWER (0x00) -#define MPI26_EVENT_ACTIVE_CABLE_PRESENT (0x01) -#define MPI26_EVENT_ACTIVE_CABLE_DEGRADED (0x02) - - +#define MPI26_EVENT_ACTIVE_CABLE_INSUFFICIENT_POWER (0x00) +#define MPI26_EVENT_ACTIVE_CABLE_PRESENT (0x01) +#define MPI26_EVENT_ACTIVE_CABLE_DEGRADED (0x02) /* Hard Reset Received Event data */ @@ -1048,6 +1079,7 @@ #define MPI2_EVENT_SAS_TOPO_LR_RATE_3_0 (0x09) #define MPI2_EVENT_SAS_TOPO_LR_RATE_6_0 (0x0A) #define MPI25_EVENT_SAS_TOPO_LR_RATE_12_0 (0x0B) +#define MPI26_EVENT_SAS_TOPO_LR_RATE_22_5 (0x0C) /* values for the PhyStatus field */ #define MPI2_EVENT_SAS_TOPO_PHYSTATUS_VACANT (0x80) @@ -1075,12 +1107,19 @@ } MPI2_EVENT_DATA_SAS_ENCL_DEV_STATUS_CHANGE, MPI2_POINTER PTR_MPI2_EVENT_DATA_SAS_ENCL_DEV_STATUS_CHANGE, Mpi2EventDataSasEnclDevStatusChange_t, - MPI2_POINTER pMpi2EventDataSasEnclDevStatusChange_t; + MPI2_POINTER pMpi2EventDataSasEnclDevStatusChange_t, + MPI26_EVENT_DATA_ENCL_DEV_STATUS_CHANGE, + MPI2_POINTER PTR_MPI26_EVENT_DATA_ENCL_DEV_STATUS_CHANGE, + Mpi26EventDataEnclDevStatusChange_t, + MPI2_POINTER pMpi26EventDataEnclDevStatusChange_t; /* SAS Enclosure Device Status Change event ReasonCode values */ #define MPI2_EVENT_SAS_ENCL_RC_ADDED (0x01) #define MPI2_EVENT_SAS_ENCL_RC_NOT_RESPONDING (0x02) +/* Enclosure Device Status Change event ReasonCode values */ +#define MPI26_EVENT_ENCL_RC_ADDED (0x01) +#define MPI26_EVENT_ENCL_RC_NOT_RESPONDING (0x02) /* SAS PHY Counter Event data */ @@ -1168,6 +1207,167 @@ #define MPI2_EVENT_HBD_DT_SAS (0x01) +/* PCIe Device Status Change Event data (MPI v2.6 and later) */ + +typedef struct _MPI26_EVENT_DATA_PCIE_DEVICE_STATUS_CHANGE +{ + U16 TaskTag; /* 0x00 */ + U8 ReasonCode; /* 0x02 */ + U8 PhysicalPort; /* 0x03 */ + U8 ASC; /* 0x04 */ + U8 ASCQ; /* 0x05 */ + U16 DevHandle; /* 0x06 */ + U32 Reserved2; /* 0x08 */ + U64 WWID; /* 0x0C */ + U8 LUN[8]; /* 0x14 */ +} MPI26_EVENT_DATA_PCIE_DEVICE_STATUS_CHANGE, + MPI2_POINTER PTR_MPI26_EVENT_DATA_PCIE_DEVICE_STATUS_CHANGE, + Mpi26EventDataPCIeDeviceStatusChange_t, + MPI2_POINTER pMpi26EventDataPCIeDeviceStatusChange_t; + +/* PCIe Device Status Change Event data ReasonCode values */ +#define MPI26_EVENT_PCIDEV_STAT_RC_SMART_DATA (0x05) +#define MPI26_EVENT_PCIDEV_STAT_RC_UNSUPPORTED (0x07) +#define MPI26_EVENT_PCIDEV_STAT_RC_INTERNAL_DEVICE_RESET (0x08) +#define MPI26_EVENT_PCIDEV_STAT_RC_TASK_ABORT_INTERNAL (0x09) +#define MPI26_EVENT_PCIDEV_STAT_RC_ABORT_TASK_SET_INTERNAL (0x0A) +#define MPI26_EVENT_PCIDEV_STAT_RC_CLEAR_TASK_SET_INTERNAL (0x0B) +#define MPI26_EVENT_PCIDEV_STAT_RC_QUERY_TASK_INTERNAL (0x0C) +#define MPI26_EVENT_PCIDEV_STAT_RC_ASYNC_NOTIFICATION (0x0D) +#define MPI26_EVENT_PCIDEV_STAT_RC_CMP_INTERNAL_DEV_RESET (0x0E) +#define MPI26_EVENT_PCIDEV_STAT_RC_CMP_TASK_ABORT_INTERNAL (0x0F) +#define MPI26_EVENT_PCIDEV_STAT_RC_DEV_INIT_FAILURE (0x10) + + +/* PCIe Enumeration Event data (MPI v2.6 and later) */ + +typedef struct _MPI26_EVENT_DATA_PCIE_ENUMERATION +{ + U8 Flags; /* 0x00 */ + U8 ReasonCode; /* 0x01 */ + U8 PhysicalPort; /* 0x02 */ + U8 Reserved1; /* 0x03 */ + U32 EnumerationStatus; /* 0x04 */ +} MPI26_EVENT_DATA_PCIE_ENUMERATION, + MPI2_POINTER PTR_MPI26_EVENT_DATA_PCIE_ENUMERATION, + Mpi26EventDataPCIeEnumeration_t, + MPI2_POINTER pMpi26EventDataPCIeEnumeration_t; + +/* PCIe Enumeration Event data Flags values */ +#define MPI26_EVENT_PCIE_ENUM_DEVICE_CHANGE (0x02) +#define MPI26_EVENT_PCIE_ENUM_IN_PROGRESS (0x01) + +/* PCIe Enumeration Event data ReasonCode values */ +#define MPI26_EVENT_PCIE_ENUM_RC_STARTED (0x01) +#define MPI26_EVENT_PCIE_ENUM_RC_COMPLETED (0x02) + +/* PCIe Enumeration Event data EnumerationStatus values */ +#define MPI26_EVENT_PCIE_ENUM_ES_MAX_SWITCHES_EXCEED (0x40000000) +#define MPI26_EVENT_PCIE_ENUM_ES_MAX_DEVICES_EXCEED (0x20000000) +#define MPI26_EVENT_PCIE_ENUM_ES_RESOURCES_EXHAUSTED (0x10000000) + + +/* 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. + */ +#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 */ + U8 PortStatus; /* 0x02 */ + U8 Reserved1; /* 0x03 */ + U8 CurrentPortInfo; /* 0x04 */ + U8 Reserved2; /* 0x05 */ + U8 PreviousPortInfo; /* 0x06 */ + U8 Reserved3; /* 0x07 */ +} MPI26_EVENT_PCIE_TOPO_PORT_ENTRY, + MPI2_POINTER PTR_MPI26_EVENT_PCIE_TOPO_PORT_ENTRY, + Mpi26EventPCIeTopoPortEntry_t, + MPI2_POINTER pMpi26EventPCIeTopoPortEntry_t; + +/* PCIe Topology Change List Event data PortStatus values */ +#define MPI26_EVENT_PCIE_TOPO_PS_DEV_ADDED (0x01) +#define MPI26_EVENT_PCIE_TOPO_PS_NOT_RESPONDING (0x02) +#define MPI26_EVENT_PCIE_TOPO_PS_PORT_CHANGED (0x03) +#define MPI26_EVENT_PCIE_TOPO_PS_NO_CHANGE (0x04) +#define MPI26_EVENT_PCIE_TOPO_PS_DELAY_NOT_RESPONDING (0x05) + +/* PCIe Topology Change List Event data defines for CurrentPortInfo and PreviousPortInfo */ +#define MPI26_EVENT_PCIE_TOPO_PI_LANE_MASK (0xF0) +#define MPI26_EVENT_PCIE_TOPO_PI_LANES_UNKNOWN (0x00) +#define MPI26_EVENT_PCIE_TOPO_PI_1_LANE (0x10) +#define MPI26_EVENT_PCIE_TOPO_PI_2_LANES (0x20) +#define MPI26_EVENT_PCIE_TOPO_PI_4_LANES (0x30) +#define MPI26_EVENT_PCIE_TOPO_PI_8_LANES (0x40) + +#define MPI26_EVENT_PCIE_TOPO_PI_RATE_MASK (0x0F) +#define MPI26_EVENT_PCIE_TOPO_PI_RATE_UNKNOWN (0x00) +#define MPI26_EVENT_PCIE_TOPO_PI_RATE_DISABLED (0x01) +#define MPI26_EVENT_PCIE_TOPO_PI_RATE_2_5 (0x02) +#define MPI26_EVENT_PCIE_TOPO_PI_RATE_5_0 (0x03) +#define MPI26_EVENT_PCIE_TOPO_PI_RATE_8_0 (0x04) +#define MPI26_EVENT_PCIE_TOPO_PI_RATE_16_0 (0x05) + +typedef struct _MPI26_EVENT_DATA_PCIE_TOPOLOGY_CHANGE_LIST +{ + U16 EnclosureHandle; /* 0x00 */ + U16 SwitchDevHandle; /* 0x02 */ + U8 NumPorts; /* 0x04 */ + U8 Reserved1; /* 0x05 */ + U16 Reserved2; /* 0x06 */ + U8 NumEntries; /* 0x08 */ + U8 StartPortNum; /* 0x09 */ + U8 SwitchStatus; /* 0x0A */ + U8 PhysicalPort; /* 0x0B */ + MPI26_EVENT_PCIE_TOPO_PORT_ENTRY PortEntry[MPI26_EVENT_PCIE_TOPO_PORT_COUNT]; /* 0x0C */ +} MPI26_EVENT_DATA_PCIE_TOPOLOGY_CHANGE_LIST, + MPI2_POINTER PTR_MPI26_EVENT_DATA_PCIE_TOPOLOGY_CHANGE_LIST, + Mpi26EventDataPCIeTopologyChangeList_t, + MPI2_POINTER pMpi26EventDataPCIeTopologyChangeList_t; + +/* PCIe Topology Change List Event data SwitchStatus values */ +#define MPI26_EVENT_PCIE_TOPO_SS_NO_PCIE_SWITCH (0x00) +#define MPI26_EVENT_PCIE_TOPO_SS_ADDED (0x01) +#define MPI26_EVENT_PCIE_TOPO_SS_NOT_RESPONDING (0x02) +#define MPI26_EVENT_PCIE_TOPO_SS_RESPONDING (0x03) +#define MPI26_EVENT_PCIE_TOPO_SS_DELAY_NOT_RESPONDING (0x04) + +/* PCIe Link Counter Event data (MPI v2.6 and later) */ + +typedef struct _MPI26_EVENT_DATA_PCIE_LINK_COUNTER +{ + U64 TimeStamp; /* 0x00 */ + U32 Reserved1; /* 0x08 */ + U8 LinkEventCode; /* 0x0C */ + U8 LinkNum; /* 0x0D */ + U16 Reserved2; /* 0x0E */ + U32 LinkEventInfo; /* 0x10 */ + U8 CounterType; /* 0x14 */ + U8 ThresholdWindow; /* 0x15 */ + U8 TimeUnits; /* 0x16 */ + U8 Reserved3; /* 0x17 */ + U32 EventThreshold; /* 0x18 */ + U16 ThresholdFlags; /* 0x1C */ + U16 Reserved4; /* 0x1E */ +} MPI26_EVENT_DATA_PCIE_LINK_COUNTER, + MPI2_POINTER PTR_MPI26_EVENT_DATA_PCIE_LINK_COUNTER, + Mpi26EventDataPcieLinkCounter_t, MPI2_POINTER pMpi26EventDataPcieLinkCounter_t; + + +/* use MPI26_PCIELINK3_EVTCODE_ values from mpi2_cnfg.h for the LinkEventCode field */ + +/* use MPI26_PCIELINK3_COUNTER_TYPE_ values from mpi2_cnfg.h for the CounterType field */ + +/* use MPI26_PCIELINK3_TIME_UNITS_ values from mpi2_cnfg.h for the TimeUnits field */ + +/* use MPI26_PCIELINK3_TFLAGS_ values from mpi2_cnfg.h for the ThresholdFlags field */ + /**************************************************************************** * EventAck message ****************************************************************************/ @@ -1293,6 +1493,13 @@ #define MPI2_FW_DOWNLOAD_ITYPE_COMPLETE (0x0A) #define MPI2_FW_DOWNLOAD_ITYPE_COMMON_BOOT_BLOCK (0x0B) #define MPI2_FW_DOWNLOAD_ITYPE_PUBLIC_KEY (0x0C) /* MPI v2.5 and newer */ +#define MPI2_FW_DOWNLOAD_ITYPE_SBR (0x0E) +#define MPI2_FW_DOWNLOAD_ITYPE_SBR_BACKUP (0x0F) +#define MPI2_FW_DOWNLOAD_ITYPE_HIIM (0x10) +#define MPI2_FW_DOWNLOAD_ITYPE_HIIA (0x11) +#define MPI2_FW_DOWNLOAD_ITYPE_CTLR (0x12) +#define MPI2_FW_DOWNLOAD_ITYPE_IMR_FIRMWARE (0x13) +#define MPI2_FW_DOWNLOAD_ITYPE_MR_NVDATA (0x14) #define MPI2_FW_DOWNLOAD_ITYPE_MIN_PRODUCT_SPECIFIC (0xF0) /* MPI v2.0 FWDownload TransactionContext Element */ @@ -1386,6 +1593,13 @@ #define MPI2_FW_UPLOAD_ITYPE_COMPLETE (0x0A) #define MPI2_FW_UPLOAD_ITYPE_COMMON_BOOT_BLOCK (0x0B) #define MPI2_FW_UPLOAD_ITYPE_CBB_BACKUP (0x0D) +#define MPI2_FW_UPLOAD_ITYPE_SBR (0x0E) +#define MPI2_FW_UPLOAD_ITYPE_SBR_BACKUP (0x0F) +#define MPI2_FW_UPLOAD_ITYPE_HIIM (0x10) +#define MPI2_FW_UPLOAD_ITYPE_HIIA (0x11) +#define MPI2_FW_UPLOAD_ITYPE_CTLR (0x12) +#define MPI2_FW_UPLOAD_ITYPE_IMR_FIRMWARE (0x13) +#define MPI2_FW_UPLOAD_ITYPE_MR_NVDATA (0x14) /* MPI v2.0 FWUpload TransactionContext Element */ typedef struct _MPI2_FW_UPLOAD_TCSGE @@ -1509,8 +1723,10 @@ #define MPI26_FW_HEADER_SIGNATURE0_ARC_0 (0x5A) #define MPI26_FW_HEADER_SIGNATURE0_ARC_1 (0x00) #define MPI26_FW_HEADER_SIGNATURE0_ARC_2 (0x01) +#define MPI26_FW_HEADER_SIGNATURE0_ARC_3 (0x02) #define MPI26_FW_HEADER_SIGNATURE0 (MPI26_FW_HEADER_SIGNATURE0_BASE+MPI26_FW_HEADER_SIGNATURE0_ARC_0) // legacy (0x5AEAA55A) #define MPI26_FW_HEADER_SIGNATURE0_3516 (MPI26_FW_HEADER_SIGNATURE0_BASE+MPI26_FW_HEADER_SIGNATURE0_ARC_1) +#define MPI26_FW_HEADER_SIGNATURE0_4008 (MPI26_FW_HEADER_SIGNATURE0_BASE+MPI26_FW_HEADER_SIGNATURE0_ARC_3) /* Signature1 field */ #define MPI2_FW_HEADER_SIGNATURE1_OFFSET (0x08) @@ -1665,7 +1881,13 @@ #define MPI2_FLASH_REGION_COMMON_BOOT_BLOCK (0x0A) #define MPI2_FLASH_REGION_INIT (MPI2_FLASH_REGION_COMMON_BOOT_BLOCK) /* older name */ #define MPI2_FLASH_REGION_CBB_BACKUP (0x0D) - +#define MPI2_FLASH_REGION_SBR (0x0E) +#define MPI2_FLASH_REGION_SBR_BACKUP (0x0F) +#define MPI2_FLASH_REGION_HIIM (0x10) +#define MPI2_FLASH_REGION_HIIA (0x11) +#define MPI2_FLASH_REGION_CTLR (0x12) +#define MPI2_FLASH_REGION_IMR_FIRMWARE (0x13) +#define MPI2_FLASH_REGION_MR_NVDATA (0x14) /* ImageRevision */ #define MPI2_FLASH_LAYOUT_IMAGE_REVISION (0x00) @@ -1960,6 +2182,8 @@ #define MPI26_CTRL_OP_DEV_ENABLE_PERSIST_CONNECTION (0x17) #define MPI26_CTRL_OP_DEV_DISABLE_PERSIST_CONNECTION (0x18) #define MPI26_CTRL_OP_DEV_CLOSE_PERSIST_CONNECTION (0x19) +#define MPI26_CTRL_OP_ENABLE_NVME_SGL_FORMAT (0x1A) +#define MPI26_CTRL_OP_DISABLE_NVME_SGL_FORMAT (0x1B) #define MPI26_CTRL_OP_PRODUCT_SPECIFIC_MIN (0x80) /* values for the PrimFlags field */ Index: mpi/mpi2_tool.h =================================================================== --- mpi/mpi2_tool.h +++ mpi/mpi2_tool.h @@ -42,7 +42,7 @@ * Title: MPI diagnostic tool structures and definitions * Creation Date: March 26, 2007 * - * mpi2_tool.h Version: 02.00.13 + * mpi2_tool.h Version: 02.00.14 * * Version History * --------------- @@ -71,6 +71,8 @@ * 08-19-13 02.00.11 Added MPI2_TOOLBOX_TEXT_DISPLAY_TOOL and related info. * 01-08-14 02.00.12 Added MPI2_TOOLBOX_CLEAN_BIT26_PRODUCT_SPECIFIC. * 11-18-14 02.00.13 Updated copyright information. + * 08-25-16 02.00.14 Added new values for the Flags field of Toolbox Clean + * Tool Request Message. * -------------------------------------------------------------------------- */ @@ -145,6 +147,16 @@ #define MPI2_TOOLBOX_CLEAN_BIT26_PRODUCT_SPECIFIC (0x04000000) #define MPI2_TOOLBOX_CLEAN_MEGARAID (0x02000000) #define MPI2_TOOLBOX_CLEAN_INITIALIZATION (0x01000000) +#define MPI2_TOOLBOX_CLEAN_SBR (0x00800000) +#define MPI2_TOOLBOX_CLEAN_SBR_BACKUP (0x00400000) +#define MPI2_TOOLBOX_CLEAN_HIIM (0x00200000) +#define MPI2_TOOLBOX_CLEAN_HIIA (0x00100000) +#define MPI2_TOOLBOX_CLEAN_CTLR (0x00080000) +#define MPI2_TOOLBOX_CLEAN_IMR_FIRMWARE (0x00040000) +#define MPI2_TOOLBOX_CLEAN_MR_NVDATA (0x00020000) +#define MPI2_TOOLBOX_CLEAN_RESERVED_5_16 (0x0001FFE0) +#define MPI2_TOOLBOX_CLEAN_ALL_BUT_MPB (0x00000010) +#define MPI2_TOOLBOX_CLEAN_ENTIRE_FLASH (0x00000008) #define MPI2_TOOLBOX_CLEAN_FLASH (0x00000004) #define MPI2_TOOLBOX_CLEAN_SEEPROM (0x00000002) #define MPI2_TOOLBOX_CLEAN_NVSRAM (0x00000001) Index: mpr.c =================================================================== --- mpr.c +++ mpr.c @@ -63,18 +63,21 @@ #include #include +#include #include #include #include #include #include +#include #include #include #include #include #include #include +#include static int mpr_diag_reset(struct mpr_softc *sc, int sleep_flag); static int mpr_init_queues(struct mpr_softc *sc); @@ -87,6 +90,7 @@ static int mpr_alloc_queues(struct mpr_softc *sc); static int mpr_alloc_replies(struct mpr_softc *sc); static int mpr_alloc_requests(struct mpr_softc *sc); +static int mpr_alloc_nvme_prp_pages(struct mpr_softc *sc); static int mpr_attach_log(struct mpr_softc *sc); static __inline void mpr_complete_command(struct mpr_softc *sc, struct mpr_command *cm); @@ -110,7 +114,7 @@ /* * Added this union to smoothly convert le64toh cm->cm_desc.Words. - * Compiler only supports unint64_t to be passed as an argument. + * Compiler only supports uint64_t to be passed as an argument. * Otherwise it will through this error: * "aggregate value used where an integer was expected" */ @@ -120,7 +124,7 @@ u32 low; u32 high; } u; -}reply_descriptor,address_descriptor; +} reply_descriptor, request_descriptor; /* Rate limit chain-fail messages to 1 per minute */ static struct timeval mpr_chainfail_interval = { 60, 0 }; @@ -311,7 +315,6 @@ if (error) device_printf(sc->mpr_dev, "Cannot transition IOC to ready\n"); - return (error); } @@ -392,7 +395,8 @@ mpr_printf(sc, "IOCCapabilities: %b\n", sc->facts->IOCCapabilities, "\20" "\3ScsiTaskFull" "\4DiagTrace" "\5SnapBuf" "\6ExtBuf" "\7EEDP" "\10BiDirTarg" "\11Multicast" "\14TransRetry" "\15IR" - "\16EventReplay" "\17RaidAccel" "\20MSIXIndex" "\21HostDisc"); + "\16EventReplay" "\17RaidAccel" "\20MSIXIndex" "\21HostDisc" + "\22FastPath" "\23RDPQArray" "\24AtomicReqDesc" "\25PCIeSRIOV"); /* * If the chip doesn't support event replay then a hard reset will be @@ -480,12 +484,15 @@ enabled = TRUE; /* - * Set flag if EEDP is supported and if TLR is supported. + * Set flags for some supported items. */ if (sc->facts->IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_EEDP) sc->eedp_enabled = TRUE; if (sc->facts->IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_TLR) sc->control_TLR = TRUE; + if (sc->facts->IOCCapabilities & + MPI26_IOCFACTS_CAPABILITY_ATOMIC_REQ) + sc->atomic_desc_capable = TRUE; /* * Size the queues. Since the reply queues always need one free @@ -501,6 +508,7 @@ TAILQ_INIT(&sc->req_list); TAILQ_INIT(&sc->high_priority_req_list); TAILQ_INIT(&sc->chain_list); + TAILQ_INIT(&sc->prp_page_list); TAILQ_INIT(&sc->tm_list); } @@ -634,6 +642,14 @@ if (sc->sense_dmat != NULL) bus_dma_tag_destroy(sc->sense_dmat); + if (sc->prp_page_busaddr != 0) + bus_dmamap_unload(sc->prp_page_dmat, sc->prp_page_map); + if (sc->prp_pages != NULL) + bus_dmamem_free(sc->prp_page_dmat, sc->prp_pages, + sc->prp_page_map); + if (sc->prp_page_dmat != NULL) + bus_dma_tag_destroy(sc->prp_page_dmat); + if (sc->reply_busaddr != 0) bus_dmamap_unload(sc->reply_dmat, sc->reply_map); if (sc->reply_frames != NULL) @@ -651,6 +667,8 @@ if (sc->chains != NULL) free(sc->chains, M_MPR); + if (sc->prps != NULL) + free(sc->prps, M_MPR); if (sc->commands != NULL) { for (i = 1; i < sc->num_reqs; i++) { cm = &sc->commands[i]; @@ -804,7 +822,7 @@ count++; } while (--cntdn); - out: +out: mpr_dprint(sc, MPR_FAULT, "%s: failed due to timeout count(%d), " "int_status(%x)!\n", __func__, count, int_status); return (ETIMEDOUT); @@ -959,7 +977,7 @@ static void mpr_enqueue_request(struct mpr_softc *sc, struct mpr_command *cm) { - reply_descriptor rd; + request_descriptor rd; MPR_FUNCTRACE(sc); mpr_dprint(sc, MPR_TRACE, "SMID %u cm %p ccb %p\n", @@ -972,14 +990,19 @@ if (++sc->io_cmds_active > sc->io_cmds_highwater) sc->io_cmds_highwater++; - rd.u.low = cm->cm_desc.Words.Low; - rd.u.high = cm->cm_desc.Words.High; - rd.word = htole64(rd.word); - /* TODO-We may need to make below regwrite atomic */ - mpr_regwrite(sc, MPI2_REQUEST_DESCRIPTOR_POST_LOW_OFFSET, - rd.u.low); - mpr_regwrite(sc, MPI2_REQUEST_DESCRIPTOR_POST_HIGH_OFFSET, - rd.u.high); + if (sc->atomic_desc_capable) { + rd.u.low = cm->cm_desc.Words.Low; + mpr_regwrite(sc, MPI26_ATOMIC_REQUEST_DESCRIPTOR_POST_OFFSET, + rd.u.low); + } else { + rd.u.low = cm->cm_desc.Words.Low; + rd.u.high = cm->cm_desc.Words.High; + rd.word = htole64(rd.word); + mpr_regwrite(sc, MPI2_REQUEST_DESCRIPTOR_POST_LOW_OFFSET, + rd.u.low); + mpr_regwrite(sc, MPI2_REQUEST_DESCRIPTOR_POST_HIGH_OFFSET, + rd.u.high); + } } /* @@ -1047,6 +1070,7 @@ time_in_msec = (now.tv_sec * 1000 + now.tv_usec/1000); init.TimeStamp.High = htole32((time_in_msec >> 32) & 0xFFFFFFFF); init.TimeStamp.Low = htole32(time_in_msec & 0xFFFFFFFF); + init.HostPageSize = HOST_PAGE_SIZE_4K; error = mpr_request_sync(sc, &init, &reply, req_sz, reply_sz, 5); if ((reply.IOCStatus & MPI2_IOCSTATUS_MASK) != MPI2_IOCSTATUS_SUCCESS) @@ -1276,6 +1300,16 @@ sc->chain_free_lowwater++; } + /* + * Allocate NVMe PRP Pages for NVMe SGL support only if the FW supports + * these devices. + */ + if ((sc->facts->MsgVersion >= MPI2_VERSION_02_06) && + (sc->facts->ProtocolFlags & MPI2_IOCFACTS_PROTOCOL_NVME_DEVICES)) { + if (mpr_alloc_nvme_prp_pages(sc) == ENOMEM) + return (ENOMEM); + } + /* XXX Need to pick a more precise value */ nsegs = (MAXPHYS / PAGE_SIZE) + 1; if (bus_dma_tag_create( sc->mpr_parent_dmat, /* parent */ @@ -1316,15 +1350,17 @@ cm->cm_desc.Default.SMID = i; cm->cm_sc = sc; TAILQ_INIT(&cm->cm_chain_list); + TAILQ_INIT(&cm->cm_prp_page_list); callout_init_mtx(&cm->cm_callout, &sc->mpr_mtx, 0); /* XXX Is a failure here a critical problem? */ - if (bus_dmamap_create(sc->buffer_dmat, 0, &cm->cm_dmamap) == 0) + if (bus_dmamap_create(sc->buffer_dmat, 0, &cm->cm_dmamap) + == 0) { if (i <= sc->facts->HighPriorityCredit) mpr_free_high_priority_command(sc, cm); else mpr_free_command(sc, cm); - else { + } else { panic("failed to allocate command %d\n", i); sc->num_reqs = i; break; @@ -1334,6 +1370,86 @@ return (0); } +/* + * Allocate contiguous buffers for PCIe NVMe devices for building native PRPs, + * which are scatter/gather lists for NVMe devices. + * + * This buffer must be contiguous due to the nature of how NVMe PRPs are built + * and translated by FW. + * + * returns ENOMEM if memory could not be allocated, otherwise returns 0. + */ +static int +mpr_alloc_nvme_prp_pages(struct mpr_softc *sc) +{ + int PRPs_per_page, PRPs_required, pages_required; + int rsize, i; + struct mpr_prp_page *prp_page; + + /* + * Assuming a MAX_IO_SIZE of 1MB and a PAGE_SIZE of 4k, the max number + * of PRPs (NVMe's Scatter/Gather Element) needed per I/O is: + * MAX_IO_SIZE / PAGE_SIZE = 256 + * + * 1 PRP entry in main frame for PRP list pointer still leaves 255 PRPs + * required for the remainder of the 1MB I/O. 512 PRPs can fit into one + * page (4096 / 8 = 512), so only one page is required for each I/O. + * + * Each of these buffers will need to be contiguous. For simplicity, + * only one buffer is allocated here, which has all of the space + * required for the NVMe Queue Depth. If there are problems allocating + * this one buffer, this function will need to change to allocate + * individual, contiguous NVME_QDEPTH buffers. + * + * The real calculation will use the real max io size. Above is just an + * example. + * + */ + PRPs_required = sc->maxio / PAGE_SIZE; + PRPs_per_page = (PAGE_SIZE / PRP_ENTRY_SIZE) - 1; + pages_required = (PRPs_required / PRPs_per_page) + 1; + + sc->prp_buffer_size = PAGE_SIZE * pages_required; + rsize = sc->prp_buffer_size * NVME_QDEPTH; + if (bus_dma_tag_create( sc->mpr_parent_dmat, /* parent */ + 4, 0, /* algnmnt, boundary */ + BUS_SPACE_MAXADDR_32BIT,/* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + rsize, /* maxsize */ + 1, /* nsegments */ + rsize, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->prp_page_dmat)) { + device_printf(sc->mpr_dev, "Cannot allocate NVMe PRP DMA " + "tag\n"); + return (ENOMEM); + } + if (bus_dmamem_alloc(sc->prp_page_dmat, (void **)&sc->prp_pages, + BUS_DMA_NOWAIT, &sc->prp_page_map)) { + device_printf(sc->mpr_dev, "Cannot allocate NVMe PRP memory\n"); + return (ENOMEM); + } + bzero(sc->prp_pages, rsize); + bus_dmamap_load(sc->prp_page_dmat, sc->prp_page_map, sc->prp_pages, + rsize, mpr_memaddr_cb, &sc->prp_page_busaddr, 0); + + sc->prps = malloc(sizeof(struct mpr_prp_page) * NVME_QDEPTH, M_MPR, + M_WAITOK | M_ZERO); + for (i = 0; i < NVME_QDEPTH; i++) { + prp_page = &sc->prps[i]; + prp_page->prp_page = (uint64_t *)(sc->prp_pages + + i * sc->prp_buffer_size); + prp_page->prp_page_busaddr = (uint64_t)(sc->prp_page_busaddr + + i * sc->prp_buffer_size); + mpr_free_prp_page(sc, prp_page); + sc->prp_pages_free_lowwater++; + } + + return (0); +} + static int mpr_init_queues(struct mpr_softc *sc) { @@ -1352,8 +1468,10 @@ /* * Initialize all of the free queue entries. */ - for (i = 0; i < sc->fqdepth; i++) - sc->free_queue[i] = sc->reply_busaddr + (i * sc->facts->ReplyFrameSize * 4); + for (i = 0; i < sc->fqdepth; i++) { + sc->free_queue[i] = sc->reply_busaddr + + (i * sc->facts->ReplyFrameSize * 4); + } sc->replyfreeindex = sc->num_replies; return (0); @@ -1520,6 +1638,18 @@ SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO, "use_phy_num", CTLFLAG_RD, &sc->use_phynum, 0, "Use the phy number for enumeration"); + + SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), + OID_AUTO, "prp_pages_free", CTLFLAG_RD, + &sc->prp_pages_free, 0, "number of free PRP pages"); + + SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), + OID_AUTO, "prp_pages_free_lowwater", CTLFLAG_RD, + &sc->prp_pages_free_lowwater, 0,"lowest number of free PRP pages"); + + SYSCTL_ADD_UQUAD(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), + OID_AUTO, "prp_page_alloc_fail", CTLFLAG_RD, + &sc->prp_page_alloc_fail, "PRP page allocation failures"); } int @@ -1912,6 +2042,7 @@ switch (flags) { case MPI2_RPY_DESCRIPT_FLAGS_SCSI_IO_SUCCESS: case MPI25_RPY_DESCRIPT_FLAGS_FAST_PATH_SCSI_IO_SUCCESS: + case MPI26_RPY_DESCRIPT_FLAGS_PCIE_ENCAPSULATED_SUCCESS: cm = &sc->commands[le16toh(desc->SCSIIOSuccess.SMID)]; cm->cm_reply = NULL; break; @@ -2200,6 +2331,519 @@ return (mpr_update_events(sc, NULL, NULL)); } +/** +* mpr_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 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 describe +* using the two PRP entriess inside the NVMe message, then PRP1 describes the +* first data memory segment, and PRP2 contains a pointer to a PRP list located +* elsewhere in memory to describe the remaining data memory segments. The PRP +* list will be contiguous. + +* The native SGL for NVMe devices is a Physical Region Page (PRP). A PRP +* consists of a list of PRP entries to describe a number of noncontigous +* physical memory segments as a single memory buffer, just as a SGL does. Note +* however, that this function is only used by the IOCTL call, so the memory +* given will be guaranteed to be contiguous. There is no need to translate +* non-contiguous SGL into a PRP in this case. All PRPs will describe contiguous +* space that is one page size each. +* +* Each NVMe message contains two PRP entries. The first (PRP1) either contains +* a PRP list pointer or a PRP element, depending upon the command. PRP2 contains +* the second PRP element if the memory being described fits within 2 PRP +* entries, or a PRP list pointer if the PRP spans more than two entries. +* +* A PRP list pointer contains the address of a PRP list, structured as a linear +* array of PRP entries. Each PRP entry in this list describes a segment of +* physical memory. +* +* Each 64-bit PRP entry comprises an address and an offset field. The address +* always points to the beginning of a PAGE_SIZE physical memory page, and the +* offset describes where within that page the memory segment begins. Only the +* first element in a PRP list may contain a non-zero offest, implying that all +* memory segments following the first begin at the start of a PAGE_SIZE page. +* +* Each PRP element normally describes a chunck of PAGE_SIZE physical memory, +* with exceptions for the first and last elements in the list. If the memory +* being described by the list begins at a non-zero offset within the first page, +* then the first PRP element will contain a non-zero offset indicating where the +* region begins within the page. The last memory segment may end before the end +* of the PAGE_SIZE segment, depending upon the overall size of the memory being +* described by the PRP list. +* +* 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. +* +* Returns nothing. +*/ +void +mpr_build_nvme_prp(struct mpr_softc *sc, struct mpr_command *cm, + Mpi26NVMeEncapsulatedRequest_t *nvme_encap_request, void *data, + uint32_t data_in_sz, uint32_t data_out_sz) +{ + int prp_size = PRP_ENTRY_SIZE; + uint64_t *prp_entry, *prp1_entry, *prp2_entry; + uint64_t *prp_entry_phys, *prp_page, *prp_page_phys; + uint32_t offset, entry_len, page_mask_result, page_mask; + bus_addr_t paddr; + size_t length; + struct mpr_prp_page *prp_page_info = NULL; + + /* + * Not all commands require a data transfer. If no data, just return + * without constructing any PRP. + */ + if (!data_in_sz && !data_out_sz) + return; + + /* + * Set pointers to PRP1 and PRP2, which are in the NVMe command. PRP1 is + * located at a 24 byte offset from the start of the NVMe command. Then + * set the current PRP entry pointer to PRP1. + */ + prp1_entry = (uint64_t *)(nvme_encap_request->NVMe_Command + + NVME_CMD_PRP1_OFFSET); + prp2_entry = (uint64_t *)(nvme_encap_request->NVMe_Command + + NVME_CMD_PRP2_OFFSET); + prp_entry = prp1_entry; + + /* + * For the PRP entries, use the specially allocated buffer of + * contiguous memory. PRP Page allocation failures should not happen + * because there should be enough PRP page buffers to account for the + * possible NVMe QDepth. + */ + prp_page_info = mpr_alloc_prp_page(sc); + KASSERT(prp_page_info != NULL, ("%s: There are no PRP Pages left to be " + "used for building a native NVMe SGL.\n", __func__)); + prp_page = (uint64_t *)prp_page_info->prp_page; + prp_page_phys = (uint64_t *)(uintptr_t)prp_page_info->prp_page_busaddr; + + /* + * Insert the allocated PRP page into the command's PRP page list. This + * will be freed when the command is freed. + */ + TAILQ_INSERT_TAIL(&cm->cm_prp_page_list, prp_page_info, prp_page_link); + + /* + * Check if we are within 1 entry of a page boundary we don't want our + * first entry to be a PRP List entry. + */ + page_mask = PAGE_SIZE - 1; + page_mask_result = (uintptr_t)((uint8_t *)prp_page + prp_size) & + page_mask; + if (!page_mask_result) + { + /* Bump up to next page boundary. */ + prp_page = (uint64_t *)((uint8_t *)prp_page + prp_size); + prp_page_phys = (uint64_t *)((uint8_t *)prp_page_phys + + prp_size); + } + + /* + * Set PRP physical pointer, which initially points to the current PRP + * DMA memory page. + */ + prp_entry_phys = prp_page_phys; + + /* Get physical address and length of the data buffer. */ + paddr = (bus_addr_t)data; + if (data_in_sz) + length = data_in_sz; + else + length = data_out_sz; + + /* Loop while the length is not zero. */ + while (length) + { + /* + * Check if we need to put a list pointer here if we are at page + * boundary - prp_size (8 bytes). + */ + page_mask_result = (uintptr_t)((uint8_t *)prp_entry_phys + + prp_size) & page_mask; + if (!page_mask_result) + { + /* + * This is the last entry in a PRP List, so we need to + * put a PRP list pointer here. What this does is: + * - bump the current memory pointer to the next + * address, which will be the next full page. + * - set the PRP Entry to point to that page. This is + * now the PRP List pointer. + * - bump the PRP Entry pointer the start of the next + * page. Since all of this PRP memory is contiguous, + * no need to get a new page - it's just the next + * address. + */ + prp_entry_phys++; + *prp_entry = + htole64((uint64_t)(uintptr_t)prp_entry_phys); + prp_entry++; + } + + /* Need to handle if entry will be part of a page. */ + offset = (uint32_t)paddr & page_mask; + entry_len = PAGE_SIZE - offset; + + if (prp_entry == prp1_entry) + { + /* + * Must fill in the first PRP pointer (PRP1) before + * moving on. + */ + *prp1_entry = htole64((uint64_t)paddr); + + /* + * Now point to the second PRP entry within the + * command (PRP2). + */ + prp_entry = prp2_entry; + } + else if (prp_entry == prp2_entry) + { + /* + * Should the PRP2 entry be a PRP List pointer or just a + * regular PRP pointer? If there is more than one more + * page of data, must use a PRP List pointer. + */ + if (length > PAGE_SIZE) + { + /* + * PRP2 will contain a PRP List pointer because + * more PRP's are needed with this command. The + * list will start at the beginning of the + * contiguous buffer. + */ + *prp2_entry = + htole64( + (uint64_t)(uintptr_t)prp_entry_phys); + + /* + * The next PRP Entry will be the start of the + * first PRP List. + */ + prp_entry = prp_page; + } + else + { + /* + * After this, the PRP Entries are complete. + * This command uses 2 PRP's and no PRP list. + */ + *prp2_entry = htole64((uint64_t)paddr); + } + } + else + { + /* + * Put entry in list and bump the addresses. + * + * After PRP1 and PRP2 are filled in, this will fill in + * all remaining PRP entries in a PRP List, one per each + * time through the loop. + */ + *prp_entry = htole64((uint64_t)paddr); + prp_entry++; + prp_entry_phys++; + } + + /* + * Bump the phys address of the command's data buffer by the + * entry_len. + */ + paddr += entry_len; + + /* Decrement length accounting for last partial page. */ + if (entry_len > length) + length = 0; + else + length -= entry_len; + } +} + +/* + * mpr_check_pcie_native_sgl - This function is called for PCIe end devices to + * determine if the driver needs to build a native SGL. If so, that native SGL + * is built in the contiguous buffers allocated especially for PCIe SGL + * creation. If the driver will not build a native SGL, return TRUE and a + * normal IEEE SGL will be built. Currently this routine supports NVMe devices + * only. + * + * Returns FALSE (0) if native SGL was built, TRUE (1) if no SGL was built. + */ +static int +mpr_check_pcie_native_sgl(struct mpr_softc *sc, struct mpr_command *cm, + bus_dma_segment_t *segs, int segs_left) +{ + uint32_t i, sge_dwords, length, offset, entry_len; + uint32_t num_entries, buff_len = 0, sges_in_segment; + uint32_t page_mask, page_mask_result, *curr_buff; + uint32_t *ptr_sgl, *ptr_first_sgl, first_page_offset; + uint32_t first_page_data_size, end_residual; + uint64_t *msg_phys; + bus_addr_t paddr; + int build_native_sgl = 0, first_prp_entry; + int prp_size = PRP_ENTRY_SIZE; + Mpi25IeeeSgeChain64_t *main_chain_element = NULL; + struct mpr_prp_page *prp_page_info = NULL; + + mpr_dprint(sc, MPR_TRACE, "%s\n", __func__); + + /* + * Add up the sizes of each segment length to get the total transfer + * size, which will be checked against the Maximum Data Transfer Size. + * If the data transfer length exceeds the MDTS for this device, just + * return 1 so a normal IEEE SGL will be built. F/W will break the I/O + * up into multiple I/O's. [nvme_mdts = 0 means unlimited] + */ + for (i = 0; i < segs_left; i++) + buff_len += htole32(segs[i].ds_len); + if ((cm->cm_targ->MDTS > 0) && (buff_len > cm->cm_targ->MDTS)) + return 1; + + /* Create page_mask (to get offset within page) */ + page_mask = PAGE_SIZE - 1; + + /* + * Check if the number of elements exceeds the max number that can be + * put in the main message frame (H/W can only translate an SGL that + * is contained entirely in the main message frame). + */ + sges_in_segment = (sc->facts->IOCRequestFrameSize - + offsetof(Mpi25SCSIIORequest_t, SGL)) / sizeof(MPI25_SGE_IO_UNION); + if (segs_left > sges_in_segment) + build_native_sgl = 1; + else + { + /* + * NVMe uses one PRP for each physical page (or part of physical + * page). + * if 4 pages or less then IEEE is OK + * if > 5 pages then we need to build a native SGL + * if > 4 and <= 5 pages, then check the physical address of + * the first SG entry, then if this first size in the page + * is >= the residual beyond 4 pages then use IEEE, + * otherwise use native SGL + */ + if (buff_len > (PAGE_SIZE * 5)) + build_native_sgl = 1; + else if ((buff_len > (PAGE_SIZE * 4)) && + (buff_len <= (PAGE_SIZE * 5)) ) + { + msg_phys = (uint64_t *)segs[0].ds_addr; + first_page_offset = + ((uint32_t)(uint64_t)(uintptr_t)msg_phys & + page_mask); + first_page_data_size = PAGE_SIZE - first_page_offset; + end_residual = buff_len % PAGE_SIZE; + + /* + * If offset into first page pushes the end of the data + * beyond end of the 5th page, we need the extra PRP + * list. + */ + if (first_page_data_size < end_residual) + build_native_sgl = 1; + + /* + * Check if first SG entry size is < residual beyond 4 + * pages. + */ + if (htole32(segs[0].ds_len) < + (buff_len - (PAGE_SIZE * 4))) + build_native_sgl = 1; + } + } + + /* check if native SGL is needed */ + if (!build_native_sgl) + return 1; + + /* + * Native SGL is needed. + * Put a chain element in main message frame that points to the first + * chain buffer. + * + * NOTE: The ChainOffset field must be 0 when using a chain pointer to + * a native SGL. + */ + + /* Set main message chain element pointer */ + main_chain_element = (pMpi25IeeeSgeChain64_t)cm->cm_sge; + + /* + * For NVMe the chain element needs to be the 2nd SGL entry in the main + * message. + */ + main_chain_element = (Mpi25IeeeSgeChain64_t *) + ((uint8_t *)main_chain_element + sizeof(MPI25_IEEE_SGE_CHAIN64)); + + /* + * For the PRP entries, use the specially allocated buffer of + * contiguous memory. PRP Page allocation failures should not happen + * because there should be enough PRP page buffers to account for the + * possible NVMe QDepth. + */ + prp_page_info = mpr_alloc_prp_page(sc); + KASSERT(prp_page_info != NULL, ("%s: There are no PRP Pages left to be " + "used for building a native NVMe SGL.\n", __func__)); + curr_buff = (uint32_t *)prp_page_info->prp_page; + msg_phys = (uint64_t *)(uintptr_t)prp_page_info->prp_page_busaddr; + + /* + * Insert the allocated PRP page into the command's PRP page list. This + * will be freed when the command is freed. + */ + TAILQ_INSERT_TAIL(&cm->cm_prp_page_list, prp_page_info, prp_page_link); + + /* + * Check if we are within 1 entry of a page boundary we don't want our + * first entry to be a PRP List entry. + */ + page_mask_result = (uintptr_t)((uint8_t *)curr_buff + prp_size) & + page_mask; + if (!page_mask_result) { + /* Bump up to next page boundary. */ + curr_buff = (uint32_t *)((uint8_t *)curr_buff + prp_size); + msg_phys = (uint64_t *)((uint8_t *)msg_phys + prp_size); + } + + /* Fill in the chain element and make it an NVMe segment type. */ + main_chain_element->Address.High = + htole32((uint32_t)((uint64_t)(uintptr_t)msg_phys >> 32)); + main_chain_element->Address.Low = + htole32((uint32_t)(uintptr_t)msg_phys); + main_chain_element->NextChainOffset = 0; + main_chain_element->Flags = MPI2_IEEE_SGE_FLAGS_CHAIN_ELEMENT | + MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR | + MPI26_IEEE_SGE_FLAGS_NSF_NVME_PRP; + + /* Set SGL pointer to start of contiguous PCIe buffer. */ + ptr_sgl = curr_buff; + sge_dwords = 2; + num_entries = 0; + + /* + * NVMe has a very convoluted PRP format. One PRP is required for each + * page or partial page. We need to split up OS SG entries if they are + * longer than one page or cross a page boundary. We also have to insert + * a PRP list pointer entry as the last entry in each physical page of + * the PRP list. + * + * NOTE: The first PRP "entry" is actually placed in the first SGL entry + * in the main message in IEEE 64 format. The 2nd entry in the main + * message is the chain element, and the rest of the PRP entries are + * built in the contiguous PCIe buffer. + */ + first_prp_entry = 1; + ptr_first_sgl = (uint32_t *)cm->cm_sge; + + for (i = 0; i < segs_left; i++) { + /* Get physical address and length of this SG entry. */ + paddr = segs[i].ds_addr; + length = segs[i].ds_len; + + /* + * Check whether a given SGE buffer lies on a non-PAGED + * boundary if this is not the first page. If so, this is not + * expected so have FW build the SGL. + */ + if (i) { + if ((uint32_t)paddr & page_mask) { + mpr_dprint(sc, MPR_ERROR, "Unaligned SGE while " + "building NVMe PRPs, low address is 0x%x\n", + (uint32_t)paddr); + return 1; + } + } + + /* Apart from last SGE, if any other SGE boundary is not page + * aligned then it means that hole exists. Existence of hole + * leads to data corruption. So fallback to IEEE SGEs. + */ + if (i != (segs_left - 1)) { + if (((uint32_t)paddr + length) & page_mask) { + mpr_dprint(sc, MPR_ERROR, "Unaligned SGE " + "boundary while building NVMe PRPs, low " + "address: 0x%x and length: %u\n", + (uint32_t)paddr, length); + return 1; + } + } + + /* Loop while the length is not zero. */ + while (length) { + /* + * Check if we need to put a list pointer here if we are + * at page boundary - prp_size. + */ + page_mask_result = (uintptr_t)((uint8_t *)ptr_sgl + + prp_size) & page_mask; + if (!page_mask_result) { + /* + * Need to put a PRP list pointer here. + */ + msg_phys = (uint64_t *)((uint8_t *)msg_phys + + prp_size); + *ptr_sgl = htole32((uintptr_t)msg_phys); + *(ptr_sgl+1) = htole32((uint64_t)(uintptr_t) + msg_phys >> 32); + ptr_sgl += sge_dwords; + num_entries++; + } + + /* Need to handle if entry will be part of a page. */ + offset = (uint32_t)paddr & page_mask; + entry_len = PAGE_SIZE - offset; + if (first_prp_entry) { + /* + * Put IEEE entry in first SGE in main message. + * (Simple element, System addr, not end of + * list.) + */ + *ptr_first_sgl = htole32((uint32_t)paddr); + *(ptr_first_sgl + 1) = + htole32((uint32_t)((uint64_t)paddr >> 32)); + *(ptr_first_sgl + 2) = htole32(entry_len); + *(ptr_first_sgl + 3) = 0; + + /* No longer the first PRP entry. */ + first_prp_entry = 0; + } else { + /* Put entry in list. */ + *ptr_sgl = htole32((uint32_t)paddr); + *(ptr_sgl + 1) = + htole32((uint32_t)((uint64_t)paddr >> 32)); + + /* Bump ptr_sgl, msg_phys, and num_entries. */ + ptr_sgl += sge_dwords; + msg_phys = (uint64_t *)((uint8_t *)msg_phys + + prp_size); + num_entries++; + } + + /* Bump the phys address by the entry_len. */ + paddr += entry_len; + + /* Decrement length accounting for last partial page. */ + if (entry_len > length) + length = 0; + else + length -= entry_len; + } + } + + /* Set chain element Length. */ + main_chain_element->Length = htole32(num_entries * prp_size); + + /* Return 0, indicating we built a native SGL. */ + return 0; +} + /* * Add a chain element as the next SGE for the specified command. * Reset cm_sge and cm_sgesize to indicate all the available space. Chains are @@ -2540,6 +3184,13 @@ } else dir = BUS_DMASYNC_PREREAD; + /* Check if a native SG list is needed for an NVMe PCIe device. */ + if (cm->cm_targ && cm->cm_targ->is_nvme && + mpr_check_pcie_native_sgl(sc, cm, segs, nsegs) == 0) { + /* A native SG list was built, skip to end. */ + goto out; + } + for (i = 0; i < nsegs; i++) { if ((cm->cm_flags & MPR_CM_FLAGS_SMP_PASS) && (i != 0)) { sflags &= ~MPI2_SGE_FLAGS_DIRECTION; @@ -2557,6 +3208,7 @@ } } +out: bus_dmamap_sync(sc->buffer_dmat, cm->cm_dmamap, dir); mpr_enqueue_request(sc, cm); Index: mpr_config.c =================================================================== --- mpr_config.c +++ mpr_config.c @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include @@ -91,7 +92,7 @@ request->Action = MPI2_CONFIG_ACTION_PAGE_HEADER; request->Header.PageType = MPI2_CONFIG_PAGETYPE_IOC; request->Header.PageNumber = 8; - request->Header.PageVersion = MPI2_IOCPAGE8_PAGEVERSION; + request->Header.PageLength = request->Header.PageVersion = 0; cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; cm->cm_data = NULL; error = mpr_wait_command(sc, cm, 60, CAN_SLEEP); @@ -137,7 +138,7 @@ request->Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; request->Header.PageType = MPI2_CONFIG_PAGETYPE_IOC; request->Header.PageNumber = 8; - request->Header.PageVersion = MPI2_IOCPAGE8_PAGEVERSION; + request->Header.PageVersion = mpi_reply->Header.PageVersion; request->Header.PageLength = mpi_reply->Header.PageLength; cm->cm_length = le16toh(mpi_reply->Header.PageLength) * 4; cm->cm_sge = &request->PageBufferSGE; @@ -221,7 +222,7 @@ request->Action = MPI2_CONFIG_ACTION_PAGE_HEADER; request->Header.PageType = MPI2_CONFIG_PAGETYPE_IO_UNIT; request->Header.PageNumber = 8; - request->Header.PageVersion = MPI2_IOUNITPAGE8_PAGEVERSION; + request->Header.PageLength = request->Header.PageVersion = 0; cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; cm->cm_data = NULL; error = mpr_wait_command(sc, cm, 60, CAN_SLEEP); @@ -267,7 +268,7 @@ request->Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; request->Header.PageType = MPI2_CONFIG_PAGETYPE_IO_UNIT; request->Header.PageNumber = 8; - request->Header.PageVersion = MPI2_IOUNITPAGE8_PAGEVERSION; + request->Header.PageVersion = mpi_reply->Header.PageVersion; request->Header.PageLength = mpi_reply->Header.PageLength; cm->cm_length = le16toh(mpi_reply->Header.PageLength) * 4; cm->cm_sge = &request->PageBufferSGE; @@ -387,7 +388,7 @@ request->Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; request->ExtPageType = MPI2_CONFIG_EXTPAGETYPE_DRIVER_MAPPING; request->Header.PageNumber = 0; - request->Header.PageVersion = MPI2_DRIVERMAPPING0_PAGEVERSION; + request->ExtPageLength = request->Header.PageVersion = 0; request->PageAddress = sc->max_dpm_entries << MPI2_DPM_PGAD_ENTRY_COUNT_SHIFT; cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; @@ -436,7 +437,7 @@ request->Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; request->ExtPageType = MPI2_CONFIG_EXTPAGETYPE_DRIVER_MAPPING; request->Header.PageNumber = 0; - request->Header.PageVersion = MPI2_DRIVERMAPPING0_PAGEVERSION; + request->Header.PageVersion = mpi_reply->Header.PageVersion; request->PageAddress = sc->max_dpm_entries << MPI2_DPM_PGAD_ENTRY_COUNT_SHIFT; request->ExtPageLength = mpi_reply->ExtPageLength; @@ -522,7 +523,7 @@ request->Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; request->ExtPageType = MPI2_CONFIG_EXTPAGETYPE_DRIVER_MAPPING; request->Header.PageNumber = 0; - request->Header.PageVersion = MPI2_DRIVERMAPPING0_PAGEVERSION; + request->ExtPageLength = request->Header.PageVersion = 0; /* We can remove below two lines ????*/ request->PageAddress = 1 << MPI2_DPM_PGAD_ENTRY_COUNT_SHIFT; request->PageAddress |= htole16(entry_idx); @@ -572,7 +573,7 @@ request->Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; request->ExtPageType = MPI2_CONFIG_EXTPAGETYPE_DRIVER_MAPPING; request->Header.PageNumber = 0; - request->Header.PageVersion = MPI2_DRIVERMAPPING0_PAGEVERSION; + request->Header.PageVersion = mpi_reply->Header.PageVersion; request->ExtPageLength = mpi_reply->ExtPageLength; request->PageAddress = 1 << MPI2_DPM_PGAD_ENTRY_COUNT_SHIFT; request->PageAddress |= htole16(entry_idx); @@ -660,7 +661,7 @@ request->Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; request->ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE; request->Header.PageNumber = 0; - request->Header.PageVersion = MPI2_SASDEVICE0_PAGEVERSION; + request->ExtPageLength = request->Header.PageVersion = 0; cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; cm->cm_data = NULL; error = mpr_wait_command(sc, cm, 60, CAN_SLEEP); @@ -707,7 +708,7 @@ request->Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; request->ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE; request->Header.PageNumber = 0; - request->Header.PageVersion = MPI2_SASDEVICE0_PAGEVERSION; + request->Header.PageVersion = mpi_reply->Header.PageVersion; request->ExtPageLength = mpi_reply->ExtPageLength; request->PageAddress = htole32(form | handle); cm->cm_length = le16toh(mpi_reply->ExtPageLength) * 4; @@ -759,6 +760,276 @@ } /** + * mpr_config_get_pcie_device_pg0 - obtain PCIe device page 0 + * @sc: 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. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpr_config_get_pcie_device_pg0(struct mpr_softc *sc, Mpi2ConfigReply_t + *mpi_reply, Mpi26PCIeDevicePage0_t *config_page, u32 form, u16 handle) +{ + MPI2_CONFIG_REQUEST *request; + MPI2_CONFIG_REPLY *reply; + struct mpr_command *cm; + Mpi26PCIeDevicePage0_t *page = NULL; + int error = 0; + u16 ioc_status; + + mpr_dprint(sc, MPR_TRACE, "%s\n", __func__); + + if ((cm = mpr_alloc_command(sc)) == NULL) { + printf("%s: command alloc failed @ line %d\n", __func__, + __LINE__); + error = EBUSY; + goto out; + } + request = (MPI2_CONFIG_REQUEST *)cm->cm_req; + bzero(request, sizeof(MPI2_CONFIG_REQUEST)); + request->Function = MPI2_FUNCTION_CONFIG; + request->Action = MPI2_CONFIG_ACTION_PAGE_HEADER; + request->Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; + request->ExtPageType = MPI2_CONFIG_EXTPAGETYPE_PCIE_DEVICE; + request->Header.PageNumber = 0; + request->ExtPageLength = request->Header.PageVersion = 0; + cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; + cm->cm_data = NULL; + error = mpr_wait_command(sc, cm, 60, CAN_SLEEP); + reply = (MPI2_CONFIG_REPLY *)cm->cm_reply; + if (error || (reply == NULL)) { + /* FIXME */ + /* + * If the request returns an error then we need to do a diag + * reset + */ + printf("%s: request for header completed with error %d", + __func__, error); + error = ENXIO; + goto out; + } + ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK; + bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY)); + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + /* FIXME */ + /* + * If the request returns an error then we need to do a diag + * reset + */ + printf("%s: header read with error; iocstatus = 0x%x\n", + __func__, ioc_status); + error = ENXIO; + goto out; + } + /* We have to do free and alloc for the reply-free and reply-post + * counters to match - Need to review the reply FIFO handling. + */ + mpr_free_command(sc, cm); + + if ((cm = mpr_alloc_command(sc)) == NULL) { + printf("%s: command alloc failed @ line %d\n", __func__, + __LINE__); + error = EBUSY; + goto out; + } + request = (MPI2_CONFIG_REQUEST *)cm->cm_req; + bzero(request, sizeof(MPI2_CONFIG_REQUEST)); + request->Function = MPI2_FUNCTION_CONFIG; + request->Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; + request->Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; + request->ExtPageType = MPI2_CONFIG_EXTPAGETYPE_PCIE_DEVICE; + request->Header.PageNumber = 0; + request->Header.PageVersion = mpi_reply->Header.PageVersion; + request->ExtPageLength = mpi_reply->ExtPageLength; + request->PageAddress = htole32(form | handle); + cm->cm_length = le16toh(mpi_reply->ExtPageLength) * 4; + cm->cm_sge = &request->PageBufferSGE; + cm->cm_sglsize = sizeof(MPI2_SGE_IO_UNION); + cm->cm_flags = MPR_CM_FLAGS_SGE_SIMPLE | MPR_CM_FLAGS_DATAIN; + cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; + page = malloc(cm->cm_length, M_MPR, M_ZERO | M_NOWAIT); + if (!page) { + printf("%s: page alloc failed\n", __func__); + error = ENOMEM; + goto out; + } + cm->cm_data = page; + + error = mpr_wait_command(sc, cm, 60, CAN_SLEEP); + reply = (MPI2_CONFIG_REPLY *)cm->cm_reply; + if (error || (reply == NULL)) { + /* FIXME */ + /* + * If the request returns an error then we need to do a diag + * reset + */ + printf("%s: request for page completed with error %d", + __func__, error); + error = ENXIO; + goto out; + } + ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK; + bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY)); + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + /* FIXME */ + /* + * If the request returns an error then we need to do a diag + * reset + */ + printf("%s: page read with error; iocstatus = 0x%x\n", + __func__, ioc_status); + error = ENXIO; + goto out; + } + bcopy(page, config_page, MIN(cm->cm_length, + sizeof(Mpi26PCIeDevicePage0_t))); +out: + free(page, M_MPR); + if (cm) + mpr_free_command(sc, cm); + return (error); +} + +/** + * mpr_config_get_pcie_device_pg2 - obtain PCIe device page 2 + * @sc: 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. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpr_config_get_pcie_device_pg2(struct mpr_softc *sc, Mpi2ConfigReply_t + *mpi_reply, Mpi26PCIeDevicePage2_t *config_page, u32 form, u16 handle) +{ + MPI2_CONFIG_REQUEST *request; + MPI2_CONFIG_REPLY *reply; + struct mpr_command *cm; + Mpi26PCIeDevicePage2_t *page = NULL; + int error = 0; + u16 ioc_status; + + mpr_dprint(sc, MPR_TRACE, "%s\n", __func__); + + if ((cm = mpr_alloc_command(sc)) == NULL) { + printf("%s: command alloc failed @ line %d\n", __func__, + __LINE__); + error = EBUSY; + goto out; + } + request = (MPI2_CONFIG_REQUEST *)cm->cm_req; + bzero(request, sizeof(MPI2_CONFIG_REQUEST)); + request->Function = MPI2_FUNCTION_CONFIG; + request->Action = MPI2_CONFIG_ACTION_PAGE_HEADER; + request->Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; + request->ExtPageType = MPI2_CONFIG_EXTPAGETYPE_PCIE_DEVICE; + request->Header.PageNumber = 2; + request->ExtPageLength = request->Header.PageVersion = 0; + cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; + cm->cm_data = NULL; + error = mpr_wait_command(sc, cm, 60, CAN_SLEEP); + reply = (MPI2_CONFIG_REPLY *)cm->cm_reply; + if (error || (reply == NULL)) { + /* FIXME */ + /* + * If the request returns an error then we need to do a diag + * reset + */ + printf("%s: request for header completed with error %d", + __func__, error); + error = ENXIO; + goto out; + } + ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK; + bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY)); + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + /* FIXME */ + /* + * If the request returns an error then we need to do a diag + * reset + */ + printf("%s: header read with error; iocstatus = 0x%x\n", + __func__, ioc_status); + error = ENXIO; + goto out; + } + /* We have to do free and alloc for the reply-free and reply-post + * counters to match - Need to review the reply FIFO handling. + */ + mpr_free_command(sc, cm); + + if ((cm = mpr_alloc_command(sc)) == NULL) { + printf("%s: command alloc failed @ line %d\n", __func__, + __LINE__); + error = EBUSY; + goto out; + } + request = (MPI2_CONFIG_REQUEST *)cm->cm_req; + bzero(request, sizeof(MPI2_CONFIG_REQUEST)); + request->Function = MPI2_FUNCTION_CONFIG; + request->Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; + request->Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; + request->ExtPageType = MPI2_CONFIG_EXTPAGETYPE_PCIE_DEVICE; + request->Header.PageNumber = 2; + request->Header.PageVersion = mpi_reply->Header.PageVersion; + request->ExtPageLength = mpi_reply->ExtPageLength; + request->PageAddress = htole32(form | handle); + cm->cm_length = le16toh(mpi_reply->ExtPageLength) * 4; + cm->cm_sge = &request->PageBufferSGE; + cm->cm_sglsize = sizeof(MPI2_SGE_IO_UNION); + cm->cm_flags = MPR_CM_FLAGS_SGE_SIMPLE | MPR_CM_FLAGS_DATAIN; + cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; + page = malloc(cm->cm_length, M_MPR, M_ZERO | M_NOWAIT); + if (!page) { + printf("%s: page alloc failed\n", __func__); + error = ENOMEM; + goto out; + } + cm->cm_data = page; + + error = mpr_wait_command(sc, cm, 60, CAN_SLEEP); + reply = (MPI2_CONFIG_REPLY *)cm->cm_reply; + if (error || (reply == NULL)) { + /* FIXME */ + /* + * If the request returns an error then we need to do a diag + * reset + */ + printf("%s: request for page completed with error %d", + __func__, error); + error = ENXIO; + goto out; + } + ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK; + bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY)); + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + /* FIXME */ + /* + * If the request returns an error then we need to do a diag + * reset + */ + printf("%s: page read with error; iocstatus = 0x%x\n", + __func__, ioc_status); + error = ENXIO; + goto out; + } + bcopy(page, config_page, MIN(cm->cm_length, + sizeof(Mpi26PCIeDevicePage2_t))); +out: + free(page, M_MPR); + if (cm) + mpr_free_command(sc, cm); + return (error); +} + +/** * mpr_config_get_bios_pg3 - obtain BIOS page 3 * @sc: per adapter object * @mpi_reply: reply mf payload returned from firmware @@ -792,7 +1063,7 @@ request->Action = MPI2_CONFIG_ACTION_PAGE_HEADER; request->Header.PageType = MPI2_CONFIG_PAGETYPE_BIOS; request->Header.PageNumber = 3; - request->Header.PageVersion = MPI2_BIOSPAGE3_PAGEVERSION; + request->Header.PageLength = request->Header.PageVersion = 0; cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; cm->cm_data = NULL; error = mpr_wait_command(sc, cm, 60, CAN_SLEEP); @@ -838,7 +1109,7 @@ request->Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; request->Header.PageType = MPI2_CONFIG_PAGETYPE_BIOS; request->Header.PageNumber = 3; - request->Header.PageVersion = MPI2_BIOSPAGE3_PAGEVERSION; + request->Header.PageVersion = mpi_reply->Header.PageVersion; request->Header.PageLength = mpi_reply->Header.PageLength; cm->cm_length = le16toh(mpi_reply->Header.PageLength) * 4; cm->cm_sge = &request->PageBufferSGE; @@ -922,7 +1193,7 @@ request->Action = MPI2_CONFIG_ACTION_PAGE_HEADER; request->Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_VOLUME; request->Header.PageNumber = 0; - request->Header.PageVersion = MPI2_RAIDVOLPAGE0_PAGEVERSION; + request->Header.PageLength = request->Header.PageVersion = 0; cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; cm->cm_data = NULL; @@ -1051,7 +1322,7 @@ request->Action = MPI2_CONFIG_ACTION_PAGE_HEADER; request->Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_VOLUME; request->Header.PageNumber = 1; - request->Header.PageVersion = MPI2_RAIDVOLPAGE1_PAGEVERSION; + request->Header.PageLength = request->Header.PageVersion = 0; cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; cm->cm_data = NULL; error = mpr_wait_command(sc, cm, 60, CAN_SLEEP); @@ -1208,7 +1479,7 @@ request->Action = MPI2_CONFIG_ACTION_PAGE_HEADER; request->Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_PHYSDISK; request->Header.PageNumber = 0; - request->Header.PageVersion = MPI2_RAIDPHYSDISKPAGE0_PAGEVERSION; + request->Header.PageLength = request->Header.PageVersion = 0; cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; cm->cm_data = NULL; Index: mpr_mapping.h =================================================================== --- mpr_mapping.h +++ mpr_mapping.h @@ -53,9 +53,36 @@ }; /** - * struct _map_topology_change - entries to be removed from mapping table - * @dpm_entry_num: index of this device in device persistent map table + * struct _map_port_change - PCIe Port entries received in PCIe Topology change + * list event + * @physical_id: WWID of the device attached to the associated port + * @device_info: bitfield provides detailed info about the device + * @MDTS: Maximum Data Transfer Size for the device * @dev_handle: device handle for the device pointed by this entry + * @slot: slot ID + * @is_processed: Flag to indicate whether this entry is processed or not + */ +struct _map_port_change { + uint64_t physical_id; + uint32_t device_info; + uint32_t MDTS; + uint16_t dev_handle; + uint16_t slot; + uint8_t reason; + uint8_t is_processed; + uint8_t reserved[2]; +}; + +/** + * struct _map_topology_change - SAS/SATA entries to be removed from mapping + * table + * @enc_handle: enclosure handle where this device is located + * @exp_handle: expander handle where this device is located + * @num_entries: number of entries in the SAS Topology Change List event + * @start_phy_num: PHY number of the first PHY in the event data + * @num_phys: number of PHYs in the expander where this device is located + * @exp_status: status for the expander where this device is located + * @phy_details: more details about each PHY in the event data */ struct _map_topology_change { uint16_t enc_handle; @@ -67,6 +94,26 @@ struct _map_phy_change *phy_details; }; +/** + * struct _map_pcie_topology_change - PCIe entries to be removed from mapping + * table + * @enc_handle: enclosure handle where this device is located + * @switch_dev_handle: PCIe switch device handle where this device is located + * @num_entries: number of entries in the PCIe Topology Change List event + * @start_port_num: port number of the first port in the event data + * @num_ports: number of ports in the PCIe switch device + * @switch_status: status for the PCIe switch where this device is located + * @port_details: more details about each Port in the event data + */ +struct _map_pcie_topology_change { + uint16_t enc_handle; + uint16_t switch_dev_handle; + uint8_t num_entries; + uint8_t start_port_num; + uint8_t num_ports; + uint8_t switch_status; + struct _map_port_change *port_details; +}; extern int mprsas_get_sas_address_for_sata_disk(struct mpr_softc *ioc, Index: mpr_mapping.c =================================================================== --- mpr_mapping.c +++ mpr_mapping.c @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include @@ -676,6 +677,55 @@ } /** + * _mapping_inc_missing_count + * @sc: per adapter object + * @map_idx: index into the mapping table for the device that is missing + * + * Increment the missing count in the mapping table for a SAS, SATA, or PCIe + * device that is not responding. If Persitent Mapping is used, increment the + * DPM entry as well. Also, add this device to the removal table for possible + * removal if a new device is added. + * + * Returns nothing. + */ +static void +_mapping_inc_missing_count(struct mpr_softc *sc, u32 map_idx) +{ + u16 ioc_pg8_flags = le16toh(sc->ioc_pg8.Flags); + struct dev_mapping_table *mt_entry; + Mpi2DriverMap0Entry_t *dpm_entry; + + if (map_idx == MPR_MAPTABLE_BAD_IDX) { + mpr_dprint(sc, MPR_INFO, "%s: device is already removed from " + "mapping table\n", __func__); + return; + } + mt_entry = &sc->mapping_table[map_idx]; + if (!mt_entry->init_complete) { + if (mt_entry->missing_count < MPR_MAX_MISSING_COUNT) + mt_entry->missing_count++; + else + mt_entry->init_complete = 1; + } + if (!mt_entry->missing_count) + mt_entry->missing_count++; + _mapping_add_to_removal_table(sc, mt_entry->dev_handle, 0); + mt_entry->dev_handle = 0; + + if (((ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) == + MPI2_IOCPAGE8_FLAGS_DEVICE_PERSISTENCE_MAPPING) && + sc->is_dpm_enable && !mt_entry->init_complete && + mt_entry->dpm_entry_num != MPR_DPM_BAD_IDX) { + dpm_entry = (Mpi2DriverMap0Entry_t *) ((u8 *)sc->dpm_pg0 + + sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER)); + dpm_entry += mt_entry->dpm_entry_num; + dpm_entry->MappingInformation = mt_entry->missing_count; + sc->dpm_flush_entry[mt_entry->dpm_entry_num] = 1; + } + mt_entry->init_complete = 1; +} + +/** * _mapping_update_missing_count - Update missing count for a device * @sc: per adapter object * @topo_change: Topology change event entry @@ -689,12 +739,9 @@ _mapping_update_missing_count(struct mpr_softc *sc, struct _map_topology_change *topo_change) { - u16 ioc_pg8_flags = le16toh(sc->ioc_pg8.Flags); u8 entry; struct _map_phy_change *phy_change; u32 map_idx; - struct dev_mapping_table *mt_entry; - Mpi2DriverMap0Entry_t *dpm_entry; for (entry = 0; entry < topo_change->num_entries; entry++) { phy_change = &topo_change->phy_details[entry]; @@ -704,35 +751,37 @@ map_idx = _mapping_get_mt_idx_from_handle(sc, phy_change-> dev_handle); phy_change->is_processed = 1; - if (map_idx == MPR_MAPTABLE_BAD_IDX) { - printf("%s: device is already removed from mapping " - "table\n", __func__); - continue; - } - mt_entry = &sc->mapping_table[map_idx]; - if (!mt_entry->init_complete) { - if (mt_entry->missing_count < MPR_MAX_MISSING_COUNT) - mt_entry->missing_count++; - else - mt_entry->init_complete = 1; - } - if (!mt_entry->missing_count) - mt_entry->missing_count++; - _mapping_add_to_removal_table(sc, mt_entry->dev_handle, 0); - mt_entry->dev_handle = 0; + _mapping_inc_missing_count(sc, map_idx); + } +} - if (((ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) == - MPI2_IOCPAGE8_FLAGS_DEVICE_PERSISTENCE_MAPPING) && - sc->is_dpm_enable && !mt_entry->init_complete && - mt_entry->dpm_entry_num != MPR_DPM_BAD_IDX) { - dpm_entry = - (Mpi2DriverMap0Entry_t *) ((u8 *)sc->dpm_pg0 + - sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER)); - dpm_entry += mt_entry->dpm_entry_num; - dpm_entry->MappingInformation = mt_entry->missing_count; - sc->dpm_flush_entry[mt_entry->dpm_entry_num] = 1; - } - mt_entry->init_complete = 1; +/** + * _mapping_update_pcie_missing_count - Update missing count for a PCIe device + * @sc: per adapter object + * @topo_change: Topology change event entry + * + * Search through the PCIe topology change list and if any device is found not + * responding it's associated map table entry and DPM entry is updated + * + * Returns nothing. + */ +static void +_mapping_update_pcie_missing_count(struct mpr_softc *sc, + struct _map_pcie_topology_change *topo_change) +{ + u8 entry; + struct _map_port_change *port_change; + u32 map_idx; + + for (entry = 0; entry < topo_change->num_entries; entry++) { + port_change = &topo_change->port_details[entry]; + if (!port_change->dev_handle || (port_change->reason != + MPI26_EVENT_PCIE_TOPO_PS_NOT_RESPONDING)) + continue; + map_idx = _mapping_get_mt_idx_from_handle(sc, port_change-> + dev_handle); + port_change->is_processed = 1; + _mapping_inc_missing_count(sc, map_idx); } } @@ -940,7 +989,7 @@ phy_change->physical_id = sas_address; phy_change->slot = le16toh(sas_device_pg0.Slot); - phy_change->device_info = le32toh(sas_device_pg0.DeviceInfo); + phy_change->device_info = device_info; if ((ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) == MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING) { @@ -990,12 +1039,118 @@ break; } } + + /* Found space in enclosure for mapping entry */ mt_entry = &sc->mapping_table[map_idx]; for (index = map_idx; index < (et_entry->num_slots + map_idx); index++, mt_entry++) { mt_entry->device_info = MPR_DEV_RESERVED; mt_entry->physical_id = et_entry->enclosure_id; mt_entry->phy_bits = et_entry->phy_bits; + mt_entry->missing_count = 0; + } + } + } +} + +/** + * _mapping_get_pcie_dev_info -get information about newly added PCIe devices + * @sc: per adapter object + * @topo_change: Topology change event entry + * + * Searches through the PCIe topology change event list and issues PCIe device + * pg0 requests for the newly added PCIe device. If the device is in an + * enclosure, search for available space in the enclosure mapping table for the + * device and reserve that space. + * + * Returns nothing + */ +static void +_mapping_get_pcie_dev_info(struct mpr_softc *sc, + struct _map_pcie_topology_change *topo_change) +{ + u16 ioc_pg8_flags = le16toh(sc->ioc_pg8.Flags); + Mpi2ConfigReply_t mpi_reply; + Mpi26PCIeDevicePage0_t pcie_device_pg0; + u8 entry, enc_idx, port_idx; + u32 map_idx, index; + struct _map_port_change *port_change, *tmp_port_change; + uint64_t pcie_wwid; + struct enc_mapping_table *et_entry; + struct dev_mapping_table *mt_entry; + u8 add_code = MPI26_EVENT_PCIE_TOPO_PS_DEV_ADDED; + + for (entry = 0; entry < topo_change->num_entries; entry++) { + port_change = &topo_change->port_details[entry]; + if (port_change->is_processed || !port_change->dev_handle || + port_change->reason != MPI26_EVENT_PCIE_TOPO_PS_DEV_ADDED) + continue; + if (mpr_config_get_pcie_device_pg0(sc, &mpi_reply, + &pcie_device_pg0, MPI26_PCIE_DEVICE_PGAD_FORM_HANDLE, + port_change->dev_handle)) { + port_change->is_processed = 1; + continue; + } + + pcie_wwid = pcie_device_pg0.WWID.High; + pcie_wwid = (pcie_wwid << 32) | pcie_device_pg0.WWID.Low; + port_change->physical_id = pcie_wwid; + port_change->slot = le16toh(pcie_device_pg0.Slot); + port_change->device_info = le32toh(pcie_device_pg0.DeviceInfo); + + if ((ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) == + MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING) { + enc_idx = _mapping_get_enc_idx_from_handle(sc, + topo_change->enc_handle); + if (enc_idx == MPR_ENCTABLE_BAD_IDX) { + port_change->is_processed = 1; + mpr_dprint(sc, MPR_MAPPING, "%s: failed to add " + "the device with handle 0x%04x because the " + "enclosure is not in the mapping table\n", + __func__, port_change->dev_handle); + continue; + } + if (!(port_change->device_info & + MPI26_PCIE_DEVINFO_NVME)) { + port_change->is_processed = 1; + continue; + } + et_entry = &sc->enclosure_table[enc_idx]; + if (et_entry->start_index != MPR_MAPTABLE_BAD_IDX) + continue; + if (!topo_change->switch_dev_handle) { + map_idx = sc->num_rsvd_entries; + et_entry->start_index = map_idx; + } else { + map_idx = _mapping_find_enc_map_space(sc, + et_entry); + et_entry->start_index = map_idx; + if (et_entry->start_index == + MPR_MAPTABLE_BAD_IDX) { + port_change->is_processed = 1; + for (port_idx = 0; port_idx < + topo_change->num_entries; + port_idx++) { + tmp_port_change = + &topo_change->port_details + [port_idx]; + if (tmp_port_change->reason == + add_code) + tmp_port_change-> + is_processed = 1; + } + break; + } + } + + /* Found space in enclosure for mapping entry */ + mt_entry = &sc->mapping_table[map_idx]; + for (index = map_idx; index < (et_entry->num_slots + + map_idx); index++, mt_entry++) { + mt_entry->device_info = MPR_DEV_RESERVED; + mt_entry->physical_id = et_entry->enclosure_id; + mt_entry->phy_bits = et_entry->phy_bits; + mt_entry->missing_count = 0; } } } @@ -1106,8 +1261,8 @@ * @sc: per adapter object * @topo_change: Topology change event entry * - * Search through the topology change event list and updates map table, - * enclosure table and DPM pages for for the newly added devices. + * Search through the topology change event list and update map table, + * enclosure table and DPM pages for the newly added devices. * * Returns nothing */ @@ -1144,10 +1299,10 @@ (sc, topo_change->enc_handle); if (enc_idx == MPR_ENCTABLE_BAD_IDX) { phy_change->is_processed = 1; - printf("%s: failed to add the device with " - "handle 0x%04x because the enclosure is " - "not in the mapping table\n", __func__, - phy_change->dev_handle); + mpr_dprint(sc, MPR_ERROR, "%s: failed to add " + "the device with handle 0x%04x because the " + "enclosure is not in the mapping table\n", + __func__, phy_change->dev_handle); continue; } et_entry = &sc->enclosure_table[enc_idx]; @@ -1157,10 +1312,11 @@ sc->mt_add_device_failed = 1; continue; } - printf("%s: failed to add the device with " - "handle 0x%04x because there is no free " - "space available in the mapping table\n", - __func__, phy_change->dev_handle); + mpr_dprint(sc, MPR_INFO, "%s: failed to add " + "the device with handle 0x%04x because " + "there is no free space available in the " + "mapping table\n", __func__, + phy_change->dev_handle); continue; } map_idx = et_entry->start_index + phy_change->slot - @@ -1268,10 +1424,11 @@ sc->mt_add_device_failed = 1; continue; } - printf("%s: failed to add the device with " - "handle 0x%04x because there is no free " - "space available in the mapping table\n", - __func__, phy_change->dev_handle); + mpr_dprint(sc, MPR_INFO, "%s: failed to add " + "the device with handle 0x%04x because " + "there is no free space available in the " + "mapping table\n", __func__, + phy_change->dev_handle); continue; } if (sc->is_dpm_enable) { @@ -1314,20 +1471,254 @@ sc->dpm_flush_entry[dpm_idx] = 1; phy_change->is_processed = 1; } else if (dpm_idx == MPR_DPM_BAD_IDX) { - phy_change->is_processed = 1; + phy_change->is_processed = 1; + mpr_dprint(sc, MPR_INFO, "%s: failed " + "to add the device with handle " + "0x%04x to persistent table " + "because there is no free space " + "available\n", __func__, + phy_change->dev_handle); + } + } + mt_entry->init_complete = 1; + } + + phy_change->is_processed = 1; + } + if (is_removed) + _mapping_clear_removed_entries(sc); +} + +/** + * _mapping_add_new_pcie_device -Add the new PCIe device into mapping table + * @sc: per adapter object + * @topo_change: Topology change event entry + * + * Search through the PCIe topology change event list and update map table, + * enclosure table and DPM pages for the newly added devices. + * + * Returns nothing + */ +static void +_mapping_add_new_pcie_device(struct mpr_softc *sc, + struct _map_pcie_topology_change *topo_change) +{ + u8 enc_idx, missing_cnt, is_removed = 0; + u16 dpm_idx; + u32 search_idx, map_idx; + u32 entry; + struct dev_mapping_table *mt_entry; + struct enc_mapping_table *et_entry; + struct _map_port_change *port_change; + u16 ioc_pg8_flags = le16toh(sc->ioc_pg8.Flags); + Mpi2DriverMap0Entry_t *dpm_entry; + uint64_t temp64_var; + u8 map_shift = MPI2_DRVMAP0_MAPINFO_SLOT_SHIFT; + u8 hdr_sz = sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER); + u16 max_num_phy_ids = le16toh(sc->ioc_pg8.MaxNumPhysicalMappedIDs); + + for (entry = 0; entry < topo_change->num_entries; entry++) { + port_change = &topo_change->port_details[entry]; + if (port_change->is_processed) + continue; + if (port_change->reason != MPI26_EVENT_PCIE_TOPO_PS_DEV_ADDED || + !port_change->dev_handle) { + port_change->is_processed = 1; + continue; + } + if ((ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) == + MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING) { + enc_idx = _mapping_get_enc_idx_from_handle + (sc, topo_change->enc_handle); + if (enc_idx == MPR_ENCTABLE_BAD_IDX) { + port_change->is_processed = 1; + mpr_dprint(sc, MPR_ERROR, "%s: failed to add " + "the device with handle 0x%04x because the " + "enclosure is not in the mapping table\n", + __func__, port_change->dev_handle); + continue; + } + et_entry = &sc->enclosure_table[enc_idx]; + if (et_entry->start_index == MPR_MAPTABLE_BAD_IDX) { + port_change->is_processed = 1; + if (!sc->mt_full_retry) { + sc->mt_add_device_failed = 1; + continue; + } + mpr_dprint(sc, MPR_INFO, "%s: failed to add " + "the device with handle 0x%04x because " + "there is no free space available in the " + "mapping table\n", __func__, + port_change->dev_handle); + continue; + } + map_idx = et_entry->start_index + port_change->slot - + et_entry->start_slot; + mt_entry = &sc->mapping_table[map_idx]; + mt_entry->physical_id = port_change->physical_id; + mt_entry->channel = 0; + mt_entry->id = map_idx; + mt_entry->dev_handle = port_change->dev_handle; + mt_entry->missing_count = 0; + mt_entry->dpm_entry_num = et_entry->dpm_entry_num; + mt_entry->device_info = port_change->device_info | + (MPR_DEV_RESERVED | MPR_MAP_IN_USE); + if (sc->is_dpm_enable) { + dpm_idx = et_entry->dpm_entry_num; + if (dpm_idx == MPR_DPM_BAD_IDX) + dpm_idx = _mapping_get_dpm_idx_from_id + (sc, et_entry->enclosure_id, + et_entry->phy_bits); + if (dpm_idx == MPR_DPM_BAD_IDX) { + dpm_idx = _mapping_get_free_dpm_idx(sc); + if (dpm_idx != MPR_DPM_BAD_IDX) { + dpm_entry = + (Mpi2DriverMap0Entry_t *) + ((u8 *) sc->dpm_pg0 + + hdr_sz); + dpm_entry += dpm_idx; + dpm_entry-> + PhysicalIdentifier.Low = + (0xFFFFFFFF & + et_entry->enclosure_id); + dpm_entry-> + PhysicalIdentifier.High = + ( et_entry->enclosure_id + >> 32); + dpm_entry->DeviceIndex = + (U16)et_entry->start_index; + dpm_entry->MappingInformation = + et_entry->num_slots; + dpm_entry->MappingInformation + <<= map_shift; + dpm_entry->PhysicalBitsMapping + = et_entry->phy_bits; + et_entry->dpm_entry_num = + dpm_idx; + /* FIXME Do I need to set the dpm_idxin mt_entry too */ + sc->dpm_entry_used[dpm_idx] = 1; + sc->dpm_flush_entry[dpm_idx] = + 1; + port_change->is_processed = 1; + } else { + port_change->is_processed = 1; mpr_dprint(sc, MPR_INFO, "%s: " "failed to add the device " "with handle 0x%04x to " "persistent table because " "there is no free space " "available\n", __func__, - phy_change->dev_handle); + port_change->dev_handle); + } + } else { + et_entry->dpm_entry_num = dpm_idx; + mt_entry->dpm_entry_num = dpm_idx; + } + } + /* FIXME Why not mt_entry too? */ + et_entry->init_complete = 1; + } else if ((ioc_pg8_flags & + MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) == + MPI2_IOCPAGE8_FLAGS_DEVICE_PERSISTENCE_MAPPING) { + map_idx = _mapping_get_mt_idx_from_id + (sc, port_change->physical_id); + if (map_idx == MPR_MAPTABLE_BAD_IDX) { + search_idx = sc->num_rsvd_entries; + if (topo_change->switch_dev_handle) + search_idx += max_num_phy_ids; + map_idx = _mapping_get_free_mt_idx(sc, + search_idx); + } + if (map_idx == MPR_MAPTABLE_BAD_IDX) { + map_idx = _mapping_get_high_missing_mt_idx(sc); + if (map_idx != MPR_MAPTABLE_BAD_IDX) { + mt_entry = &sc->mapping_table[map_idx]; + if (mt_entry->dev_handle) { + _mapping_add_to_removal_table + (sc, mt_entry->dev_handle, + 0); + is_removed = 1; + } + mt_entry->init_complete = 0; + } + } + if (map_idx != MPR_MAPTABLE_BAD_IDX) { + mt_entry = &sc->mapping_table[map_idx]; + mt_entry->physical_id = + port_change->physical_id; + mt_entry->channel = 0; + mt_entry->id = map_idx; + mt_entry->dev_handle = port_change->dev_handle; + mt_entry->missing_count = 0; + mt_entry->device_info = + port_change->device_info | + (MPR_DEV_RESERVED | MPR_MAP_IN_USE); + } else { + port_change->is_processed = 1; + if (!sc->mt_full_retry) { + sc->mt_add_device_failed = 1; + continue; + } + mpr_dprint(sc, MPR_INFO, "%s: failed to add " + "the device with handle 0x%04x because " + "there is no free space available in the " + "mapping table\n", __func__, + port_change->dev_handle); + continue; + } + if (sc->is_dpm_enable) { + if (mt_entry->dpm_entry_num != + MPR_DPM_BAD_IDX) { + dpm_idx = mt_entry->dpm_entry_num; + dpm_entry = (Mpi2DriverMap0Entry_t *) + ((u8 *)sc->dpm_pg0 + hdr_sz); + dpm_entry += dpm_idx; + missing_cnt = dpm_entry-> + MappingInformation & + MPI2_DRVMAP0_MAPINFO_MISSING_MASK; + temp64_var = dpm_entry-> + PhysicalIdentifier.High; + temp64_var = (temp64_var << 32) | + dpm_entry->PhysicalIdentifier.Low; + if ((mt_entry->physical_id == + temp64_var) && !missing_cnt) + mt_entry->init_complete = 1; + } else { + dpm_idx = _mapping_get_free_dpm_idx(sc); + mt_entry->init_complete = 0; + } + if (dpm_idx != MPR_DPM_BAD_IDX && + !mt_entry->init_complete) { + mt_entry->init_complete = 1; + mt_entry->dpm_entry_num = dpm_idx; + dpm_entry = (Mpi2DriverMap0Entry_t *) + ((u8 *)sc->dpm_pg0 + hdr_sz); + dpm_entry += dpm_idx; + dpm_entry->PhysicalIdentifier.Low = + (0xFFFFFFFF & + mt_entry->physical_id); + dpm_entry->PhysicalIdentifier.High = + (mt_entry->physical_id >> 32); + dpm_entry->DeviceIndex = (U16) map_idx; + dpm_entry->MappingInformation = 0; + dpm_entry->PhysicalBitsMapping = 0; + sc->dpm_entry_used[dpm_idx] = 1; + sc->dpm_flush_entry[dpm_idx] = 1; + port_change->is_processed = 1; + } else if (dpm_idx == MPR_DPM_BAD_IDX) { + port_change->is_processed = 1; + mpr_dprint(sc, MPR_INFO, "%s: failed " + "to add the device with handle " + "0x%04x to persistent table " + "because there is no free space " + "available\n", __func__, + port_change->dev_handle); } } mt_entry->init_complete = 1; } - phy_change->is_processed = 1; + port_change->is_processed = 1; } if (is_removed) _mapping_clear_removed_entries(sc); @@ -2073,6 +2464,56 @@ } /** + * mpr_mapping_pcie_topology_change_event - handle PCIe topology change events + * @sc: per adapter object + * @event_data: event data payload + * + * Returns nothing. + */ +void +mpr_mapping_pcie_topology_change_event(struct mpr_softc *sc, + Mpi26EventDataPCIeTopologyChangeList_t *event_data) +{ + struct _map_pcie_topology_change topo_change; + struct _map_port_change *port_change; + Mpi26EventPCIeTopoPortEntry_t *event_port_change; + u8 i, num_entries; + + topo_change.switch_dev_handle = le16toh(event_data->SwitchDevHandle); + topo_change.enc_handle = le16toh(event_data->EnclosureHandle); + num_entries = event_data->NumEntries; + topo_change.num_entries = num_entries; + topo_change.start_port_num = event_data->StartPortNum; + topo_change.num_ports = event_data->NumPorts; + topo_change.switch_status = event_data->SwitchStatus; + event_port_change = event_data->PortEntry; + topo_change.port_details = NULL; + + if (!num_entries) + goto out; + port_change = malloc(sizeof(struct _map_port_change) * num_entries, + M_MPR, M_NOWAIT|M_ZERO); + topo_change.port_details = port_change; + if (!port_change) + goto out; + for (i = 0; i < num_entries; i++, event_port_change++, port_change++) { + port_change->dev_handle = le16toh(event_port_change-> + AttachedDevHandle); + port_change->reason = event_port_change->PortStatus; + } + _mapping_update_pcie_missing_count(sc, &topo_change); + _mapping_get_pcie_dev_info(sc, &topo_change); + _mapping_clear_removed_entries(sc); + _mapping_add_new_pcie_device(sc, &topo_change); + +out: + free(topo_change.port_details, M_MPR); + _mapping_flush_dpm_pages(sc); + if (sc->pending_map_events) + sc->pending_map_events--; +} + +/** * _mapping_check_update_ir_mt_idx - Check and update IR map table index * @sc: per adapter object * @event_data: event data payload Index: mpr_pci.c =================================================================== --- mpr_pci.c +++ mpr_pci.c @@ -54,6 +54,7 @@ #include #include #include +#include #include #include @@ -110,6 +111,10 @@ 0xffff, 0xffff, 0, "Avago Technologies (LSI) SAS3108_5" }, { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3108_6, 0xffff, 0xffff, 0, "Avago Technologies (LSI) SAS3108_6" }, + { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3216, + 0xffff, 0xffff, 0, "Avago Technologies (LSI) SAS3216" }, + { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3224, + 0xffff, 0xffff, 0, "Avago Technologies (LSI) SAS3224" }, { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3316_1, 0xffff, 0xffff, 0, "Avago Technologies (LSI) SAS3316_1" }, { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3316_2, @@ -118,10 +123,24 @@ 0xffff, 0xffff, 0, "Avago Technologies (LSI) SAS3324_1" }, { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3324_2, 0xffff, 0xffff, 0, "Avago Technologies (LSI) SAS3324_2" }, - { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3216, - 0xffff, 0xffff, 0, "Avago Technologies (LSI) SAS3216" }, - { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3224, - 0xffff, 0xffff, 0, "Avago Technologies (LSI) SAS3224" }, + { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3408, + 0xffff, 0xffff, 0, "Avago Technologies (LSI) SAS3408" }, + { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3416, + 0xffff, 0xffff, 0, "Avago Technologies (LSI) SAS3416" }, + { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3508, + 0xffff, 0xffff, 0, "Avago Technologies (LSI) SAS3508" }, + { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3508_1, + 0xffff, 0xffff, 0, "Avago Technologies (LSI) SAS3508_1" }, + { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3516, + 0xffff, 0xffff, 0, "Avago Technologies (LSI) SAS3516" }, + { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3516_1, + 0xffff, 0xffff, 0, "Avago Technologies (LSI) SAS3516_1" }, + { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3616, + 0xffff, 0xffff, 0, "Avago Technologies (LSI) SAS3616" }, + { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3708, + 0xffff, 0xffff, 0, "Avago Technologies (LSI) SAS3708" }, + { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3716, + 0xffff, 0xffff, 0, "Avago Technologies (LSI) SAS3716" }, { 0, 0, 0, 0, 0, NULL } }; @@ -164,7 +183,7 @@ { struct mpr_softc *sc; struct mpr_ident *m; - int error; + int error, i; sc = device_get_softc(dev); bzero(sc, sizeof(*sc)); @@ -175,13 +194,32 @@ /* Twiddle basic PCI config bits for a sanity check */ pci_enable_busmaster(dev); - /* Allocate the System Interface Register Set */ - sc->mpr_regs_rid = PCIR_BAR(1); - if ((sc->mpr_regs_resource = bus_alloc_resource_any(dev, - SYS_RES_MEMORY, &sc->mpr_regs_rid, RF_ACTIVE)) == NULL) { + /* Set flag if this is a Gen3.5 IOC */ + if ((m->device == MPI26_MFGPAGE_DEVID_SAS3508) || + (m->device == MPI26_MFGPAGE_DEVID_SAS3508_1) || + (m->device == MPI26_MFGPAGE_DEVID_SAS3408) || + (m->device == MPI26_MFGPAGE_DEVID_SAS3516) || + (m->device == MPI26_MFGPAGE_DEVID_SAS3516_1) || + (m->device == MPI26_MFGPAGE_DEVID_SAS3416) || + (m->device == MPI26_MFGPAGE_DEVID_SAS3716) || + (m->device == MPI26_MFGPAGE_DEVID_SAS3616) || + (m->device == MPI26_MFGPAGE_DEVID_SAS3708)) { + sc->mpr_flags |= MPR_FLAGS_GEN35_IOC; + } + + for (i = 0; i < PCI_MAXMAPS_0; i++) { + sc->mpr_regs_rid = PCIR_BAR(i); + + if ((sc->mpr_regs_resource = bus_alloc_resource_any(dev, + SYS_RES_MEMORY, &sc->mpr_regs_rid, RF_ACTIVE)) != NULL) + break; + } + + if (sc->mpr_regs_resource == NULL) { mpr_printf(sc, "Cannot allocate PCI registers\n"); return (ENXIO); } + sc->mpr_btag = rman_get_bustag(sc->mpr_regs_resource); sc->mpr_bhandle = rman_get_bushandle(sc->mpr_regs_resource); Index: mpr_sas.h =================================================================== --- mpr_sas.h +++ mpr_sas.h @@ -83,6 +83,8 @@ uint8_t scsi_req_desc_type; uint8_t stop_at_shutdown; uint8_t supports_SSU; + uint8_t is_nvme; + uint32_t MDTS; }; struct mprsas_softc { Index: mpr_sas.c =================================================================== --- mpr_sas.c +++ mpr_sas.c @@ -72,10 +72,13 @@ #include #endif +#include + #include #include #include #include +#include #include #include #include @@ -477,13 +480,13 @@ } /* - * The MPT3 firmware performs debounce on the link to avoid transient link - * errors and false removals. When it does decide that link has been lost - * and a device needs to go away, it expects that the host will perform a - * target reset and then an op remove. The reset has the side-effect of - * aborting any outstanding requests for the device, which is required for - * the op-remove to succeed. It's not clear if the host should check for - * the device coming back alive after the reset. + * The firmware performs debounce on the link to avoid transient link errors + * and false removals. When it does decide that link has been lost and a + * device needs to go away, it expects that the host will perform a target reset + * and then an op remove. The reset has the side-effect of aborting any + * outstanding requests for the device, which is required for the op-remove to + * succeed. It's not clear if the host should check for the device coming back + * alive after the reset. */ void mprsas_prepare_remove(struct mprsas_softc *sassc, uint16_t handle) @@ -705,7 +708,14 @@ setbit(events, MPI2_EVENT_IR_PHYSICAL_DISK); setbit(events, MPI2_EVENT_IR_OPERATION_STATUS); setbit(events, MPI2_EVENT_TEMP_THRESHOLD); - setbit(events, MPI2_EVENT_ACTIVE_CABLE_EXCEPTION); + if (sc->facts->MsgVersion >= MPI2_VERSION_02_06) { + setbit(events, MPI2_EVENT_ACTIVE_CABLE_EXCEPTION); + if (sc->mpr_flags & MPR_FLAGS_GEN35_IOC) { + setbit(events, MPI2_EVENT_PCIE_DEVICE_STATUS_CHANGE); + setbit(events, MPI2_EVENT_PCIE_ENUMERATION); + setbit(events, MPI2_EVENT_PCIE_TOPOLOGY_CHANGE_LIST); + } + } mpr_register_events(sc, events, mprsas_evt_handler, NULL, &sc->sassc->mprsas_eh); @@ -1018,6 +1028,7 @@ if ((sc->max_io_pages > 0) && (sc->max_io_pages * PAGE_SIZE < cpi->maxio)) cpi->maxio = sc->max_io_pages * PAGE_SIZE; + sc->maxio = cpi->maxio; mprsas_set_ccbstatus(ccb, CAM_REQ_CMP); break; } @@ -1679,6 +1690,160 @@ } } +/** + * mprsas_build_nvme_unmap - Build Native NVMe DSM command equivalent + * to SCSI Unmap. + * Return 0 - for success, + * 1 - to immediately return back the command with success status to CAM + * negative value - to fallback to firmware path i.e. issue scsi unmap + * to FW without any translation. + */ +static int +mprsas_build_nvme_unmap(struct mpr_softc *sc, struct mpr_command *cm, + union ccb *ccb, struct mprsas_target *targ) +{ + Mpi26NVMeEncapsulatedRequest_t *req = NULL; + struct ccb_scsiio *csio; + struct unmap_parm_list *plist; + struct nvme_dsm_range *nvme_dsm_ranges = NULL; + struct nvme_command *c; + int i, res; + uint16_t ndesc, list_len, data_length; + struct mpr_prp_page *prp_page_info; + uint64_t nvme_dsm_ranges_dma_handle; + + csio = &ccb->csio; +#if __FreeBSD_version >= 1100103 + list_len = (scsiio_cdb_ptr(csio)[7] << 8 | scsiio_cdb_ptr(csio)[8]); +#else + if (csio->ccb_h.flags & CAM_CDB_POINTER) { + list_len = (ccb->csio.cdb_io.cdb_ptr[7] << 8 | + ccb->csio.cdb_io.cdb_ptr[8]); + } else { + list_len = (ccb->csio.cdb_io.cdb_bytes[7] << 8 | + ccb->csio.cdb_io.cdb_bytes[8]); + } +#endif + if (!list_len) { + mpr_dprint(sc, MPR_ERROR, "Parameter list length is Zero\n"); + return -EINVAL; + } + + plist = malloc(csio->dxfer_len, M_MPR, M_ZERO|M_NOWAIT); + if (!plist) { + mpr_dprint(sc, MPR_ERROR, "Unable to allocate memory to " + "save UNMAP data\n"); + return -ENOMEM; + } + + /* Copy SCSI unmap data to a local buffer */ + bcopy(csio->data_ptr, plist, csio->dxfer_len); + + /* return back the unmap command to CAM with success status, + * if number of descripts is zero. + */ + ndesc = be16toh(plist->unmap_blk_desc_data_len) >> 4; + if (!ndesc) { + mpr_dprint(sc, MPR_XINFO, "Number of descriptors in " + "UNMAP cmd is Zero\n"); + res = 1; + goto out; + } + + data_length = ndesc * sizeof(struct nvme_dsm_range); + if (data_length > targ->MDTS) { + mpr_dprint(sc, MPR_ERROR, "data length: %d is greater than " + "Device's MDTS: %d\n", data_length, targ->MDTS); + res = -EINVAL; + goto out; + } + + prp_page_info = mpr_alloc_prp_page(sc); + KASSERT(prp_page_info != NULL, ("%s: There is no PRP Page for " + "UNMAP command.\n", __func__)); + + /* + * Insert the allocated PRP page into the command's PRP page list. This + * will be freed when the command is freed. + */ + TAILQ_INSERT_TAIL(&cm->cm_prp_page_list, prp_page_info, prp_page_link); + + nvme_dsm_ranges = (struct nvme_dsm_range *)prp_page_info->prp_page; + nvme_dsm_ranges_dma_handle = prp_page_info->prp_page_busaddr; + + bzero(nvme_dsm_ranges, data_length); + + /* Convert SCSI unmap's descriptor data to NVMe DSM specific Range data + * for each descriptors contained in SCSI UNMAP data. + */ + for (i = 0; i < ndesc; i++) { + nvme_dsm_ranges[i].length = + htole32(be32toh(plist->desc[i].nlb)); + nvme_dsm_ranges[i].starting_lba = + htole64(be64toh(plist->desc[i].slba)); + nvme_dsm_ranges[i].attributes = 0; + } + + /* Build MPI2.6's NVMe Encapsulated Request Message */ + req = (Mpi26NVMeEncapsulatedRequest_t *)cm->cm_req; + bzero(req, sizeof(*req)); + req->DevHandle = htole16(targ->handle); + req->Function = MPI2_FUNCTION_NVME_ENCAPSULATED; + req->Flags = MPI26_NVME_FLAGS_WRITE; + req->ErrorResponseBaseAddress.High = + htole32((uint32_t)((uint64_t)cm->cm_sense_busaddr >> 32)); + req->ErrorResponseBaseAddress.Low = + htole32(cm->cm_sense_busaddr); + req->ErrorResponseAllocationLength = + htole16(sizeof(struct nvme_completion)); + req->EncapsulatedCommandLength = + htole16(sizeof(struct nvme_command)); + req->DataLength = htole32(data_length); + + /* Build NVMe DSM command */ + c = (struct nvme_command *) req->NVMe_Command; + c->opc = NVME_OPC_DATASET_MANAGEMENT; + c->nsid = htole32(csio->ccb_h.target_lun + 1); + c->cdw10 = htole32(ndesc - 1); + c->cdw11 = htole32(NVME_DSM_ATTR_DEALLOCATE); + + cm->cm_length = data_length; + cm->cm_data = NULL; + + cm->cm_complete = mprsas_scsiio_complete; + cm->cm_complete_data = ccb; + cm->cm_targ = targ; + cm->cm_lun = csio->ccb_h.target_lun; + cm->cm_ccb = ccb; + + cm->cm_desc.Default.RequestFlags = + MPI26_REQ_DESCRIPT_FLAGS_PCIE_ENCAPSULATED; + +#if __FreeBSD_version >= 1000029 + callout_reset_sbt(&cm->cm_callout, SBT_1MS * ccb->ccb_h.timeout, 0, + mprsas_scsiio_timeout, cm, 0); +#else //__FreeBSD_version < 1000029 + callout_reset(&cm->cm_callout, (ccb->ccb_h.timeout * hz) / 1000, + mprsas_scsiio_timeout, cm); +#endif //__FreeBSD_version >= 1000029 + + targ->issued++; + targ->outstanding++; + TAILQ_INSERT_TAIL(&targ->commands, cm, cm_link); + ccb->ccb_h.status |= CAM_SIM_QUEUED; + + mprsas_log_command(cm, MPR_XINFO, "%s cm %p ccb %p outstanding %u\n", + __func__, cm, ccb, targ->outstanding); + + mpr_build_nvme_prp(sc, cm, req, (void *)nvme_dsm_ranges_dma_handle, 0, + data_length); + mpr_map_command(sc, cm); + +out: + free(plist, M_MPR); + return 0; +} + static void mprsas_action_scsiio(struct mprsas_softc *sassc, union ccb *ccb) { @@ -1688,9 +1853,10 @@ struct mprsas_target *targ; struct mprsas_lun *lun; struct mpr_command *cm; - uint8_t i, lba_byte, *ref_tag_addr; + uint8_t i, lba_byte, *ref_tag_addr, scsi_opcode; uint16_t eedp_flags; uint32_t mpi_control; + int rc; sc = sassc->sc; MPR_FUNCTRACE(sc); @@ -1776,6 +1942,30 @@ return; } + /* For NVME device's issue UNMAP command directly to NVME drives by + * constructing equivalent native NVMe DataSetManagement command. + */ +#if __FreeBSD_version >= 1100103 + scsi_opcode = scsiio_cdb_ptr(csio)[0]; +#else + if (csio->ccb_h.flags & CAM_CDB_POINTER) + scsi_opcode = csio->cdb_io.cdb_ptr[0]; + else + scsi_opcode = csio->cdb_io.cdb_bytes[0]; +#endif + if (scsi_opcode == UNMAP && + targ->is_nvme && + (csio->ccb_h.flags & CAM_DATA_MASK) == CAM_DATA_VADDR) { + rc = mprsas_build_nvme_unmap(sc, cm, ccb, targ); + if (rc == 1) { /* return command to CAM with success status */ + mpr_free_command(sc, cm); + mprsas_set_ccbstatus(ccb, CAM_REQ_CMP); + xpt_done(ccb); + return; + } else if (!rc) /* Issued NVMe Encapsulated Request Message */ + return; + } + req = (MPI2_SCSI_IO_REQUEST *)cm->cm_req; bzero(req, sizeof(*req)); req->DevHandle = htole16(targ->handle); @@ -1848,8 +2038,8 @@ bcopy(csio->cdb_io.cdb_ptr, &req->CDB.CDB32[0], csio->cdb_len); else { KASSERT(csio->cdb_len <= IOCDBLEN, - ("cdb_len %d is greater than IOCDBLEN but CAM_CDB_POINTER is not set", - csio->cdb_len)); + ("cdb_len %d is greater than IOCDBLEN but CAM_CDB_POINTER " + "is not set", csio->cdb_len)); bcopy(csio->cdb_io.cdb_bytes, &req->CDB.CDB32[0],csio->cdb_len); } req->IoFlags = htole16(csio->cdb_len); @@ -1873,6 +2063,10 @@ eedp_flags |= (MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG | MPI2_SCSIIO_EEDPFLAGS_CHECK_REFTAG | MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD); + if (sc->mpr_flags & MPR_FLAGS_GEN35_IOC) { + eedp_flags |= + MPI25_SCSIIO_EEDPFLAGS_APPTAG_DISABLE_MODE; + } req->EEDPFlags = htole16(eedp_flags); /* @@ -1932,11 +2126,15 @@ req->IoFlags |= MPI25_SCSIIO_IOFLAGS_FAST_PATH; cm->cm_desc.FastPathSCSIIO.RequestFlags = MPI25_REQ_DESCRIPT_FLAGS_FAST_PATH_SCSI_IO; - cm->cm_desc.FastPathSCSIIO.DevHandle = htole16(targ->handle); + if (!sc->atomic_desc_capable) { + cm->cm_desc.FastPathSCSIIO.DevHandle = + htole16(targ->handle); + } } else { cm->cm_desc.SCSIIO.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO; - cm->cm_desc.SCSIIO.DevHandle = htole16(targ->handle); + if (!sc->atomic_desc_capable) + cm->cm_desc.SCSIIO.DevHandle = htole16(targ->handle); } #if __FreeBSD_version >= 1000029 @@ -2159,6 +2357,200 @@ } } +/** mprsas_nvme_trans_status_code + * + * Convert Native NVMe command error status to + * equivalent SCSI error status. + * + * Returns appropriate scsi_status + */ +static u8 +mprsas_nvme_trans_status_code(struct nvme_status nvme_status, + struct mpr_command *cm) +{ + u8 status = MPI2_SCSI_STATUS_GOOD; + int skey, asc, ascq; + union ccb *ccb = cm->cm_complete_data; + int returned_sense_len; + + status = MPI2_SCSI_STATUS_CHECK_CONDITION; + skey = SSD_KEY_ILLEGAL_REQUEST; + asc = SCSI_ASC_NO_SENSE; + ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; + + switch (nvme_status.sct) { + case NVME_SCT_GENERIC: + switch (nvme_status.sc) { + case NVME_SC_SUCCESS: + status = MPI2_SCSI_STATUS_GOOD; + skey = SSD_KEY_NO_SENSE; + asc = SCSI_ASC_NO_SENSE; + ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; + break; + case NVME_SC_INVALID_OPCODE: + status = MPI2_SCSI_STATUS_CHECK_CONDITION; + skey = SSD_KEY_ILLEGAL_REQUEST; + asc = SCSI_ASC_ILLEGAL_COMMAND; + ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; + break; + case NVME_SC_INVALID_FIELD: + status = MPI2_SCSI_STATUS_CHECK_CONDITION; + skey = SSD_KEY_ILLEGAL_REQUEST; + asc = SCSI_ASC_INVALID_CDB; + ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; + break; + case NVME_SC_DATA_TRANSFER_ERROR: + status = MPI2_SCSI_STATUS_CHECK_CONDITION; + skey = SSD_KEY_MEDIUM_ERROR; + asc = SCSI_ASC_NO_SENSE; + ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; + break; + case NVME_SC_ABORTED_POWER_LOSS: + status = MPI2_SCSI_STATUS_TASK_ABORTED; + skey = SSD_KEY_ABORTED_COMMAND; + asc = SCSI_ASC_WARNING; + ascq = SCSI_ASCQ_POWER_LOSS_EXPECTED; + break; + case NVME_SC_INTERNAL_DEVICE_ERROR: + status = MPI2_SCSI_STATUS_CHECK_CONDITION; + skey = SSD_KEY_HARDWARE_ERROR; + asc = SCSI_ASC_INTERNAL_TARGET_FAILURE; + ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; + break; + case NVME_SC_ABORTED_BY_REQUEST: + case NVME_SC_ABORTED_SQ_DELETION: + case NVME_SC_ABORTED_FAILED_FUSED: + case NVME_SC_ABORTED_MISSING_FUSED: + status = MPI2_SCSI_STATUS_TASK_ABORTED; + skey = SSD_KEY_ABORTED_COMMAND; + asc = SCSI_ASC_NO_SENSE; + ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; + break; + case NVME_SC_INVALID_NAMESPACE_OR_FORMAT: + status = MPI2_SCSI_STATUS_CHECK_CONDITION; + skey = SSD_KEY_ILLEGAL_REQUEST; + asc = SCSI_ASC_ACCESS_DENIED_INVALID_LUN_ID; + ascq = SCSI_ASCQ_INVALID_LUN_ID; + break; + case NVME_SC_LBA_OUT_OF_RANGE: + status = MPI2_SCSI_STATUS_CHECK_CONDITION; + skey = SSD_KEY_ILLEGAL_REQUEST; + asc = SCSI_ASC_ILLEGAL_BLOCK; + ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; + break; + case NVME_SC_CAPACITY_EXCEEDED: + status = MPI2_SCSI_STATUS_CHECK_CONDITION; + skey = SSD_KEY_MEDIUM_ERROR; + asc = SCSI_ASC_NO_SENSE; + ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; + break; + case NVME_SC_NAMESPACE_NOT_READY: + status = MPI2_SCSI_STATUS_CHECK_CONDITION; + skey = SSD_KEY_NOT_READY; + asc = SCSI_ASC_LUN_NOT_READY; + ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; + break; + } + break; + case NVME_SCT_COMMAND_SPECIFIC: + switch (nvme_status.sc) { + case NVME_SC_INVALID_FORMAT: + status = MPI2_SCSI_STATUS_CHECK_CONDITION; + skey = SSD_KEY_ILLEGAL_REQUEST; + asc = SCSI_ASC_FORMAT_COMMAND_FAILED; + ascq = SCSI_ASCQ_FORMAT_COMMAND_FAILED; + break; + case NVME_SC_CONFLICTING_ATTRIBUTES: + status = MPI2_SCSI_STATUS_CHECK_CONDITION; + skey = SSD_KEY_ILLEGAL_REQUEST; + asc = SCSI_ASC_INVALID_CDB; + ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; + break; + } + break; + case NVME_SCT_MEDIA_ERROR: + switch (nvme_status.sc) { + case NVME_SC_WRITE_FAULTS: + status = MPI2_SCSI_STATUS_CHECK_CONDITION; + skey = SSD_KEY_MEDIUM_ERROR; + asc = SCSI_ASC_PERIPHERAL_DEV_WRITE_FAULT; + ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; + break; + case NVME_SC_UNRECOVERED_READ_ERROR: + status = MPI2_SCSI_STATUS_CHECK_CONDITION; + skey = SSD_KEY_MEDIUM_ERROR; + asc = SCSI_ASC_UNRECOVERED_READ_ERROR; + ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; + break; + case NVME_SC_GUARD_CHECK_ERROR: + status = MPI2_SCSI_STATUS_CHECK_CONDITION; + skey = SSD_KEY_MEDIUM_ERROR; + asc = SCSI_ASC_LOG_BLOCK_GUARD_CHECK_FAILED; + ascq = SCSI_ASCQ_LOG_BLOCK_GUARD_CHECK_FAILED; + break; + case NVME_SC_APPLICATION_TAG_CHECK_ERROR: + status = MPI2_SCSI_STATUS_CHECK_CONDITION; + skey = SSD_KEY_MEDIUM_ERROR; + asc = SCSI_ASC_LOG_BLOCK_APPTAG_CHECK_FAILED; + ascq = SCSI_ASCQ_LOG_BLOCK_APPTAG_CHECK_FAILED; + break; + case NVME_SC_REFERENCE_TAG_CHECK_ERROR: + status = MPI2_SCSI_STATUS_CHECK_CONDITION; + skey = SSD_KEY_MEDIUM_ERROR; + asc = SCSI_ASC_LOG_BLOCK_REFTAG_CHECK_FAILED; + ascq = SCSI_ASCQ_LOG_BLOCK_REFTAG_CHECK_FAILED; + break; + case NVME_SC_COMPARE_FAILURE: + status = MPI2_SCSI_STATUS_CHECK_CONDITION; + skey = SSD_KEY_MISCOMPARE; + asc = SCSI_ASC_MISCOMPARE_DURING_VERIFY; + ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; + break; + case NVME_SC_ACCESS_DENIED: + status = MPI2_SCSI_STATUS_CHECK_CONDITION; + skey = SSD_KEY_ILLEGAL_REQUEST; + asc = SCSI_ASC_ACCESS_DENIED_INVALID_LUN_ID; + ascq = SCSI_ASCQ_INVALID_LUN_ID; + break; + } + break; + } + + returned_sense_len = sizeof(struct scsi_sense_data); + if (returned_sense_len < ccb->csio.sense_len) + ccb->csio.sense_resid = ccb->csio.sense_len - + returned_sense_len; + else + ccb->csio.sense_resid = 0; + + scsi_set_sense_data(&ccb->csio.sense_data, SSD_TYPE_FIXED, + 1, skey, asc, ascq, SSD_ELEM_NONE); + ccb->ccb_h.status |= CAM_AUTOSNS_VALID; + + return status; +} + +/** mprsas_complete_nvme_unmap + * + * Complete native NVMe command issued using NVMe Encapsulated + * Request Message. + */ +static u8 +mprsas_complete_nvme_unmap(struct mpr_softc *sc, struct mpr_command *cm) +{ + Mpi26NVMeEncapsulatedErrorReply_t *mpi_reply; + struct nvme_completion *nvme_completion = NULL; + u8 scsi_status = MPI2_SCSI_STATUS_GOOD; + + mpi_reply =(Mpi26NVMeEncapsulatedErrorReply_t *)cm->cm_reply; + if (le16toh(mpi_reply->ErrorResponseCount)){ + nvme_completion = (struct nvme_completion *)cm->cm_sense; + scsi_status = mprsas_nvme_trans_status_code( + nvme_completion->status, cm); + } + return scsi_status; +} + static void mprsas_scsiio_complete(struct mpr_softc *sc, struct mpr_command *cm) { @@ -2167,7 +2559,7 @@ struct ccb_scsiio *csio; struct mprsas_softc *sassc; struct scsi_vpd_supported_page_list *vpd_list = NULL; - u8 *TLR_bits, TLR_on; + u8 *TLR_bits, TLR_on, *scsi_cdb; int dir = 0, i; u16 alloc_len; struct mprsas_target *target; @@ -2266,13 +2658,27 @@ } /* + * Point to the SCSI CDB, which is dependent on the CAM_CDB_POINTER + * flag, and use it in a few places in the rest of this function for + * convenience. Use the macro if available. + */ +#if __FreeBSD_version >= 1100103 + scsi_cdb = scsiio_cdb_ptr(csio); +#else + if (csio->ccb_h.flags & CAM_CDB_POINTER) + scsi_cdb = csio->cdb_io.cdb_ptr; + else + scsi_cdb = csio->cdb_io.cdb_bytes; +#endif + + /* * If this is a Start Stop Unit command and it was issued by the driver * during shutdown, decrement the refcount to account for all of the * commands that were sent. All SSU commands should be completed before * shutdown completes, meaning SSU_refcount will be 0 after SSU_started * is TRUE. */ - if (sc->SSU_started && (csio->cdb_io.cdb_bytes[0] == START_STOP_UNIT)) { + if (sc->SSU_started && (scsi_cdb[0] == START_STOP_UNIT)) { mpr_dprint(sc, MPR_INFO, "Decrementing SSU count.\n"); sc->SSU_refcount--; } @@ -2313,6 +2719,14 @@ return; } + target = &sassc->targets[target_id]; + if (scsi_cdb[0] == UNMAP && + target->is_nvme && + (csio->ccb_h.flags & CAM_DATA_MASK) == CAM_DATA_VADDR) { + rep->SCSIStatus = mprsas_complete_nvme_unmap(sc, cm); + csio->scsi_status = rep->SCSIStatus; + } + mprsas_log_command(cm, MPR_XINFO, "ioc %x scsi %x state %x xfer %u\n", le16toh(rep->IOCStatus), rep->SCSIStatus, rep->SCSIState, @@ -2324,7 +2738,6 @@ /* FALLTHROUGH */ case MPI2_IOCSTATUS_SUCCESS: case MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR: - if ((le16toh(rep->IOCStatus) & MPI2_IOCSTATUS_MASK) == MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR) mprsas_log_command(cm, MPR_XINFO, "recovered error\n"); @@ -2402,9 +2815,9 @@ * controller, turn the TLR_bits value ON if page 0x90 is * supported. */ - if ((csio->cdb_io.cdb_bytes[0] == INQUIRY) && - (csio->cdb_io.cdb_bytes[1] & SI_EVPD) && - (csio->cdb_io.cdb_bytes[2] == SVPD_SUPPORTED_PAGE_LIST) && + if ((scsi_cdb[0] == INQUIRY) && + (scsi_cdb[1] & SI_EVPD) && + (scsi_cdb[2] == SVPD_SUPPORTED_PAGE_LIST) && ((csio->ccb_h.flags & CAM_DATA_MASK) == CAM_DATA_VADDR) && (csio->data_ptr != NULL) && ((csio->data_ptr[0] & 0x1f) == T_SEQUENTIAL) && @@ -2416,8 +2829,7 @@ TLR_bits = &sc->mapping_table[target_id].TLR_bits; *TLR_bits = (u8)MPI2_SCSIIO_CONTROL_NO_TLR; TLR_on = (u8)MPI2_SCSIIO_CONTROL_TLR_ON; - alloc_len = ((u16)csio->cdb_io.cdb_bytes[3] << 8) + - csio->cdb_io.cdb_bytes[4]; + alloc_len = ((u16)scsi_cdb[3] << 8) + scsi_cdb[4]; alloc_len -= csio->resid; for (i = 0; i < MIN(vpd_list->length, alloc_len); i++) { if (vpd_list->list[i] == 0x90) { @@ -2432,7 +2844,7 @@ * a SCSI StartStopUnit command will be sent to it when the * driver is being shutdown. */ - if ((csio->cdb_io.cdb_bytes[0] == INQUIRY) && + if ((scsi_cdb[0] == INQUIRY) && (csio->data_ptr != NULL) && ((csio->data_ptr[0] & 0x1f) == T_DIRECT) && (sc->mapping_table[target_id].device_info & @@ -2523,7 +2935,14 @@ rep->SCSIStatus, rep->SCSIState, le32toh(rep->TransferCount)); csio->resid = cm->cm_length; - mprsas_set_ccbstatus(ccb, CAM_REQ_CMP_ERR); + + if (scsi_cdb[0] == UNMAP && + target->is_nvme && + (csio->ccb_h.flags & CAM_DATA_MASK) == CAM_DATA_VADDR) + mprsas_set_ccbstatus(ccb, CAM_REQ_CMP); + else + mprsas_set_ccbstatus(ccb, CAM_REQ_CMP_ERR); + break; } Index: mpr_sas_lsi.c =================================================================== --- mpr_sas_lsi.c +++ mpr_sas_lsi.c @@ -71,6 +71,7 @@ #include #include #include +#include #include #include #include @@ -116,6 +117,8 @@ static void mprsas_fw_event_free(struct mpr_softc *, struct mpr_fw_event_work *); static int mprsas_add_device(struct mpr_softc *sc, u16 handle, u8 linkrate); +static int mprsas_add_pcie_device(struct mpr_softc *sc, u16 handle, + u8 linkrate); static int mprsas_get_sata_identify(struct mpr_softc *sc, u16 handle, Mpi2SataPassthroughReply_t *mpi_reply, char *id_buffer, int sz, u32 devinfo); @@ -156,6 +159,7 @@ bcopy(event->EventData, fw_event->event_data, sz); fw_event->event = event->Event; if ((event->Event == MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST || + event->Event == MPI2_EVENT_PCIE_TOPOLOGY_CHANGE_LIST || event->Event == MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE || event->Event == MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST) && sc->track_mapping_events) @@ -167,13 +171,13 @@ * events are processed. */ if ((event->Event == MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST || + event->Event == MPI2_EVENT_PCIE_TOPOLOGY_CHANGE_LIST || event->Event == MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST) && sc->wait_for_port_enable) mprsas_startup_increment(sc->sassc); TAILQ_INSERT_TAIL(&sc->sassc->ev_queue, fw_event, ev_link); taskqueue_enqueue(sc->sassc->ev_tq, &sc->sassc->ev_task); - } static void @@ -205,7 +209,7 @@ { MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST *data; MPI2_EVENT_SAS_TOPO_PHY_ENTRY *phy; - int i; + uint8_t i; data = (MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST *) fw_event->event_data; @@ -674,6 +678,60 @@ } break; } + case MPI2_EVENT_PCIE_TOPOLOGY_CHANGE_LIST: + { + MPI26_EVENT_DATA_PCIE_TOPOLOGY_CHANGE_LIST *data; + MPI26_EVENT_PCIE_TOPO_PORT_ENTRY *port_entry; + uint8_t i, link_rate; + uint16_t handle; + + data = (MPI26_EVENT_DATA_PCIE_TOPOLOGY_CHANGE_LIST *) + fw_event->event_data; + + mpr_mapping_pcie_topology_change_event(sc, + fw_event->event_data); + + for (i = 0; i < data->NumEntries; i++) { + port_entry = &data->PortEntry[i]; + handle = le16toh(port_entry->AttachedDevHandle); + link_rate = port_entry->CurrentPortInfo & + MPI26_EVENT_PCIE_TOPO_PI_RATE_MASK; + switch (port_entry->PortStatus) { + case MPI26_EVENT_PCIE_TOPO_PS_DEV_ADDED: + if (link_rate < + MPI26_EVENT_PCIE_TOPO_PI_RATE_2_5) { + mpr_dprint(sc, MPR_ERROR, "%s: Cannot " + "add PCIe device with handle 0x%x " + "with unknown link rate.\n", + __func__, handle); + break; + } + if (mprsas_add_pcie_device(sc, handle, + link_rate)) { + mpr_dprint(sc, MPR_ERROR, "%s: failed " + "to add PCIe device with handle " + "0x%x\n", __func__, handle); + mprsas_prepare_remove(sassc, handle); + } + break; + case MPI26_EVENT_PCIE_TOPO_PS_NOT_RESPONDING: + mprsas_prepare_remove(sassc, handle); + break; + case MPI26_EVENT_PCIE_TOPO_PS_PORT_CHANGED: + case MPI26_EVENT_PCIE_TOPO_PS_NO_CHANGE: + case MPI26_EVENT_PCIE_TOPO_PS_DELAY_NOT_RESPONDING: + default: + break; + } + } + /* + * refcount was incremented for this event in + * mprsas_evt_handler. Decrement it here because the event has + * been processed. + */ + mprsas_startup_decrement(sassc); + break; + } case MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE: case MPI2_EVENT_SAS_BROADCAST_PRIMITIVE: default: @@ -703,7 +761,8 @@ } static int -mprsas_add_device(struct mpr_softc *sc, u16 handle, u8 linkrate){ +mprsas_add_device(struct mpr_softc *sc, u16 handle, u8 linkrate) +{ char devstring[80]; struct mprsas_softc *sassc; struct mprsas_target *targ; @@ -779,8 +838,8 @@ if (sc->use_phynum != -1) id = mpr_mapping_get_sas_id(sc, sas_address, handle); if (id == MPR_MAP_BAD_ID) { - if ((sc->use_phynum == 0) - || ((id = config_page.PhyNum) > sassc->maxtargets)) { + if ((sc->use_phynum == 0) || + ((id = config_page.PhyNum) > sassc->maxtargets)) { mpr_dprint(sc, MPR_INFO, "failure at %s:%d/%s()! " "Could not get ID for device with handle 0x%04x\n", __FILE__, __LINE__, __func__, handle); @@ -827,8 +886,10 @@ if (is_SATA_SSD) { targ->flags = MPR_TARGET_IS_SATA_SSD; } - if (le16toh(config_page.Flags) & - MPI25_SAS_DEVICE0_FLAGS_FAST_PATH_CAPABLE) { + if ((le16toh(config_page.Flags) & + MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH) && + (le16toh(config_page.Flags) & + MPI25_SAS_DEVICE0_FLAGS_FAST_PATH_CAPABLE)) { targ->scsi_req_desc_type = MPI25_REQ_DESCRIPT_FLAGS_FAST_PATH_SCSI_IO; } @@ -908,7 +969,7 @@ mprsas_startup_decrement(sassc); return (error); } - + int mprsas_get_sas_address_for_sata_disk(struct mpr_softc *sc, u64 *sas_address, u16 handle, u32 device_info, u8 *is_SATA_SSD) @@ -1140,6 +1201,141 @@ } static int +mprsas_add_pcie_device(struct mpr_softc *sc, u16 handle, u8 linkrate) +{ + char devstring[80]; + struct mprsas_softc *sassc; + struct mprsas_target *targ; + Mpi2ConfigReply_t mpi_reply; + Mpi26PCIeDevicePage0_t config_page; + Mpi26PCIeDevicePage2_t config_page2; + uint64_t pcie_wwid, parent_wwid = 0; + u32 device_info, parent_devinfo = 0; + unsigned int id; + int error = 0; + struct mprsas_lun *lun; + + sassc = sc->sassc; + mprsas_startup_increment(sassc); + if ((mpr_config_get_pcie_device_pg0(sc, &mpi_reply, &config_page, + MPI26_PCIE_DEVICE_PGAD_FORM_HANDLE, handle))) { + printf("%s: error reading PCIe device page0\n", __func__); + error = ENXIO; + goto out; + } + + device_info = le32toh(config_page.DeviceInfo); + + if (((device_info & MPI26_PCIE_DEVINFO_PCI_SWITCH) == 0) + && (le16toh(config_page.ParentDevHandle) != 0)) { + Mpi2ConfigReply_t tmp_mpi_reply; + Mpi26PCIeDevicePage0_t parent_config_page; + + if ((mpr_config_get_pcie_device_pg0(sc, &tmp_mpi_reply, + &parent_config_page, MPI26_PCIE_DEVICE_PGAD_FORM_HANDLE, + le16toh(config_page.ParentDevHandle)))) { + printf("%s: error reading PCIe device %#x page0\n", + __func__, le16toh(config_page.ParentDevHandle)); + } else { + parent_wwid = parent_config_page.WWID.High; + parent_wwid = (parent_wwid << 32) | + parent_config_page.WWID.Low; + parent_devinfo = le32toh(parent_config_page.DeviceInfo); + } + } + /* TODO Check proper endianness */ + pcie_wwid = config_page.WWID.High; + pcie_wwid = (pcie_wwid << 32) | config_page.WWID.Low; + mpr_dprint(sc, MPR_INFO, "PCIe WWID from PCIe device page0 = %jx\n", + pcie_wwid); + + if ((mpr_config_get_pcie_device_pg2(sc, &mpi_reply, &config_page2, + MPI26_PCIE_DEVICE_PGAD_FORM_HANDLE, handle))) { + printf("%s: error reading PCIe device page2\n", __func__); + error = ENXIO; + goto out; + } + + id = mpr_mapping_get_sas_id(sc, pcie_wwid, handle); + if (id == MPR_MAP_BAD_ID) { + printf("failure at %s:%d/%s()! Could not get ID for device " + "with handle 0x%04x\n", __FILE__, __LINE__, __func__, + handle); + error = ENXIO; + goto out; + } + + if (mprsas_check_id(sassc, id) != 0) { + device_printf(sc->mpr_dev, "Excluding target id %d\n", id); + error = ENXIO; + goto out; + } + + mpr_dprint(sc, MPR_MAPPING, "WWID from PCIe device page0 = %jx\n", + pcie_wwid); + targ = &sassc->targets[id]; + targ->devinfo = device_info; + targ->encl_handle = le16toh(config_page.EnclosureHandle); + targ->encl_slot = le16toh(config_page.Slot); + targ->encl_level = config_page.EnclosureLevel; + targ->connector_name[0] = ((char *)&config_page.ConnectorName)[0]; + targ->connector_name[1] = ((char *)&config_page.ConnectorName)[1]; + targ->connector_name[2] = ((char *)&config_page.ConnectorName)[2]; + targ->connector_name[3] = ((char *)&config_page.ConnectorName)[3]; + targ->is_nvme = device_info & MPI26_PCIE_DEVINFO_NVME; + targ->MDTS = config_page2.MaximumDataTransferSize; + /* + * Assume always TRUE for encl_level_valid because there is no valid + * flag for PCIe. + */ + targ->encl_level_valid = TRUE; + targ->handle = handle; + targ->parent_handle = le16toh(config_page.ParentDevHandle); + targ->sasaddr = mpr_to_u64(&config_page.WWID); + targ->parent_sasaddr = le64toh(parent_wwid); + targ->parent_devinfo = parent_devinfo; + targ->tid = id; + targ->linkrate = linkrate; + targ->flags = 0; + if ((le16toh(config_page.Flags) & + MPI26_PCIEDEV0_FLAGS_ENABLED_FAST_PATH) && + (le16toh(config_page.Flags) & + MPI26_PCIEDEV0_FLAGS_FAST_PATH_CAPABLE)) { + targ->scsi_req_desc_type = + MPI25_REQ_DESCRIPT_FLAGS_FAST_PATH_SCSI_IO; + } + TAILQ_INIT(&targ->commands); + TAILQ_INIT(&targ->timedout_commands); + while (!SLIST_EMPTY(&targ->luns)) { + lun = SLIST_FIRST(&targ->luns); + SLIST_REMOVE_HEAD(&targ->luns, lun_link); + free(lun, M_MPR); + } + SLIST_INIT(&targ->luns); + + mpr_describe_devinfo(targ->devinfo, devstring, 80); + mpr_dprint(sc, (MPR_INFO|MPR_MAPPING), "Found PCIe device <%s> <%s> " + "handle<0x%04x> enclosureHandle<0x%04x> slot %d\n", devstring, + mpr_describe_table(mpr_pcie_linkrate_names, targ->linkrate), + targ->handle, targ->encl_handle, targ->encl_slot); + if (targ->encl_level_valid) { + mpr_dprint(sc, (MPR_INFO|MPR_MAPPING), "At enclosure level %d " + "and connector name (%4s)\n", targ->encl_level, + targ->connector_name); + } +#if ((__FreeBSD_version >= 1000000) && (__FreeBSD_version < 1000039)) || \ + (__FreeBSD_version < 902502) + if ((sassc->flags & MPRSAS_IN_STARTUP) == 0) +#endif + mprsas_rescan_target(sc, targ); + mpr_dprint(sc, MPR_MAPPING, "Target id 0x%x added\n", targ->tid); + +out: + mprsas_startup_decrement(sassc); + return (error); +} + +static int mprsas_volume_add(struct mpr_softc *sc, u16 handle) { struct mprsas_softc *sassc; @@ -1235,8 +1431,8 @@ if (target->stop_at_shutdown) { ccb = xpt_alloc_ccb_nowait(); if (ccb == NULL) { - mpr_dprint(sc, MPR_FAULT, "Unable to alloc CCB to stop " - "unit.\n"); + mpr_dprint(sc, MPR_FAULT, "Unable to alloc CCB " + "to stop unit.\n"); return; } Index: mpr_table.h =================================================================== --- mpr_table.h +++ mpr_table.h @@ -40,6 +40,7 @@ extern struct mpr_table_lookup mpr_event_names[]; extern struct mpr_table_lookup mpr_phystatus_names[]; extern struct mpr_table_lookup mpr_linkrate_names[]; +extern struct mpr_table_lookup mpr_pcie_linkrate_names[]; void _mpr_print_iocfacts(struct mpr_softc *, MPI2_IOC_FACTS_REPLY *); void _mpr_print_portfacts(struct mpr_softc *, MPI2_PORT_FACTS_REPLY *); Index: mpr_table.c =================================================================== --- mpr_table.c +++ mpr_table.c @@ -58,6 +58,7 @@ #include #include #include +#include #include #include #include @@ -74,6 +75,7 @@ return(table[i+1].string); } +//SLM-Add new PCIe info to all of these tables struct mpr_table_lookup mpr_event_names[] = { {"LogData", 0x01}, {"StateChange", 0x02}, @@ -100,6 +102,10 @@ {"TempThreshold", 0x27}, {"HostMessage", 0x28}, {"PowerPerformanceChange", 0x29}, + {"PCIeDeviceStatusChange", 0x30}, + {"PCIeEnumeration", 0x31}, + {"PCIeTopologyChangeList", 0x32}, + {"PCIeLinkCounter", 0x33}, {"CableEvent", 0x34}, {NULL, 0}, {"Unknown Event", 0} @@ -192,6 +198,16 @@ {"Unknown", 0x00} }; +struct mpr_table_lookup mpr_pcie_linkrate_names[] = { + {"Port disabled", 0x01}, + {"2.5GT/sec", 0x02}, + {"5.0GT/sec", 0x03}, + {"8.0GT/sec", 0x04}, + {"16.0GT/sec", 0x05}, + {NULL, 0}, + {"LinkRate Unknown", 0x00} +}; + void mpr_describe_devinfo(uint32_t devinfo, char *string, int len) { @@ -321,6 +337,7 @@ "\40MaxEnclosures"); break; } +//SLM-add for PCIE EVENT too case MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST: { MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST *data; Index: mpr_user.c =================================================================== --- mpr_user.c +++ mpr_user.c @@ -99,6 +99,7 @@ #include #include #include +#include #include #include #include @@ -747,6 +748,8 @@ { MPI2_REQUEST_HEADER *hdr, tmphdr; MPI2_DEFAULT_REPLY *rpl; + Mpi26NVMeEncapsulatedErrorReply_t *nvme_error_reply = NULL; + Mpi26NVMeEncapsulatedRequest_t *nvme_encap_request = NULL; struct mpr_command *cm = NULL; int i, err = 0, dir = 0, sz; uint8_t tool, function = 0; @@ -923,8 +926,8 @@ cm->cm_data, data->DataSize); } if (err != 0) - mpr_dprint(sc, MPR_FAULT, "%s: failed to copy " - "IOCTL data from user space\n", __func__); + mpr_dprint(sc, MPR_FAULT, "%s: failed to copy IOCTL " + "data from user space\n", __func__); } /* * Set this flag only if processing a command that does not need an @@ -946,6 +949,35 @@ } cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; + if (function == MPI2_FUNCTION_NVME_ENCAPSULATED) { + nvme_encap_request = + (Mpi26NVMeEncapsulatedRequest_t *)cm->cm_req; + cm->cm_desc.Default.RequestFlags = + MPI26_REQ_DESCRIPT_FLAGS_PCIE_ENCAPSULATED; + + /* + * Get the Physical Address of the sense buffer. + * Save the user's Error Response buffer address and use that + * field to hold the sense buffer address. + * Clear the internal sense buffer, which will potentially hold + * the Completion Queue Entry on return, or 0 if no Entry. + * Build the PRPs and set direction bits. + * Send the request. + */ + cm->nvme_error_response = + (uint64_t *)(uintptr_t)(((uint64_t)nvme_encap_request-> + ErrorResponseBaseAddress.High << 32) | + (uint64_t)nvme_encap_request-> + ErrorResponseBaseAddress.Low); + nvme_encap_request->ErrorResponseBaseAddress.High = + htole32((uint32_t)((uint64_t)cm->cm_sense_busaddr >> 32)); + nvme_encap_request->ErrorResponseBaseAddress.Low = + htole32(cm->cm_sense_busaddr); + memset(cm->cm_sense, 0, NVME_ERROR_RESPONSE_SIZE); + mpr_build_nvme_prp(sc, cm, nvme_encap_request, cm->cm_data, + data->DataSize, data->DataOutSize); + } + /* * Set up Sense buffer and SGL offset for IO passthru. SCSI IO request * uses SCSI IO or Fast Path SCSI IO descriptor. @@ -994,15 +1026,19 @@ MPI25_REQ_DESCRIPT_FLAGS_FAST_PATH_SCSI_IO) { cm->cm_desc.FastPathSCSIIO.RequestFlags = MPI25_REQ_DESCRIPT_FLAGS_FAST_PATH_SCSI_IO; - cm->cm_desc.FastPathSCSIIO.DevHandle = - scsi_io_req->DevHandle; + if (!sc->atomic_desc_capable) { + cm->cm_desc.FastPathSCSIIO.DevHandle = + scsi_io_req->DevHandle; + } scsi_io_req->IoFlags |= MPI25_SCSIIO_IOFLAGS_FAST_PATH; } else { cm->cm_desc.SCSIIO.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO; - cm->cm_desc.SCSIIO.DevHandle = - scsi_io_req->DevHandle; + if (!sc->atomic_desc_capable) { + cm->cm_desc.SCSIIO.DevHandle = + scsi_io_req->DevHandle; + } } /* @@ -1079,6 +1115,38 @@ mpr_lock(sc); } } + + /* + * Copy out the NVMe Error Reponse to user. The Error Response + * buffer is given by the user, but a sense buffer is used to + * get that data from the IOC. The user's + * ErrorResponseBaseAddress is saved in the + * 'nvme_error_response' field before the command because that + * field is set to a sense buffer. When the command is + * complete, the Error Response data from the IOC is copied to + * that user address after it is checked for validity. + * Also note that 'sense' buffers are not defined for + * NVMe commands. Sense terminalogy is only used here so that + * the same IOCTL structure and sense buffers can be used for + * NVMe. + */ + if (function == MPI2_FUNCTION_NVME_ENCAPSULATED) { + if (cm->nvme_error_response == NULL) { + mpr_dprint(sc, MPR_INFO, "NVMe Error Response " + "buffer is NULL. Response data will not be " + "returned.\n"); + mpr_unlock(sc); + goto RetFreeUnlocked; + } + + nvme_error_reply = + (Mpi26NVMeEncapsulatedErrorReply_t *)cm->cm_reply; + sz = MIN(le32toh(nvme_error_reply->ErrorResponseCount), + NVME_ERROR_RESPONSE_SIZE); + mpr_unlock(sc); + copyout(cm->cm_sense, cm->nvme_error_response, sz); + mpr_lock(sc); + } } mpr_unlock(sc); @@ -2068,7 +2136,7 @@ return (EINVAL); if (target > sc->max_devices) { - mpr_dprint(sc, MPR_FAULT, "Target ID is out of range " + mpr_dprint(sc, MPR_XINFO, "Target ID is out of range " "for Bus/Target to DevHandle mapping."); return (EINVAL); } Index: mprvar.h =================================================================== --- mprvar.h +++ mprvar.h @@ -33,7 +33,7 @@ #ifndef _MPRVAR_H #define _MPRVAR_H -#define MPR_DRIVER_VERSION "15.01.00.00-fbsd" +#define MPR_DRIVER_VERSION "15.255.03.00" #define MPR_DB_MAX_WAIT 2500 @@ -50,6 +50,17 @@ #define MPR_DEFAULT_CHAIN_SEG_SIZE 8 #define MPR_MAX_CHAIN_ELEMENT_SIZE 16 +/* + * PCIe NVMe Specific defines + */ +//SLM-for now just use the same value as a SAS disk +#define NVME_QDEPTH MPR_REQ_FRAMES +#define PRP_ENTRY_SIZE 8 +#define NVME_CMD_PRP1_OFFSET 24 /* PRP1 offset in NVMe cmd */ +#define NVME_CMD_PRP2_OFFSET 32 /* PRP2 offset in NVMe cmd */ +#define NVME_ERROR_RESPONSE_SIZE 16 /* Max NVME Error Response */ +#define HOST_PAGE_SIZE_4K 12 + #define MPR_FUNCTRACE(sc) \ mpr_dprint((sc), MPR_TRACE, "%s\n", __func__) @@ -184,6 +195,12 @@ uint64_t chain_busaddr; }; +struct mpr_prp_page { + TAILQ_ENTRY(mpr_prp_page) prp_page_link; + uint64_t *prp_page; + uint64_t prp_page_busaddr; +}; + /* * This needs to be at least 2 to support SMP passthrough. */ @@ -229,9 +246,11 @@ #define MPR_CM_STATE_TIMEDOUT 2 bus_dmamap_t cm_dmamap; struct scsi_sense_data *cm_sense; + uint64_t *nvme_error_response; TAILQ_HEAD(, mpr_chain) cm_chain_list; + TAILQ_HEAD(, mpr_prp_page) cm_prp_page_list; uint32_t cm_req_busaddr; - uint32_t cm_sense_busaddr; + bus_addr_t cm_sense_busaddr; struct callout cm_callout; }; @@ -257,27 +276,35 @@ #define MPR_FLAGS_SHUTDOWN (1 << 3) #define MPR_FLAGS_DIAGRESET (1 << 4) #define MPR_FLAGS_ATTACH_DONE (1 << 5) +#define MPR_FLAGS_GEN35_IOC (1 << 6) u_int mpr_debug; u_int disable_msix; u_int disable_msi; + u_int atomic_desc_capable; int tm_cmds_active; int io_cmds_active; int io_cmds_highwater; int chain_free; int max_chains; int max_io_pages; + u_int maxio; int chain_free_lowwater; uint32_t chain_frame_size; uint16_t chain_seg_size; + int prp_buffer_size; + int prp_pages_free; + int prp_pages_free_lowwater; u_int enable_ssu; int spinup_wait_time; int use_phynum; uint64_t chain_alloc_fail; + uint64_t prp_page_alloc_fail; struct sysctl_ctx_list sysctl_ctx; struct sysctl_oid *sysctl_tree; char fw_version[16]; struct mpr_command *commands; struct mpr_chain *chains; + struct mpr_prp_page *prps; struct callout periodic; struct mprsas_softc *sassc; @@ -285,6 +312,7 @@ TAILQ_HEAD(, mpr_command) req_list; TAILQ_HEAD(, mpr_command) high_priority_req_list; TAILQ_HEAD(, mpr_chain) chain_list; + TAILQ_HEAD(, mpr_prp_page) prp_page_list; TAILQ_HEAD(, mpr_command) tm_list; int replypostindex; int replyfreeindex; @@ -333,6 +361,11 @@ bus_dma_tag_t chain_dmat; bus_dmamap_t chain_map; + uint8_t *prp_pages; + bus_addr_t prp_page_busaddr; + bus_dma_tag_t prp_page_dmat; + bus_dmamap_t prp_page_map; + MPI2_REPLY_DESCRIPTORS_UNION *post_queue; bus_addr_t post_busaddr; uint32_t *free_queue; @@ -471,10 +504,33 @@ TAILQ_INSERT_TAIL(&sc->chain_list, chain, chain_link); } +static __inline struct mpr_prp_page * +mpr_alloc_prp_page(struct mpr_softc *sc) +{ + struct mpr_prp_page *prp_page; + + if ((prp_page = TAILQ_FIRST(&sc->prp_page_list)) != NULL) { + TAILQ_REMOVE(&sc->prp_page_list, prp_page, prp_page_link); + sc->prp_pages_free--; + if (sc->prp_pages_free < sc->prp_pages_free_lowwater) + sc->prp_pages_free_lowwater = sc->prp_pages_free; + } else + sc->prp_page_alloc_fail++; + return (prp_page); +} + +static __inline void +mpr_free_prp_page(struct mpr_softc *sc, struct mpr_prp_page *prp_page) +{ + sc->prp_pages_free++; + TAILQ_INSERT_TAIL(&sc->prp_page_list, prp_page, prp_page_link); +} + static __inline void mpr_free_command(struct mpr_softc *sc, struct mpr_command *cm) { struct mpr_chain *chain, *chain_temp; + struct mpr_prp_page *prp_page, *prp_page_temp; if (cm->cm_reply != NULL) mpr_free_reply(sc, cm->cm_reply_data); @@ -497,6 +553,11 @@ TAILQ_REMOVE(&cm->cm_chain_list, chain, chain_link); mpr_free_chain(sc, chain); } + TAILQ_FOREACH_SAFE(prp_page, &cm->cm_prp_page_list, prp_page_link, + prp_page_temp) { + TAILQ_REMOVE(&cm->cm_prp_page_list, prp_page, prp_page_link); + mpr_free_prp_page(sc, prp_page); + } TAILQ_INSERT_TAIL(&sc->req_list, cm, cm_link); } @@ -510,7 +571,8 @@ return (NULL); TAILQ_REMOVE(&sc->req_list, cm, cm_link); - KASSERT(cm->cm_state == MPR_CM_STATE_FREE, ("mpr: Allocating busy command\n")); + KASSERT(cm->cm_state == MPR_CM_STATE_FREE, ("mpr: Allocating busy " + "command\n")); cm->cm_state = MPR_CM_STATE_BUSY; return (cm); } @@ -547,7 +609,8 @@ return (NULL); TAILQ_REMOVE(&sc->high_priority_req_list, cm, cm_link); - KASSERT(cm->cm_state == MPR_CM_STATE_FREE, ("mpr: Allocating busy command\n")); + KASSERT(cm->cm_state == MPR_CM_STATE_FREE, ("mpr: Allocating busy " + "command\n")); cm->cm_state = MPR_CM_STATE_BUSY; return (cm); } @@ -653,6 +716,9 @@ int mpr_restart(struct mpr_softc *); int mpr_update_events(struct mpr_softc *, struct mpr_event_handle *, uint8_t *); int mpr_deregister_events(struct mpr_softc *, struct mpr_event_handle *); +void mpr_build_nvme_prp(struct mpr_softc *sc, struct mpr_command *cm, + Mpi26NVMeEncapsulatedRequest_t *nvme_encap_request, void *data, + uint32_t data_in_sz, uint32_t data_out_sz); int mpr_push_sge(struct mpr_command *, MPI2_SGE_SIMPLE64 *, size_t, int); int mpr_push_ieee_sge(struct mpr_command *, void *, int); int mpr_add_dmaseg(struct mpr_command *, vm_paddr_t, size_t, u_int, int); @@ -682,6 +748,10 @@ Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage8_t *config_page); int mpr_config_get_sas_device_pg0(struct mpr_softc *, Mpi2ConfigReply_t *, Mpi2SasDevicePage0_t *, u32 , u16 ); +int mpr_config_get_pcie_device_pg0(struct mpr_softc *sc, Mpi2ConfigReply_t + *mpi_reply, Mpi26PCIeDevicePage0_t *config_page, u32 form, u16 handle); +int mpr_config_get_pcie_device_pg2(struct mpr_softc *sc, Mpi2ConfigReply_t + *mpi_reply, Mpi26PCIeDevicePage2_t *config_page, u32 form, u16 handle); int mpr_config_get_dpm_pg0(struct mpr_softc *, Mpi2ConfigReply_t *, Mpi2DriverMappingPage0_t *, u16 ); int mpr_config_get_raid_volume_pg1(struct mpr_softc *sc, @@ -702,6 +772,8 @@ int mpr_mapping_initialize(struct mpr_softc *); void mpr_mapping_topology_change_event(struct mpr_softc *, Mpi2EventDataSasTopologyChangeList_t *); +void mpr_mapping_pcie_topology_change_event(struct mpr_softc *sc, + Mpi26EventDataPCIeTopologyChangeList_t *event_data); int mpr_mapping_is_reinit_required(struct mpr_softc *); void mpr_mapping_free_memory(struct mpr_softc *sc); int mpr_config_set_dpm_pg0(struct mpr_softc *, Mpi2ConfigReply_t *, @@ -769,5 +841,52 @@ #define CAM_PRIORITY_NORMAL CAM_PRIORITY_NONE #endif +/* Definitions for SCSI unmap translation to NVMe DSM command */ + +/* UNMAP block descriptor structure */ +struct unmap_blk_desc { + uint64_t slba; + uint32_t nlb; + uint32_t resv; +}; + +/* UNMAP command's data */ +struct unmap_parm_list { + uint16_t unmap_data_len; + uint16_t unmap_blk_desc_data_len; + uint32_t resv; + struct unmap_blk_desc desc[0]; +}; + +/* SCSI ADDITIONAL SENSE Codes */ +#define FIXED_SENSE_DATA 0x70 +#define SCSI_ASC_NO_SENSE 0x00 +#define SCSI_ASC_PERIPHERAL_DEV_WRITE_FAULT 0x03 +#define SCSI_ASC_LUN_NOT_READY 0x04 +#define SCSI_ASC_WARNING 0x0B +#define SCSI_ASC_LOG_BLOCK_GUARD_CHECK_FAILED 0x10 +#define SCSI_ASC_LOG_BLOCK_APPTAG_CHECK_FAILED 0x10 +#define SCSI_ASC_LOG_BLOCK_REFTAG_CHECK_FAILED 0x10 +#define SCSI_ASC_UNRECOVERED_READ_ERROR 0x11 +#define SCSI_ASC_MISCOMPARE_DURING_VERIFY 0x1D +#define SCSI_ASC_ACCESS_DENIED_INVALID_LUN_ID 0x20 +#define SCSI_ASC_ILLEGAL_COMMAND 0x20 +#define SCSI_ASC_ILLEGAL_BLOCK 0x21 +#define SCSI_ASC_INVALID_CDB 0x24 +#define SCSI_ASC_INVALID_LUN 0x25 +#define SCSI_ASC_INVALID_PARAMETER 0x26 +#define SCSI_ASC_FORMAT_COMMAND_FAILED 0x31 +#define SCSI_ASC_INTERNAL_TARGET_FAILURE 0x44 + +/* SCSI ADDITIONAL SENSE Code Qualifiers */ +#define SCSI_ASCQ_CAUSE_NOT_REPORTABLE 0x00 +#define SCSI_ASCQ_FORMAT_COMMAND_FAILED 0x01 +#define SCSI_ASCQ_LOG_BLOCK_GUARD_CHECK_FAILED 0x01 +#define SCSI_ASCQ_LOG_BLOCK_APPTAG_CHECK_FAILED 0x02 +#define SCSI_ASCQ_LOG_BLOCK_REFTAG_CHECK_FAILED 0x03 +#define SCSI_ASCQ_FORMAT_IN_PROGRESS 0x04 +#define SCSI_ASCQ_POWER_LOSS_EXPECTED 0x08 +#define SCSI_ASCQ_INVALID_LUN_ID 0x09 + #endif