diff options
Diffstat (limited to 'drivers/scsi/mpt2sas')
| -rw-r--r-- | drivers/scsi/mpt2sas/Kconfig | 3 | ||||
| -rw-r--r-- | drivers/scsi/mpt2sas/mpi/mpi2.h | 68 | ||||
| -rw-r--r-- | drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h | 529 | ||||
| -rw-r--r-- | drivers/scsi/mpt2sas/mpi/mpi2_history.txt | 334 | ||||
| -rw-r--r-- | drivers/scsi/mpt2sas/mpi/mpi2_init.h | 61 | ||||
| -rw-r--r-- | drivers/scsi/mpt2sas/mpi/mpi2_ioc.h | 352 | ||||
| -rw-r--r-- | drivers/scsi/mpt2sas/mpi/mpi2_raid.h | 79 | ||||
| -rw-r--r-- | drivers/scsi/mpt2sas/mpi/mpi2_sas.h | 16 | ||||
| -rw-r--r-- | drivers/scsi/mpt2sas/mpi/mpi2_tool.h | 70 | ||||
| -rw-r--r-- | drivers/scsi/mpt2sas/mpi/mpi2_type.h | 2 | ||||
| -rw-r--r-- | drivers/scsi/mpt2sas/mpt2sas_base.c | 1711 | ||||
| -rw-r--r-- | drivers/scsi/mpt2sas/mpt2sas_base.h | 362 | ||||
| -rw-r--r-- | drivers/scsi/mpt2sas/mpt2sas_config.c | 170 | ||||
| -rw-r--r-- | drivers/scsi/mpt2sas/mpt2sas_ctl.c | 938 | ||||
| -rw-r--r-- | drivers/scsi/mpt2sas/mpt2sas_ctl.h | 3 | ||||
| -rw-r--r-- | drivers/scsi/mpt2sas/mpt2sas_debug.h | 4 | ||||
| -rw-r--r-- | drivers/scsi/mpt2sas/mpt2sas_scsih.c | 4282 | ||||
| -rw-r--r-- | drivers/scsi/mpt2sas/mpt2sas_transport.c | 1148 |
18 files changed, 7682 insertions, 2450 deletions
diff --git a/drivers/scsi/mpt2sas/Kconfig b/drivers/scsi/mpt2sas/Kconfig index 70c4c2467dd..39f08dd2055 100644 --- a/drivers/scsi/mpt2sas/Kconfig +++ b/drivers/scsi/mpt2sas/Kconfig @@ -2,7 +2,7 @@ # Kernel configuration file for the MPT2SAS # # This code is based on drivers/scsi/mpt2sas/Kconfig -# Copyright (C) 2007-2009 LSI Corporation +# Copyright (C) 2007-2012 LSI Corporation # (mailto:DL-MPTFusionLinux@lsi.com) # This program is free software; you can redistribute it and/or @@ -44,6 +44,7 @@ config SCSI_MPT2SAS tristate "LSI MPT Fusion SAS 2.0 Device Driver" depends on PCI && SCSI select SCSI_SAS_ATTRS + select RAID_ATTRS ---help--- This driver supports PCI-Express SAS 6Gb/s Host Adapters. diff --git a/drivers/scsi/mpt2sas/mpi/mpi2.h b/drivers/scsi/mpt2sas/mpi/mpi2.h index 91416810529..7b14a015c90 100644 --- a/drivers/scsi/mpt2sas/mpi/mpi2.h +++ b/drivers/scsi/mpt2sas/mpi/mpi2.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2009 LSI Corporation. + * Copyright (c) 2000-2013 LSI Corporation. * * * Name: mpi2.h @@ -8,7 +8,7 @@ * scatter/gather formats. * Creation Date: June 21, 2006 * - * mpi2.h Version: 02.00.13 + * mpi2.h Version: 02.00.28 * * Version History * --------------- @@ -53,6 +53,31 @@ * bytes reserved. * Added RAID Accelerator functionality. * 07-30-09 02.00.13 Bumped MPI2_HEADER_VERSION_UNIT. + * 10-28-09 02.00.14 Bumped MPI2_HEADER_VERSION_UNIT. + * Added MSI-x index mask and shift for Reply Post Host + * Index register. + * Added function code for Host Based Discovery Action. + * 02-10-10 02.00.15 Bumped MPI2_HEADER_VERSION_UNIT. + * Added define for MPI2_FUNCTION_PWR_MGMT_CONTROL. + * Added defines for product-specific range of message + * function codes, 0xF0 to 0xFF. + * 05-12-10 02.00.16 Bumped MPI2_HEADER_VERSION_UNIT. + * Added alternative defines for the SGE Direction bit. + * 08-11-10 02.00.17 Bumped MPI2_HEADER_VERSION_UNIT. + * 11-10-10 02.00.18 Bumped MPI2_HEADER_VERSION_UNIT. + * Added MPI2_IEEE_SGE_FLAGS_SYSTEMPLBCPI_ADDR define. + * 02-23-11 02.00.19 Bumped MPI2_HEADER_VERSION_UNIT. + * Added MPI2_FUNCTION_SEND_HOST_MESSAGE. + * 03-09-11 02.00.20 Bumped MPI2_HEADER_VERSION_UNIT. + * 05-25-11 02.00.21 Bumped MPI2_HEADER_VERSION_UNIT. + * 08-24-11 02.00.22 Bumped MPI2_HEADER_VERSION_UNIT. + * 11-18-11 02.00.23 Bumped MPI2_HEADER_VERSION_UNIT. + * 02-06-12 02.00.24 Bumped MPI2_HEADER_VERSION_UNIT. + * 03-29-12 02.00.25 Bumped MPI2_HEADER_VERSION_UNIT. + * Added Hard Reset delay timings. + * 07-10-12 02.00.26 Bumped MPI2_HEADER_VERSION_UNIT. + * 07-26-12 02.00.27 Bumped MPI2_HEADER_VERSION_UNIT. + * 11-27-12 02.00.28 Bumped MPI2_HEADER_VERSION_UNIT. * -------------------------------------------------------------------------- */ @@ -78,7 +103,7 @@ #define MPI2_VERSION_02_00 (0x0200) /* versioning for this MPI header set */ -#define MPI2_HEADER_VERSION_UNIT (0x0D) +#define MPI2_HEADER_VERSION_UNIT (0x1C) #define MPI2_HEADER_VERSION_DEV (0x00) #define MPI2_HEADER_VERSION_UNIT_MASK (0xFF00) #define MPI2_HEADER_VERSION_UNIT_SHIFT (8) @@ -232,9 +257,12 @@ typedef volatile struct _MPI2_SYSTEM_INTERFACE_REGS #define MPI2_REPLY_FREE_HOST_INDEX_OFFSET (0x00000048) /* - * Offset for the Reply Descriptor Post Queue + * Defines for the Reply Descriptor Post Queue */ #define MPI2_REPLY_POST_HOST_INDEX_OFFSET (0x0000006C) +#define MPI2_REPLY_POST_HOST_INDEX_MASK (0x00FFFFFF) +#define MPI2_RPHI_MSIX_INDEX_MASK (0xFF000000) +#define MPI2_RPHI_MSIX_INDEX_SHIFT (24) /* * Defines for the HCBSize and address @@ -253,6 +281,11 @@ typedef volatile struct _MPI2_SYSTEM_INTERFACE_REGS #define MPI2_REQUEST_DESCRIPTOR_POST_HIGH_OFFSET (0x000000C4) +/* Hard Reset delay timings */ +#define MPI2_HARD_RESET_PCIE_FIRST_READ_DELAY_MICRO_SEC (50000) +#define MPI2_HARD_RESET_PCIE_RESET_READ_WINDOW_MICRO_SEC (255000) +#define MPI2_HARD_RESET_PCIE_SECOND_READ_DELAY_MICRO_SEC (256000) + /***************************************************************************** * * Message Descriptors @@ -459,15 +492,13 @@ typedef union _MPI2_REPLY_DESCRIPTORS_UNION MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR RAIDAcceleratorSuccess; U64 Words; } MPI2_REPLY_DESCRIPTORS_UNION, MPI2_POINTER PTR_MPI2_REPLY_DESCRIPTORS_UNION, - Mpi2ReplyDescriptorsUnion_t, MPI2_POINTER pMpi2ReplyDescriptorsUnion_t; +Mpi2ReplyDescriptorsUnion_t, MPI2_POINTER pMpi2ReplyDescriptorsUnion_t; /***************************************************************************** * * Message Functions -* 0x80 -> 0x8F reserved for private message use per product -* * *****************************************************************************/ @@ -497,12 +528,22 @@ typedef union _MPI2_REPLY_DESCRIPTORS_UNION #define MPI2_FUNCTION_TARGET_CMD_BUF_BASE_POST (0x24) /* Target Command Buffer Post Base */ #define MPI2_FUNCTION_TARGET_CMD_BUF_LIST_POST (0x25) /* Target Command Buffer Post List */ #define MPI2_FUNCTION_RAID_ACCELERATOR (0x2C) /* RAID Accelerator*/ +/* Host Based Discovery Action */ +#define MPI2_FUNCTION_HOST_BASED_DISCOVERY_ACTION (0x2F) +/* Power Management Control */ +#define MPI2_FUNCTION_PWR_MGMT_CONTROL (0x30) +/* Send Host Message */ +#define MPI2_FUNCTION_SEND_HOST_MESSAGE (0x31) +/* beginning of product-specific range */ +#define MPI2_FUNCTION_MIN_PRODUCT_SPECIFIC (0xF0) +/* end of product-specific range */ +#define MPI2_FUNCTION_MAX_PRODUCT_SPECIFIC (0xFF) + /* Doorbell functions */ #define MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET (0x40) -/* #define MPI2_FUNCTION_IO_UNIT_RESET (0x41) */ #define MPI2_FUNCTION_HANDSHAKE (0x42) @@ -912,6 +953,9 @@ typedef struct _MPI2_MPI_SGE_UNION #define MPI2_SGE_FLAGS_IOC_TO_HOST (0x00) #define MPI2_SGE_FLAGS_HOST_TO_IOC (0x04) +#define MPI2_SGE_FLAGS_DEST (MPI2_SGE_FLAGS_IOC_TO_HOST) +#define MPI2_SGE_FLAGS_SOURCE (MPI2_SGE_FLAGS_HOST_TO_IOC) + /* Address Size */ #define MPI2_SGE_FLAGS_32_BIT_ADDRESSING (0x00) @@ -1037,10 +1081,16 @@ typedef struct _MPI2_IEEE_SGE_UNION #define MPI2_IEEE_SGE_FLAGS_ADDR_MASK (0x03) #define MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR (0x00) + /* IEEE Simple Element only */ #define MPI2_IEEE_SGE_FLAGS_IOCDDR_ADDR (0x01) + /* IEEE Simple Element only */ #define MPI2_IEEE_SGE_FLAGS_IOCPLB_ADDR (0x02) #define MPI2_IEEE_SGE_FLAGS_IOCPLBNTA_ADDR (0x03) - + /* IEEE Simple Element only */ +#define MPI2_IEEE_SGE_FLAGS_SYSTEMPLBPCI_ADDR (0x03) + /* IEEE Chain Element only */ +#define MPI2_IEEE_SGE_FLAGS_SYSTEMPLBCPI_ADDR \ + (MPI2_IEEE_SGE_FLAGS_SYSTEMPLBPCI_ADDR) /* typo in name */ /**************************************************************************** * IEEE SGE operation Macros diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h b/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h index 1611c57a6fd..88cb7f828bb 100644 --- a/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h +++ b/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h @@ -1,12 +1,12 @@ /* - * Copyright (c) 2000-2009 LSI Corporation. + * Copyright (c) 2000-2013 LSI Corporation. * * * Name: mpi2_cnfg.h * Title: MPI Configuration messages and pages * Creation Date: November 10, 2006 * - * mpi2_cnfg.h Version: 02.00.12 + * mpi2_cnfg.h Version: 02.00.23 * * Version History * --------------- @@ -107,6 +107,50 @@ * to SAS Device Page 0 Flags field. * Added PhyInfo defines for power condition. * Added Ethernet configuration pages. + * 10-28-09 02.00.13 Added MPI2_IOUNITPAGE1_ENABLE_HOST_BASED_DISCOVERY. + * Added SAS PHY Page 4 structure and defines. + * 02-10-10 02.00.14 Modified the comments for the configuration page + * structures that contain an array of data. The host + * should use the "count" field in the page data (e.g. the + * NumPhys field) to determine the number of valid elements + * in the array. + * Added/modified some MPI2_MFGPAGE_DEVID_SAS defines. + * Added PowerManagementCapabilities to IO Unit Page 7. + * Added PortWidthModGroup field to + * MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS. + * Added MPI2_CONFIG_PAGE_SASIOUNIT_6 and related defines. + * Added MPI2_CONFIG_PAGE_SASIOUNIT_7 and related defines. + * Added MPI2_CONFIG_PAGE_SASIOUNIT_8 and related defines. + * 05-12-10 02.00.15 Added MPI2_RAIDVOL0_STATUS_FLAG_VOL_NOT_CONSISTENT + * define. + * Added MPI2_PHYSDISK0_INCOMPATIBLE_MEDIA_TYPE define. + * Added MPI2_SAS_NEG_LINK_RATE_UNSUPPORTED_PHY define. + * 08-11-10 02.00.16 Removed IO Unit Page 1 device path (multi-pathing) + * defines. + * 11-10-10 02.00.17 Added ReceptacleID field (replacing Reserved1) to + * MPI2_MANPAGE7_CONNECTOR_INFO and reworked defines for + * the Pinout field. + * Added BoardTemperature and BoardTemperatureUnits fields + * to MPI2_CONFIG_PAGE_IO_UNIT_7. + * Added MPI2_CONFIG_EXTPAGETYPE_EXT_MANUFACTURING define + * and MPI2_CONFIG_PAGE_EXT_MAN_PS structure. + * 02-23-11 02.00.18 Added ProxyVF_ID field to MPI2_CONFIG_REQUEST. + * Added IO Unit Page 8, IO Unit Page 9, + * and IO Unit Page 10. + * Added SASNotifyPrimitiveMasks field to + * MPI2_CONFIG_PAGE_IOC_7. + * 03-09-11 02.00.19 Fixed IO Unit Page 10 (to match the spec). + * 05-25-11 02.00.20 Cleaned up a few comments. + * 08-24-11 02.00.21 Marked the IO Unit Page 7 PowerManagementCapabilities + * for PCIe link as obsolete. + * Added SpinupFlags field containing a Disable Spin-up + * bit to the MPI2_SAS_IOUNIT4_SPINUP_GROUP fields of + * SAS IO Unit Page 4. + * 11-18-11 02.00.22 Added define MPI2_IOCPAGE6_CAP_FLAGS_4K_SECTORS_SUPPORT. + * Added UEFIVersion field to BIOS Page 1 and defined new + * BiosOptions bits. + * 11-27-12 02.00.23 Added MPI2_MANPAGE7_FLAG_EVENTREPLAY_SLOT_ORDER. + * Added MPI2_BIOSPAGE1_OPTIONS_MASK_OEM_ID. * -------------------------------------------------------------------------- */ @@ -190,6 +234,7 @@ typedef union _MPI2_CONFIG_EXT_PAGE_HEADER_UNION #define MPI2_CONFIG_EXTPAGETYPE_DRIVER_MAPPING (0x17) #define MPI2_CONFIG_EXTPAGETYPE_SAS_PORT (0x18) #define MPI2_CONFIG_EXTPAGETYPE_ETHERNET (0x19) +#define MPI2_CONFIG_EXTPAGETYPE_EXT_MANUFACTURING (0x1A) /***************************************************************************** @@ -301,7 +346,9 @@ typedef struct _MPI2_CONFIG_REQUEST U8 VP_ID; /* 0x08 */ U8 VF_ID; /* 0x09 */ U16 Reserved1; /* 0x0A */ - U32 Reserved2; /* 0x0C */ + U8 Reserved2; /* 0x0C */ + U8 ProxyVF_ID; /* 0x0D */ + U16 Reserved4; /* 0x0E */ U32 Reserved3; /* 0x10 */ MPI2_CONFIG_PAGE_HEADER Header; /* 0x14 */ U32 PageAddress; /* 0x18 */ @@ -319,7 +366,7 @@ typedef struct _MPI2_CONFIG_REQUEST #define MPI2_CONFIG_ACTION_PAGE_READ_NVRAM (0x06) #define MPI2_CONFIG_ACTION_PAGE_GET_CHANGEABLE (0x07) -/* values for SGLFlags field are in the SGL section of mpi2.h */ +/* use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */ /* Config Reply Message */ @@ -365,14 +412,19 @@ typedef struct _MPI2_CONFIG_REPLY #define MPI2_MFGPAGE_DEVID_SAS2116_1 (0x0064) #define MPI2_MFGPAGE_DEVID_SAS2116_2 (0x0065) +#define MPI2_MFGPAGE_DEVID_SSS6200 (0x007E) + #define MPI2_MFGPAGE_DEVID_SAS2208_1 (0x0080) #define MPI2_MFGPAGE_DEVID_SAS2208_2 (0x0081) #define MPI2_MFGPAGE_DEVID_SAS2208_3 (0x0082) #define MPI2_MFGPAGE_DEVID_SAS2208_4 (0x0083) #define MPI2_MFGPAGE_DEVID_SAS2208_5 (0x0084) #define MPI2_MFGPAGE_DEVID_SAS2208_6 (0x0085) -#define MPI2_MFGPAGE_DEVID_SAS2208_7 (0x0086) -#define MPI2_MFGPAGE_DEVID_SAS2208_8 (0x0087) +#define MPI2_MFGPAGE_DEVID_SAS2308_1 (0x0086) +#define MPI2_MFGPAGE_DEVID_SAS2308_2 (0x0087) +#define MPI2_MFGPAGE_DEVID_SAS2308_3 (0x006E) + + /* Manufacturing Page 0 */ @@ -538,7 +590,7 @@ typedef struct _MPI2_CONFIG_PAGE_MAN_4 /* * Host code (drivers, BIOS, utilities, etc.) should leave this define set to - * one and check Header.PageLength or NumPhys at runtime. + * one and check the value returned for NumPhys at runtime. */ #ifndef MPI2_MAN_PAGE_5_PHY_ENTRIES #define MPI2_MAN_PAGE_5_PHY_ENTRIES (1) @@ -587,23 +639,31 @@ typedef struct _MPI2_MANPAGE7_CONNECTOR_INFO U32 Pinout; /* 0x00 */ U8 Connector[16]; /* 0x04 */ U8 Location; /* 0x14 */ - U8 Reserved1; /* 0x15 */ + U8 ReceptacleID; /* 0x15 */ U16 Slot; /* 0x16 */ U32 Reserved2; /* 0x18 */ } MPI2_MANPAGE7_CONNECTOR_INFO, MPI2_POINTER PTR_MPI2_MANPAGE7_CONNECTOR_INFO, Mpi2ManPage7ConnectorInfo_t, MPI2_POINTER pMpi2ManPage7ConnectorInfo_t; /* defines for the Pinout field */ -#define MPI2_MANPAGE7_PINOUT_SFF_8484_L4 (0x00080000) -#define MPI2_MANPAGE7_PINOUT_SFF_8484_L3 (0x00040000) -#define MPI2_MANPAGE7_PINOUT_SFF_8484_L2 (0x00020000) -#define MPI2_MANPAGE7_PINOUT_SFF_8484_L1 (0x00010000) -#define MPI2_MANPAGE7_PINOUT_SFF_8470_L4 (0x00000800) -#define MPI2_MANPAGE7_PINOUT_SFF_8470_L3 (0x00000400) -#define MPI2_MANPAGE7_PINOUT_SFF_8470_L2 (0x00000200) -#define MPI2_MANPAGE7_PINOUT_SFF_8470_L1 (0x00000100) -#define MPI2_MANPAGE7_PINOUT_SFF_8482 (0x00000002) -#define MPI2_MANPAGE7_PINOUT_CONNECTION_UNKNOWN (0x00000001) +#define MPI2_MANPAGE7_PINOUT_LANE_MASK (0x0000FF00) +#define MPI2_MANPAGE7_PINOUT_LANE_SHIFT (8) + +#define MPI2_MANPAGE7_PINOUT_TYPE_MASK (0x000000FF) +#define MPI2_MANPAGE7_PINOUT_TYPE_UNKNOWN (0x00) +#define MPI2_MANPAGE7_PINOUT_SATA_SINGLE (0x01) +#define MPI2_MANPAGE7_PINOUT_SFF_8482 (0x02) +#define MPI2_MANPAGE7_PINOUT_SFF_8486 (0x03) +#define MPI2_MANPAGE7_PINOUT_SFF_8484 (0x04) +#define MPI2_MANPAGE7_PINOUT_SFF_8087 (0x05) +#define MPI2_MANPAGE7_PINOUT_SFF_8643_4I (0x06) +#define MPI2_MANPAGE7_PINOUT_SFF_8643_8I (0x07) +#define MPI2_MANPAGE7_PINOUT_SFF_8470 (0x08) +#define MPI2_MANPAGE7_PINOUT_SFF_8088 (0x09) +#define MPI2_MANPAGE7_PINOUT_SFF_8644_4X (0x0A) +#define MPI2_MANPAGE7_PINOUT_SFF_8644_8X (0x0B) +#define MPI2_MANPAGE7_PINOUT_SFF_8644_16X (0x0C) +#define MPI2_MANPAGE7_PINOUT_SFF_8436 (0x0D) /* defines for the Location field */ #define MPI2_MANPAGE7_LOCATION_UNKNOWN (0x01) @@ -616,7 +676,7 @@ typedef struct _MPI2_MANPAGE7_CONNECTOR_INFO /* * Host code (drivers, BIOS, utilities, etc.) should leave this define set to - * one and check NumPhys at runtime. + * one and check the value returned for NumPhys at runtime. */ #ifndef MPI2_MANPAGE7_CONNECTOR_INFO_MAX #define MPI2_MANPAGE7_CONNECTOR_INFO_MAX (1) @@ -637,9 +697,10 @@ typedef struct _MPI2_CONFIG_PAGE_MAN_7 MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_7, Mpi2ManufacturingPage7_t, MPI2_POINTER pMpi2ManufacturingPage7_t; -#define MPI2_MANUFACTURING7_PAGEVERSION (0x00) +#define MPI2_MANUFACTURING7_PAGEVERSION (0x01) /* defines for the Flags field */ +#define MPI2_MANPAGE7_FLAG_EVENTREPLAY_SLOT_ORDER (0x00000002) #define MPI2_MANPAGE7_FLAG_USE_SLOT_INFO (0x00000001) @@ -712,7 +773,9 @@ typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_1 #define MPI2_IOUNITPAGE1_PAGEVERSION (0x04) /* IO Unit Page 1 Flags defines */ +#define MPI2_IOUNITPAGE1_ENABLE_HOST_BASED_DISCOVERY (0x00000800) #define MPI2_IOUNITPAGE1_MASK_SATA_WRITE_CACHE (0x00000600) +#define MPI2_IOUNITPAGE1_SATA_WRITE_CACHE_SHIFT (9) #define MPI2_IOUNITPAGE1_ENABLE_SATA_WRITE_CACHE (0x00000000) #define MPI2_IOUNITPAGE1_DISABLE_SATA_WRITE_CACHE (0x00000200) #define MPI2_IOUNITPAGE1_UNCHANGED_SATA_WRITE_CACHE (0x00000400) @@ -720,15 +783,13 @@ typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_1 #define MPI2_IOUNITPAGE1_DISABLE_IR (0x00000040) #define MPI2_IOUNITPAGE1_DISABLE_TASK_SET_FULL_HANDLING (0x00000020) #define MPI2_IOUNITPAGE1_IR_USE_STATIC_VOLUME_ID (0x00000004) -#define MPI2_IOUNITPAGE1_MULTI_PATHING (0x00000002) -#define MPI2_IOUNITPAGE1_SINGLE_PATHING (0x00000000) /* IO Unit Page 3 */ /* * Host code (drivers, BIOS, utilities, etc.) should leave this define set to - * one and check Header.PageLength at runtime. + * one and check the value returned for GPIOCount at runtime. */ #ifndef MPI2_IO_UNIT_PAGE_3_GPIO_VAL_MAX #define MPI2_IO_UNIT_PAGE_3_GPIO_VAL_MAX (1) @@ -757,7 +818,7 @@ typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_3 /* * Upper layer code (drivers, utilities, etc.) should leave this define set to - * one and check Header.PageLength or NumDmaEngines at runtime. + * one and check the value returned for NumDmaEngines at runtime. */ #ifndef MPI2_IOUNITPAGE5_DMAENGINE_ENTRIES #define MPI2_IOUNITPAGE5_DMAENGINE_ENTRIES (1) @@ -820,15 +881,17 @@ typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_7 { U8 PCIeWidth; /* 0x06 */ U8 PCIeSpeed; /* 0x07 */ U32 ProcessorState; /* 0x08 */ - U32 Reserved2; /* 0x0C */ + U32 PowerManagementCapabilities; /* 0x0C */ U16 IOCTemperature; /* 0x10 */ U8 IOCTemperatureUnits; /* 0x12 */ U8 IOCSpeed; /* 0x13 */ - U32 Reserved3; /* 0x14 */ + U16 BoardTemperature; /* 0x14 */ + U8 BoardTemperatureUnits; /* 0x16 */ + U8 Reserved3; /* 0x17 */ } MPI2_CONFIG_PAGE_IO_UNIT_7, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IO_UNIT_7, Mpi2IOUnitPage7_t, MPI2_POINTER pMpi2IOUnitPage7_t; -#define MPI2_IOUNITPAGE7_PAGEVERSION (0x00) +#define MPI2_IOUNITPAGE7_PAGEVERSION (0x02) /* defines for IO Unit Page 7 PCIeWidth field */ #define MPI2_IOUNITPAGE7_PCIE_WIDTH_X1 (0x01) @@ -849,6 +912,13 @@ typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_7 { #define MPI2_IOUNITPAGE7_PSTATE_DISABLED (0x01) #define MPI2_IOUNITPAGE7_PSTATE_ENABLED (0x02) +/* defines for IO Unit Page 7 PowerManagementCapabilities field */ +#define MPI2_IOUNITPAGE7_PMCAP_12_5_PCT_IOCSPEED (0x00000400) +#define MPI2_IOUNITPAGE7_PMCAP_25_0_PCT_IOCSPEED (0x00000200) +#define MPI2_IOUNITPAGE7_PMCAP_50_0_PCT_IOCSPEED (0x00000100) +#define MPI2_IOUNITPAGE7_PMCAP_PCIE_WIDTH_CHANGE (0x00000008) /* obsolete */ +#define MPI2_IOUNITPAGE7_PMCAP_PCIE_SPEED_CHANGE (0x00000004) /* obsolete */ + /* defines for IO Unit Page 7 IOCTemperatureUnits field */ #define MPI2_IOUNITPAGE7_IOC_TEMP_NOT_PRESENT (0x00) #define MPI2_IOUNITPAGE7_IOC_TEMP_FAHRENHEIT (0x01) @@ -860,6 +930,125 @@ typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_7 { #define MPI2_IOUNITPAGE7_IOC_SPEED_QUARTER (0x04) #define MPI2_IOUNITPAGE7_IOC_SPEED_EIGHTH (0x08) +/* defines for IO Unit Page 7 BoardTemperatureUnits field */ +#define MPI2_IOUNITPAGE7_BOARD_TEMP_NOT_PRESENT (0x00) +#define MPI2_IOUNITPAGE7_BOARD_TEMP_FAHRENHEIT (0x01) +#define MPI2_IOUNITPAGE7_BOARD_TEMP_CELSIUS (0x02) + +/* IO Unit Page 8 */ + +#define MPI2_IOUNIT8_NUM_THRESHOLDS (4) + +typedef struct _MPI2_IOUNIT8_SENSOR { + U16 Flags; /* 0x00 */ + U16 Reserved1; /* 0x02 */ + U16 + Threshold[MPI2_IOUNIT8_NUM_THRESHOLDS]; /* 0x04 */ + U32 Reserved2; /* 0x0C */ + U32 Reserved3; /* 0x10 */ + U32 Reserved4; /* 0x14 */ +} MPI2_IOUNIT8_SENSOR, MPI2_POINTER PTR_MPI2_IOUNIT8_SENSOR, +Mpi2IOUnit8Sensor_t, MPI2_POINTER pMpi2IOUnit8Sensor_t; + +/* defines for IO Unit Page 8 Sensor Flags field */ +#define MPI2_IOUNIT8_SENSOR_FLAGS_T3_ENABLE (0x0008) +#define MPI2_IOUNIT8_SENSOR_FLAGS_T2_ENABLE (0x0004) +#define MPI2_IOUNIT8_SENSOR_FLAGS_T1_ENABLE (0x0002) +#define MPI2_IOUNIT8_SENSOR_FLAGS_T0_ENABLE (0x0001) + +/* + * Host code (drivers, BIOS, utilities, etc.) should leave this define set to + * one and check the value returned for NumSensors at runtime. + */ +#ifndef MPI2_IOUNITPAGE8_SENSOR_ENTRIES +#define MPI2_IOUNITPAGE8_SENSOR_ENTRIES (1) +#endif + +typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_8 { + MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ + U32 Reserved1; /* 0x04 */ + U32 Reserved2; /* 0x08 */ + U8 NumSensors; /* 0x0C */ + U8 PollingInterval; /* 0x0D */ + U16 Reserved3; /* 0x0E */ + MPI2_IOUNIT8_SENSOR + Sensor[MPI2_IOUNITPAGE8_SENSOR_ENTRIES];/* 0x10 */ +} MPI2_CONFIG_PAGE_IO_UNIT_8, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IO_UNIT_8, +Mpi2IOUnitPage8_t, MPI2_POINTER pMpi2IOUnitPage8_t; + +#define MPI2_IOUNITPAGE8_PAGEVERSION (0x00) + + +/* IO Unit Page 9 */ + +typedef struct _MPI2_IOUNIT9_SENSOR { + U16 CurrentTemperature; /* 0x00 */ + U16 Reserved1; /* 0x02 */ + U8 Flags; /* 0x04 */ + U8 Reserved2; /* 0x05 */ + U16 Reserved3; /* 0x06 */ + U32 Reserved4; /* 0x08 */ + U32 Reserved5; /* 0x0C */ +} MPI2_IOUNIT9_SENSOR, MPI2_POINTER PTR_MPI2_IOUNIT9_SENSOR, +Mpi2IOUnit9Sensor_t, MPI2_POINTER pMpi2IOUnit9Sensor_t; + +/* defines for IO Unit Page 9 Sensor Flags field */ +#define MPI2_IOUNIT9_SENSOR_FLAGS_TEMP_VALID (0x01) + +/* + * Host code (drivers, BIOS, utilities, etc.) should leave this define set to + * one and check the value returned for NumSensors at runtime. + */ +#ifndef MPI2_IOUNITPAGE9_SENSOR_ENTRIES +#define MPI2_IOUNITPAGE9_SENSOR_ENTRIES (1) +#endif + +typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_9 { + MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ + U32 Reserved1; /* 0x04 */ + U32 Reserved2; /* 0x08 */ + U8 NumSensors; /* 0x0C */ + U8 Reserved4; /* 0x0D */ + U16 Reserved3; /* 0x0E */ + MPI2_IOUNIT9_SENSOR + Sensor[MPI2_IOUNITPAGE9_SENSOR_ENTRIES];/* 0x10 */ +} MPI2_CONFIG_PAGE_IO_UNIT_9, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IO_UNIT_9, +Mpi2IOUnitPage9_t, MPI2_POINTER pMpi2IOUnitPage9_t; + +#define MPI2_IOUNITPAGE9_PAGEVERSION (0x00) + + +/* IO Unit Page 10 */ + +typedef struct _MPI2_IOUNIT10_FUNCTION { + U8 CreditPercent; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U16 Reserved2; /* 0x02 */ +} MPI2_IOUNIT10_FUNCTION, MPI2_POINTER PTR_MPI2_IOUNIT10_FUNCTION, +Mpi2IOUnit10Function_t, MPI2_POINTER pMpi2IOUnit10Function_t; + +/* + * Host code (drivers, BIOS, utilities, etc.) should leave this define set to + * one and check the value returned for NumFunctions at runtime. + */ +#ifndef MPI2_IOUNITPAGE10_FUNCTION_ENTRIES +#define MPI2_IOUNITPAGE10_FUNCTION_ENTRIES (1) +#endif + +typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_10 { + MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ + U8 NumFunctions; /* 0x04 */ + U8 Reserved1; /* 0x05 */ + U16 Reserved2; /* 0x06 */ + U32 Reserved3; /* 0x08 */ + U32 Reserved4; /* 0x0C */ + MPI2_IOUNIT10_FUNCTION + Function[MPI2_IOUNITPAGE10_FUNCTION_ENTRIES];/* 0x10 */ +} MPI2_CONFIG_PAGE_IO_UNIT_10, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IO_UNIT_10, +Mpi2IOUnitPage10_t, MPI2_POINTER pMpi2IOUnitPage10_t; + +#define MPI2_IOUNITPAGE10_PAGEVERSION (0x01) + /**************************************************************************** @@ -947,9 +1136,10 @@ typedef struct _MPI2_CONFIG_PAGE_IOC_6 } MPI2_CONFIG_PAGE_IOC_6, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IOC_6, Mpi2IOCPage6_t, MPI2_POINTER pMpi2IOCPage6_t; -#define MPI2_IOCPAGE6_PAGEVERSION (0x04) +#define MPI2_IOCPAGE6_PAGEVERSION (0x05) /* defines for IOC Page 6 CapabilitiesFlags */ +#define MPI2_IOCPAGE6_CAP_FLAGS_4K_SECTORS_SUPPORT (0x00000020) #define MPI2_IOCPAGE6_CAP_FLAGS_RAID10_SUPPORT (0x00000010) #define MPI2_IOCPAGE6_CAP_FLAGS_RAID1_SUPPORT (0x00000008) #define MPI2_IOCPAGE6_CAP_FLAGS_RAID1E_SUPPORT (0x00000004) @@ -967,12 +1157,12 @@ typedef struct _MPI2_CONFIG_PAGE_IOC_7 U32 Reserved1; /* 0x04 */ U32 EventMasks[MPI2_IOCPAGE7_EVENTMASK_WORDS];/* 0x08 */ U16 SASBroadcastPrimitiveMasks; /* 0x18 */ - U16 Reserved2; /* 0x1A */ + U16 SASNotifyPrimitiveMasks; /* 0x1A */ U32 Reserved3; /* 0x1C */ } MPI2_CONFIG_PAGE_IOC_7, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IOC_7, Mpi2IOCPage7_t, MPI2_POINTER pMpi2IOCPage7_t; -#define MPI2_IOCPAGE7_PAGEVERSION (0x01) +#define MPI2_IOCPAGE7_PAGEVERSION (0x02) /* IOC Page 8 */ @@ -1020,24 +1210,32 @@ typedef struct _MPI2_CONFIG_PAGE_IOC_8 typedef struct _MPI2_CONFIG_PAGE_BIOS_1 { - MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ - U32 BiosOptions; /* 0x04 */ - U32 IOCSettings; /* 0x08 */ - U32 Reserved1; /* 0x0C */ - U32 DeviceSettings; /* 0x10 */ - U16 NumberOfDevices; /* 0x14 */ - U16 Reserved2; /* 0x16 */ - U16 IOTimeoutBlockDevicesNonRM; /* 0x18 */ - U16 IOTimeoutSequential; /* 0x1A */ - U16 IOTimeoutOther; /* 0x1C */ - U16 IOTimeoutBlockDevicesRM; /* 0x1E */ + MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ + U32 BiosOptions; /* 0x04 */ + U32 IOCSettings; /* 0x08 */ + U32 Reserved1; /* 0x0C */ + U32 DeviceSettings; /* 0x10 */ + U16 NumberOfDevices; /* 0x14 */ + U16 UEFIVersion; /* 0x16 */ + U16 IOTimeoutBlockDevicesNonRM; /* 0x18 */ + U16 IOTimeoutSequential; /* 0x1A */ + U16 IOTimeoutOther; /* 0x1C */ + U16 IOTimeoutBlockDevicesRM; /* 0x1E */ } MPI2_CONFIG_PAGE_BIOS_1, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_BIOS_1, Mpi2BiosPage1_t, MPI2_POINTER pMpi2BiosPage1_t; -#define MPI2_BIOSPAGE1_PAGEVERSION (0x04) +#define MPI2_BIOSPAGE1_PAGEVERSION (0x05) /* values for BIOS Page 1 BiosOptions field */ -#define MPI2_BIOSPAGE1_OPTIONS_DISABLE_BIOS (0x00000001) +#define MPI2_BIOSPAGE1_OPTIONS_MASK_OEM_ID (0x000000F0) +#define MPI2_BIOSPAGE1_OPTIONS_LSI_OEM_ID (0x00000000) + +#define MPI2_BIOSPAGE1_OPTIONS_MASK_UEFI_HII_REGISTRATION (0x00000006) +#define MPI2_BIOSPAGE1_OPTIONS_ENABLE_UEFI_HII (0x00000000) +#define MPI2_BIOSPAGE1_OPTIONS_DISABLE_UEFI_HII (0x00000002) +#define MPI2_BIOSPAGE1_OPTIONS_VERSION_CHECK_UEFI_HII (0x00000004) + +#define MPI2_BIOSPAGE1_OPTIONS_DISABLE_BIOS (0x00000001) /* values for BIOS Page 1 IOCSettings field */ #define MPI2_BIOSPAGE1_IOCSET_MASK_BOOT_PREFERENCE (0x00030000) @@ -1064,6 +1262,13 @@ typedef struct _MPI2_CONFIG_PAGE_BIOS_1 #define MPI2_BIOSPAGE1_DEVSET_DISABLE_NON_RM_LUN (0x00000002) #define MPI2_BIOSPAGE1_DEVSET_DISABLE_OTHER_LUN (0x00000001) +/* defines for BIOS Page 1 UEFIVersion field */ +#define MPI2_BIOSPAGE1_UEFI_VER_MAJOR_MASK (0xFF00) +#define MPI2_BIOSPAGE1_UEFI_VER_MAJOR_SHIFT (8) +#define MPI2_BIOSPAGE1_UEFI_VER_MINOR_MASK (0x00FF) +#define MPI2_BIOSPAGE1_UEFI_VER_MINOR_SHIFT (0) + + /* BIOS Page 2 */ @@ -1192,7 +1397,7 @@ typedef struct _MPI2_CONFIG_PAGE_BIOS_3 /* * Host code (drivers, BIOS, utilities, etc.) should leave this define set to - * one and check Header.PageLength or NumPhys at runtime. + * one and check the value returned for NumPhys at runtime. */ #ifndef MPI2_BIOS_PAGE_4_PHY_ENTRIES #define MPI2_BIOS_PAGE_4_PHY_ENTRIES (1) @@ -1266,7 +1471,7 @@ typedef struct _MPI2_RAIDVOL0_SETTINGS /* * Host code (drivers, BIOS, utilities, etc.) should leave this define set to - * one and check Header.PageLength at runtime. + * one and check the value returned for NumPhysDisks at runtime. */ #ifndef MPI2_RAID_VOL_PAGE_0_PHYSDISK_MAX #define MPI2_RAID_VOL_PAGE_0_PHYSDISK_MAX (1) @@ -1323,6 +1528,7 @@ typedef struct _MPI2_CONFIG_PAGE_RAID_VOL_0 #define MPI2_RAIDVOL0_STATUS_FLAG_CAPACITY_EXPANSION (0x00040000) #define MPI2_RAIDVOL0_STATUS_FLAG_BACKGROUND_INIT (0x00020000) #define MPI2_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS (0x00010000) +#define MPI2_RAIDVOL0_STATUS_FLAG_VOL_NOT_CONSISTENT (0x00000080) #define MPI2_RAIDVOL0_STATUS_FLAG_OCE_ALLOWED (0x00000040) #define MPI2_RAIDVOL0_STATUS_FLAG_BGI_COMPLETE (0x00000020) #define MPI2_RAIDVOL0_STATUS_FLAG_1E_OFFSET_MIRROR (0x00000000) @@ -1445,11 +1651,15 @@ typedef struct _MPI2_CONFIG_PAGE_RD_PDISK_0 #define MPI2_PHYSDISK0_INCOMPATIBLE_MAX_LBA (0x03) #define MPI2_PHYSDISK0_INCOMPATIBLE_SATA_EXTENDED_CMD (0x04) #define MPI2_PHYSDISK0_INCOMPATIBLE_REMOVEABLE_MEDIA (0x05) +#define MPI2_PHYSDISK0_INCOMPATIBLE_MEDIA_TYPE (0x06) #define MPI2_PHYSDISK0_INCOMPATIBLE_UNKNOWN (0xFF) /* PhysDiskAttributes defines */ +#define MPI2_PHYSDISK0_ATTRIB_MEDIA_MASK (0x0C) #define MPI2_PHYSDISK0_ATTRIB_SOLID_STATE_DRIVE (0x08) #define MPI2_PHYSDISK0_ATTRIB_HARD_DISK_DRIVE (0x04) + +#define MPI2_PHYSDISK0_ATTRIB_PROTOCOL_MASK (0x03) #define MPI2_PHYSDISK0_ATTRIB_SAS_PROTOCOL (0x02) #define MPI2_PHYSDISK0_ATTRIB_SATA_PROTOCOL (0x01) @@ -1468,7 +1678,7 @@ typedef struct _MPI2_CONFIG_PAGE_RD_PDISK_0 /* * Host code (drivers, BIOS, utilities, etc.) should leave this define set to - * one and check Header.PageLength or NumPhysDiskPaths at runtime. + * one and check the value returned for NumPhysDiskPaths at runtime. */ #ifndef MPI2_RAID_PHYS_DISK1_PATH_MAX #define MPI2_RAID_PHYS_DISK1_PATH_MAX (1) @@ -1521,6 +1731,7 @@ typedef struct _MPI2_CONFIG_PAGE_RD_PDISK_1 #define MPI2_SAS_NEG_LINK_RATE_SATA_OOB_COMPLETE (0x03) #define MPI2_SAS_NEG_LINK_RATE_PORT_SELECTOR (0x04) #define MPI2_SAS_NEG_LINK_RATE_SMP_RESET_IN_PROGRESS (0x05) +#define MPI2_SAS_NEG_LINK_RATE_UNSUPPORTED_PHY (0x06) #define MPI2_SAS_NEG_LINK_RATE_1_5 (0x08) #define MPI2_SAS_NEG_LINK_RATE_3_0 (0x09) #define MPI2_SAS_NEG_LINK_RATE_6_0 (0x0A) @@ -1547,6 +1758,7 @@ typedef struct _MPI2_CONFIG_PAGE_RD_PDISK_1 #define MPI2_SAS_PHYINFO_PHY_VACANT (0x80000000) #define MPI2_SAS_PHYINFO_PHY_POWER_CONDITION_MASK (0x18000000) +#define MPI2_SAS_PHYINFO_SHIFT_PHY_POWER_CONDITION (27) #define MPI2_SAS_PHYINFO_PHY_POWER_ACTIVE (0x00000000) #define MPI2_SAS_PHYINFO_PHY_POWER_PARTIAL (0x08000000) #define MPI2_SAS_PHYINFO_PHY_POWER_SLUMBER (0x10000000) @@ -1630,7 +1842,7 @@ typedef struct _MPI2_SAS_IO_UNIT0_PHY_DATA /* * Host code (drivers, BIOS, utilities, etc.) should leave this define set to - * one and check Header.ExtPageLength or NumPhys at runtime. + * one and check the value returned for NumPhys at runtime. */ #ifndef MPI2_SAS_IOUNIT0_PHY_MAX #define MPI2_SAS_IOUNIT0_PHY_MAX (1) @@ -1701,7 +1913,7 @@ typedef struct _MPI2_SAS_IO_UNIT1_PHY_DATA /* * Host code (drivers, BIOS, utilities, etc.) should leave this define set to - * one and check Header.ExtPageLength or NumPhys at runtime. + * one and check the value returned for NumPhys at runtime. */ #ifndef MPI2_SAS_IOUNIT1_PHY_MAX #define MPI2_SAS_IOUNIT1_PHY_MAX (1) @@ -1786,13 +1998,17 @@ typedef struct _MPI2_SAS_IOUNIT4_SPINUP_GROUP { U8 MaxTargetSpinup; /* 0x00 */ U8 SpinupDelay; /* 0x01 */ - U16 Reserved1; /* 0x02 */ + U8 SpinupFlags; /* 0x02 */ + U8 Reserved1; /* 0x03 */ } MPI2_SAS_IOUNIT4_SPINUP_GROUP, MPI2_POINTER PTR_MPI2_SAS_IOUNIT4_SPINUP_GROUP, Mpi2SasIOUnit4SpinupGroup_t, MPI2_POINTER pMpi2SasIOUnit4SpinupGroup_t; +/* defines for SAS IO Unit Page 4 SpinupFlags */ +#define MPI2_SASIOUNIT4_SPINUP_DISABLE_FLAG (0x01) + /* * Host code (drivers, BIOS, utilities, etc.) should leave this define set to - * four and check Header.ExtPageLength or NumPhys at runtime. + * one and check the value returned for NumPhys at runtime. */ #ifndef MPI2_SAS_IOUNIT4_PHY_MAX #define MPI2_SAS_IOUNIT4_PHY_MAX (4) @@ -1830,7 +2046,7 @@ typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_4 typedef struct _MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS { U8 ControlFlags; /* 0x00 */ - U8 Reserved1; /* 0x01 */ + U8 PortWidthModGroup; /* 0x01 */ U16 InactivityTimerExponent; /* 0x02 */ U8 SATAPartialTimeout; /* 0x04 */ U8 Reserved2; /* 0x05 */ @@ -1850,6 +2066,9 @@ typedef struct _MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS { #define MPI2_SASIOUNIT5_CONTROL_SATA_SLUMBER_ENABLE (0x02) #define MPI2_SASIOUNIT5_CONTROL_SATA_PARTIAL_ENABLE (0x01) +/* defines for PortWidthModeGroup field */ +#define MPI2_SASIOUNIT5_PWMG_DISABLE (0xFF) + /* defines for InactivityTimerExponent field */ #define MPI2_SASIOUNIT5_ITE_MASK_SAS_SLUMBER (0x7000) #define MPI2_SASIOUNIT5_ITE_SHIFT_SAS_SLUMBER (12) @@ -1871,7 +2090,7 @@ typedef struct _MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS { /* * Host code (drivers, BIOS, utilities, etc.) should leave this define set to - * one and check Header.ExtPageLength or NumPhys at runtime. + * one and check the value returned for NumPhys at runtime. */ #ifndef MPI2_SAS_IOUNIT5_PHY_MAX #define MPI2_SAS_IOUNIT5_PHY_MAX (1) @@ -1889,9 +2108,155 @@ typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_5 { MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SASIOUNIT_5, Mpi2SasIOUnitPage5_t, MPI2_POINTER pMpi2SasIOUnitPage5_t; -#define MPI2_SASIOUNITPAGE5_PAGEVERSION (0x00) +#define MPI2_SASIOUNITPAGE5_PAGEVERSION (0x01) + + +/* SAS IO Unit Page 6 */ + +typedef struct _MPI2_SAS_IO_UNIT6_PORT_WIDTH_MOD_GROUP_STATUS { + U8 CurrentStatus; /* 0x00 */ + U8 CurrentModulation; /* 0x01 */ + U8 CurrentUtilization; /* 0x02 */ + U8 Reserved1; /* 0x03 */ + U32 Reserved2; /* 0x04 */ +} MPI2_SAS_IO_UNIT6_PORT_WIDTH_MOD_GROUP_STATUS, + MPI2_POINTER PTR_MPI2_SAS_IO_UNIT6_PORT_WIDTH_MOD_GROUP_STATUS, + Mpi2SasIOUnit6PortWidthModGroupStatus_t, + MPI2_POINTER pMpi2SasIOUnit6PortWidthModGroupStatus_t; + +/* defines for CurrentStatus field */ +#define MPI2_SASIOUNIT6_STATUS_UNAVAILABLE (0x00) +#define MPI2_SASIOUNIT6_STATUS_UNCONFIGURED (0x01) +#define MPI2_SASIOUNIT6_STATUS_INVALID_CONFIG (0x02) +#define MPI2_SASIOUNIT6_STATUS_LINK_DOWN (0x03) +#define MPI2_SASIOUNIT6_STATUS_OBSERVATION_ONLY (0x04) +#define MPI2_SASIOUNIT6_STATUS_INACTIVE (0x05) +#define MPI2_SASIOUNIT6_STATUS_ACTIVE_IOUNIT (0x06) +#define MPI2_SASIOUNIT6_STATUS_ACTIVE_HOST (0x07) + +/* defines for CurrentModulation field */ +#define MPI2_SASIOUNIT6_MODULATION_25_PERCENT (0x00) +#define MPI2_SASIOUNIT6_MODULATION_50_PERCENT (0x01) +#define MPI2_SASIOUNIT6_MODULATION_75_PERCENT (0x02) +#define MPI2_SASIOUNIT6_MODULATION_100_PERCENT (0x03) +/* + * Host code (drivers, BIOS, utilities, etc.) should leave this define set to + * one and check the value returned for NumGroups at runtime. + */ +#ifndef MPI2_SAS_IOUNIT6_GROUP_MAX +#define MPI2_SAS_IOUNIT6_GROUP_MAX (1) +#endif + +typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_6 { + MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ + U32 Reserved1; /* 0x08 */ + U32 Reserved2; /* 0x0C */ + U8 NumGroups; /* 0x10 */ + U8 Reserved3; /* 0x11 */ + U16 Reserved4; /* 0x12 */ + MPI2_SAS_IO_UNIT6_PORT_WIDTH_MOD_GROUP_STATUS + PortWidthModulationGroupStatus[MPI2_SAS_IOUNIT6_GROUP_MAX]; /* 0x14 */ +} MPI2_CONFIG_PAGE_SASIOUNIT_6, + MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SASIOUNIT_6, + Mpi2SasIOUnitPage6_t, MPI2_POINTER pMpi2SasIOUnitPage6_t; +#define MPI2_SASIOUNITPAGE6_PAGEVERSION (0x00) + + +/* SAS IO Unit Page 7 */ + +typedef struct _MPI2_SAS_IO_UNIT7_PORT_WIDTH_MOD_GROUP_SETTINGS { + U8 Flags; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U16 Reserved2; /* 0x02 */ + U8 Threshold75Pct; /* 0x04 */ + U8 Threshold50Pct; /* 0x05 */ + U8 Threshold25Pct; /* 0x06 */ + U8 Reserved3; /* 0x07 */ +} MPI2_SAS_IO_UNIT7_PORT_WIDTH_MOD_GROUP_SETTINGS, + MPI2_POINTER PTR_MPI2_SAS_IO_UNIT7_PORT_WIDTH_MOD_GROUP_SETTINGS, + Mpi2SasIOUnit7PortWidthModGroupSettings_t, + MPI2_POINTER pMpi2SasIOUnit7PortWidthModGroupSettings_t; + +/* defines for Flags field */ +#define MPI2_SASIOUNIT7_FLAGS_ENABLE_PORT_WIDTH_MODULATION (0x01) + + +/* + * Host code (drivers, BIOS, utilities, etc.) should leave this define set to + * one and check the value returned for NumGroups at runtime. + */ +#ifndef MPI2_SAS_IOUNIT7_GROUP_MAX +#define MPI2_SAS_IOUNIT7_GROUP_MAX (1) +#endif + +typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_7 { + MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ + U8 SamplingInterval; /* 0x08 */ + U8 WindowLength; /* 0x09 */ + U16 Reserved1; /* 0x0A */ + U32 Reserved2; /* 0x0C */ + U32 Reserved3; /* 0x10 */ + U8 NumGroups; /* 0x14 */ + U8 Reserved4; /* 0x15 */ + U16 Reserved5; /* 0x16 */ + MPI2_SAS_IO_UNIT7_PORT_WIDTH_MOD_GROUP_SETTINGS + PortWidthModulationGroupSettings[MPI2_SAS_IOUNIT7_GROUP_MAX]; /* 0x18 */ +} MPI2_CONFIG_PAGE_SASIOUNIT_7, + MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SASIOUNIT_7, + Mpi2SasIOUnitPage7_t, MPI2_POINTER pMpi2SasIOUnitPage7_t; + +#define MPI2_SASIOUNITPAGE7_PAGEVERSION (0x00) + + +/* SAS IO Unit Page 8 */ + +typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_8 { + MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ + U32 Reserved1; /* 0x08 */ + U32 PowerManagementCapabilities;/* 0x0C */ + U32 Reserved2; /* 0x10 */ +} MPI2_CONFIG_PAGE_SASIOUNIT_8, + MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SASIOUNIT_8, + Mpi2SasIOUnitPage8_t, MPI2_POINTER pMpi2SasIOUnitPage8_t; + +#define MPI2_SASIOUNITPAGE8_PAGEVERSION (0x00) + +/* defines for PowerManagementCapabilities field */ +#define MPI2_SASIOUNIT8_PM_HOST_PORT_WIDTH_MOD (0x00001000) +#define MPI2_SASIOUNIT8_PM_HOST_SAS_SLUMBER_MODE (0x00000800) +#define MPI2_SASIOUNIT8_PM_HOST_SAS_PARTIAL_MODE (0x00000400) +#define MPI2_SASIOUNIT8_PM_HOST_SATA_SLUMBER_MODE (0x00000200) +#define MPI2_SASIOUNIT8_PM_HOST_SATA_PARTIAL_MODE (0x00000100) +#define MPI2_SASIOUNIT8_PM_IOUNIT_PORT_WIDTH_MOD (0x00000010) +#define MPI2_SASIOUNIT8_PM_IOUNIT_SAS_SLUMBER_MODE (0x00000008) +#define MPI2_SASIOUNIT8_PM_IOUNIT_SAS_PARTIAL_MODE (0x00000004) +#define MPI2_SASIOUNIT8_PM_IOUNIT_SATA_SLUMBER_MODE (0x00000002) +#define MPI2_SASIOUNIT8_PM_IOUNIT_SATA_PARTIAL_MODE (0x00000001) + + + +/* SAS IO Unit Page 16 */ + +typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT16 { + MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ + U64 TimeStamp; /* 0x08 */ + U32 Reserved1; /* 0x10 */ + U32 Reserved2; /* 0x14 */ + U32 FastPathPendedRequests; /* 0x18 */ + U32 FastPathUnPendedRequests; /* 0x1C */ + U32 FastPathHostRequestStarts; /* 0x20 */ + U32 FastPathFirmwareRequestStarts; /* 0x24 */ + U32 FastPathHostCompletions; /* 0x28 */ + U32 FastPathFirmwareCompletions; /* 0x2C */ + U32 NonFastPathRequestStarts; /* 0x30 */ + U32 NonFastPathHostCompletions; /* 0x30 */ +} MPI2_CONFIG_PAGE_SASIOUNIT16, +MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SASIOUNIT16, +Mpi2SasIOUnitPage16_t, MPI2_POINTER pMpi2SasIOUnitPage16_t; + +#define MPI2_SASIOUNITPAGE16_PAGEVERSION (0x00) /**************************************************************************** @@ -2009,13 +2374,12 @@ typedef struct _MPI2_CONFIG_PAGE_EXPANDER_1 /* use MPI2_SAS_NEG_LINK_RATE_ defines for the NegotiatedLinkRate field */ -/* use MPI2_SAS_APHYINFO_ defines for AttachedPhyInfo field */ - /* values for SAS Expander Page 1 DiscoveryInfo field */ #define MPI2_SAS_EXPANDER1_DISCINFO_BAD_PHY_DISABLED (0x04) #define MPI2_SAS_EXPANDER1_DISCINFO_LINK_STATUS_CHANGE (0x02) #define MPI2_SAS_EXPANDER1_DISCINFO_NO_ROUTING_ENTRIES (0x01) +/* use MPI2_SAS_APHYINFO_ defines for AttachedPhyInfo field */ /**************************************************************************** * SAS Device Config Pages @@ -2076,6 +2440,7 @@ typedef struct _MPI2_CONFIG_PAGE_SAS_DEV_0 /* see mpi2_sas.h for values for SAS Device Page 0 DeviceInfo values */ /* values for SAS Device Page 0 Flags field */ +#define MPI2_SAS_DEVICE0_FLAGS_UNAUTHORIZED_DEVICE (0x8000) #define MPI2_SAS_DEVICE0_FLAGS_SLUMBER_PM_CAPABLE (0x1000) #define MPI2_SAS_DEVICE0_FLAGS_PARTIAL_PM_CAPABLE (0x0800) #define MPI2_SAS_DEVICE0_FLAGS_SATA_ASYNCHRONOUS_NOTIFY (0x0400) @@ -2134,6 +2499,8 @@ typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_0 #define MPI2_SASPHY0_PAGEVERSION (0x03) +/* use MPI2_SAS_APHYINFO_ defines for AttachedPhyInfo field */ + /* use MPI2_SAS_PRATE_ defines for the ProgrammedLinkRate field */ /* use MPI2_SAS_HWRATE_ defines for the HwLinkRate field */ @@ -2141,12 +2508,10 @@ typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_0 /* values for SAS PHY Page 0 Flags field */ #define MPI2_SAS_PHY0_FLAGS_SGPIO_DIRECT_ATTACH_ENC (0x01) -/* use MPI2_SAS_APHYINFO_ defines for AttachedPhyInfo field */ +/* use MPI2_SAS_PHYINFO_ for the PhyInfo field */ /* use MPI2_SAS_NEG_LINK_RATE_ defines for the NegotiatedLinkRate field */ -/* use MPI2_SAS_PHYINFO_ for the PhyInfo field */ - /* SAS PHY Page 1 */ @@ -2179,7 +2544,7 @@ typedef struct _MPI2_SASPHY2_PHY_EVENT { /* * Host code (drivers, BIOS, utilities, etc.) should leave this define set to - * one and check Header.ExtPageLength or NumPhyEvents at runtime. + * one and check the value returned for NumPhyEvents at runtime. */ #ifndef MPI2_SASPHY2_PHY_EVENT_MAX #define MPI2_SASPHY2_PHY_EVENT_MAX (1) @@ -2271,7 +2636,7 @@ typedef struct _MPI2_SASPHY3_PHY_EVENT_CONFIG { /* * Host code (drivers, BIOS, utilities, etc.) should leave this define set to - * one and check Header.ExtPageLength or NumPhyEvents at runtime. + * one and check the value returned for NumPhyEvents at runtime. */ #ifndef MPI2_SASPHY3_PHY_EVENT_MAX #define MPI2_SASPHY3_PHY_EVENT_MAX (1) @@ -2291,6 +2656,26 @@ typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_3 { #define MPI2_SASPHY3_PAGEVERSION (0x00) +/* SAS PHY Page 4 */ + +typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_4 { + MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ + U16 Reserved1; /* 0x08 */ + U8 Reserved2; /* 0x0A */ + U8 Flags; /* 0x0B */ + U8 InitialFrame[28]; /* 0x0C */ +} MPI2_CONFIG_PAGE_SAS_PHY_4, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SAS_PHY_4, + Mpi2SasPhyPage4_t, MPI2_POINTER pMpi2SasPhyPage4_t; + +#define MPI2_SASPHY4_PAGEVERSION (0x00) + +/* values for the Flags field */ +#define MPI2_SASPHY4_FLAGS_FRAME_VALID (0x02) +#define MPI2_SASPHY4_FLAGS_SATA_FRAME (0x01) + + + + /**************************************************************************** * SAS Port Config Pages ****************************************************************************/ @@ -2362,7 +2747,7 @@ typedef struct _MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0 /* * Host code (drivers, BIOS, utilities, etc.) should leave this define set to - * one and check Header.ExtPageLength or NumPhys at runtime. + * one and check the value returned for NumLogEntries at runtime. */ #ifndef MPI2_LOG_0_NUM_LOG_ENTRIES #define MPI2_LOG_0_NUM_LOG_ENTRIES (1) @@ -2412,7 +2797,7 @@ typedef struct _MPI2_CONFIG_PAGE_LOG_0 /* * Host code (drivers, BIOS, utilities, etc.) should leave this define set to - * one and check Header.ExtPageLength or NumPhys at runtime. + * one and check the value returned for NumElements at runtime. */ #ifndef MPI2_RAIDCONFIG0_MAX_ELEMENTS #define MPI2_RAIDCONFIG0_MAX_ELEMENTS (1) @@ -2611,5 +2996,25 @@ typedef struct _MPI2_CONFIG_PAGE_ETHERNET_1 { #define MPI2_ETHPG1_MS_DATA_RATE_1GBIT (0x03) +/**************************************************************************** +* Extended Manufacturing Config Pages +****************************************************************************/ + +/* + * Generic structure to use for product-specific extended manufacturing pages + * (currently Extended Manufacturing Page 40 through Extended Manufacturing + * Page 60). + */ + +typedef struct _MPI2_CONFIG_PAGE_EXT_MAN_PS { + MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ + U32 ProductSpecificInfo; /* 0x08 */ +} MPI2_CONFIG_PAGE_EXT_MAN_PS, + MPI2_POINTER PTR_MPI2_CONFIG_PAGE_EXT_MAN_PS, + Mpi2ExtManufacturingPagePS_t, + MPI2_POINTER pMpi2ExtManufacturingPagePS_t; + +/* PageVersion should be provided by product-specific code */ + #endif diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_history.txt b/drivers/scsi/mpt2sas/mpi/mpi2_history.txt deleted file mode 100644 index 65fcaa31cb3..00000000000 --- a/drivers/scsi/mpt2sas/mpi/mpi2_history.txt +++ /dev/null @@ -1,334 +0,0 @@ - ============================== - Fusion-MPT MPI 2.0 Header File Change History - ============================== - - Copyright (c) 2000-2009 LSI Corporation. - - --------------------------------------- - Header Set Release Version: 02.00.12 - Header Set Release Date: 05-06-09 - --------------------------------------- - - Filename Current version Prior version - ---------- --------------- ------------- - mpi2.h 02.00.12 02.00.11 - mpi2_cnfg.h 02.00.11 02.00.10 - mpi2_init.h 02.00.07 02.00.06 - mpi2_ioc.h 02.00.11 02.00.10 - mpi2_raid.h 02.00.03 02.00.03 - mpi2_sas.h 02.00.02 02.00.02 - mpi2_targ.h 02.00.03 02.00.03 - mpi2_tool.h 02.00.03 02.00.02 - mpi2_type.h 02.00.00 02.00.00 - mpi2_ra.h 02.00.00 - mpi2_history.txt 02.00.11 02.00.12 - - - * Date Version Description - * -------- -------- ------------------------------------------------------ - -mpi2.h - * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. - * 06-04-07 02.00.01 Bumped MPI2_HEADER_VERSION_UNIT. - * 06-26-07 02.00.02 Bumped MPI2_HEADER_VERSION_UNIT. - * 08-31-07 02.00.03 Bumped MPI2_HEADER_VERSION_UNIT. - * Moved ReplyPostHostIndex register to offset 0x6C of the - * MPI2_SYSTEM_INTERFACE_REGS and modified the define for - * MPI2_REPLY_POST_HOST_INDEX_OFFSET. - * Added union of request descriptors. - * Added union of reply descriptors. - * 10-31-07 02.00.04 Bumped MPI2_HEADER_VERSION_UNIT. - * Added define for MPI2_VERSION_02_00. - * Fixed the size of the FunctionDependent5 field in the - * MPI2_DEFAULT_REPLY structure. - * 12-18-07 02.00.05 Bumped MPI2_HEADER_VERSION_UNIT. - * Removed the MPI-defined Fault Codes and extended the - * product specific codes up to 0xEFFF. - * Added a sixth key value for the WriteSequence register - * and changed the flush value to 0x0. - * Added message function codes for Diagnostic Buffer Post - * and Diagnsotic Release. - * New IOCStatus define: MPI2_IOCSTATUS_DIAGNOSTIC_RELEASED - * Moved MPI2_VERSION_UNION from mpi2_ioc.h. - * 02-29-08 02.00.06 Bumped MPI2_HEADER_VERSION_UNIT. - * 03-03-08 02.00.07 Bumped MPI2_HEADER_VERSION_UNIT. - * 05-21-08 02.00.08 Bumped MPI2_HEADER_VERSION_UNIT. - * Added #defines for marking a reply descriptor as unused. - * 06-27-08 02.00.09 Bumped MPI2_HEADER_VERSION_UNIT. - * 10-02-08 02.00.10 Bumped MPI2_HEADER_VERSION_UNIT. - * Moved LUN field defines from mpi2_init.h. - * 01-19-09 02.00.11 Bumped MPI2_HEADER_VERSION_UNIT. - * 05-06-09 02.00.12 Bumped MPI2_HEADER_VERSION_UNIT. - * In all request and reply descriptors, replaced VF_ID - * field with MSIxIndex field. - * Removed DevHandle field from - * MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR and made those - * bytes reserved. - * Added RAID Accelerator functionality. - * -------------------------------------------------------------------------- - -mpi2_cnfg.h - * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. - * 06-04-07 02.00.01 Added defines for SAS IO Unit Page 2 PhyFlags. - * Added Manufacturing Page 11. - * Added MPI2_SAS_EXPANDER0_FLAGS_CONNECTOR_END_DEVICE - * define. - * 06-26-07 02.00.02 Adding generic structure for product-specific - * Manufacturing pages: MPI2_CONFIG_PAGE_MANUFACTURING_PS. - * Rework of BIOS Page 2 configuration page. - * Fixed MPI2_BIOSPAGE2_BOOT_DEVICE to be a union of the - * forms. - * Added configuration pages IOC Page 8 and Driver - * Persistent Mapping Page 0. - * 08-31-07 02.00.03 Modified configuration pages dealing with Integrated - * RAID (Manufacturing Page 4, RAID Volume Pages 0 and 1, - * RAID Physical Disk Pages 0 and 1, RAID Configuration - * Page 0). - * Added new value for AccessStatus field of SAS Device - * Page 0 (_SATA_NEEDS_INITIALIZATION). - * 10-31-07 02.00.04 Added missing SEPDevHandle field to - * MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0. - * 12-18-07 02.00.05 Modified IO Unit Page 0 to use 32-bit version fields for - * NVDATA. - * Modified IOC Page 7 to use masks and added field for - * SASBroadcastPrimitiveMasks. - * Added MPI2_CONFIG_PAGE_BIOS_4. - * Added MPI2_CONFIG_PAGE_LOG_0. - * 02-29-08 02.00.06 Modified various names to make them 32-character unique. - * Added SAS Device IDs. - * Updated Integrated RAID configuration pages including - * Manufacturing Page 4, IOC Page 6, and RAID Configuration - * Page 0. - * 05-21-08 02.00.07 Added define MPI2_MANPAGE4_MIX_SSD_SAS_SATA. - * Added define MPI2_MANPAGE4_PHYSDISK_128MB_COERCION. - * Fixed define MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING. - * Added missing MaxNumRoutedSasAddresses field to - * MPI2_CONFIG_PAGE_EXPANDER_0. - * Added SAS Port Page 0. - * Modified structure layout for - * MPI2_CONFIG_PAGE_DRIVER_MAPPING_0. - * 06-27-08 02.00.08 Changed MPI2_CONFIG_PAGE_RD_PDISK_1 to use - * MPI2_RAID_PHYS_DISK1_PATH_MAX to size the array. - * 10-02-08 02.00.09 Changed MPI2_RAID_PGAD_CONFIGNUM_MASK from 0x0000FFFF - * to 0x000000FF. - * Added two new values for the Physical Disk Coercion Size - * bits in the Flags field of Manufacturing Page 4. - * Added product-specific Manufacturing pages 16 to 31. - * Modified Flags bits for controlling write cache on SATA - * drives in IO Unit Page 1. - * Added new bit to AdditionalControlFlags of SAS IO Unit - * Page 1 to control Invalid Topology Correction. - * Added SupportedPhysDisks field to RAID Volume Page 1 and - * added related defines. - * Added additional defines for RAID Volume Page 0 - * VolumeStatusFlags field. - * Modified meaning of RAID Volume Page 0 VolumeSettings - * define for auto-configure of hot-swap drives. - * Added PhysDiskAttributes field (and related defines) to - * RAID Physical Disk Page 0. - * Added MPI2_SAS_PHYINFO_PHY_VACANT define. - * Added three new DiscoveryStatus bits for SAS IO Unit - * Page 0 and SAS Expander Page 0. - * Removed multiplexing information from SAS IO Unit pages. - * Added BootDeviceWaitTime field to SAS IO Unit Page 4. - * Removed Zone Address Resolved bit from PhyInfo and from - * Expander Page 0 Flags field. - * Added two new AccessStatus values to SAS Device Page 0 - * for indicating routing problems. Added 3 reserved words - * to this page. - * 01-19-09 02.00.10 Fixed defines for GPIOVal field of IO Unit Page 3. - * Inserted missing reserved field into structure for IOC - * Page 6. - * Added more pending task bits to RAID Volume Page 0 - * VolumeStatusFlags defines. - * Added MPI2_PHYSDISK0_STATUS_FLAG_NOT_CERTIFIED define. - * Added a new DiscoveryStatus bit for SAS IO Unit Page 0 - * and SAS Expander Page 0 to flag a downstream initiator - * when in simplified routing mode. - * Removed SATA Init Failure defines for DiscoveryStatus - * fields of SAS IO Unit Page 0 and SAS Expander Page 0. - * Added MPI2_SAS_DEVICE0_ASTATUS_DEVICE_BLOCKED define. - * Added PortGroups, DmaGroup, and ControlGroup fields to - * SAS Device Page 0. - * 05-06-09 02.00.11 Added structures and defines for IO Unit Page 5 and IO - * Unit Page 6. - * Added expander reduced functionality data to SAS - * Expander Page 0. - * Added SAS PHY Page 2 and SAS PHY Page 3. - * -------------------------------------------------------------------------- - -mpi2_init.h - * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. - * 10-31-07 02.00.01 Fixed name for pMpi2SCSITaskManagementRequest_t. - * 12-18-07 02.00.02 Modified Task Management Target Reset Method defines. - * 02-29-08 02.00.03 Added Query Task Set and Query Unit Attention. - * 03-03-08 02.00.04 Fixed name of struct _MPI2_SCSI_TASK_MANAGE_REPLY. - * 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 - * common to many structures. - * 05-06-09 02.00.07 Changed task management type of Query Unit Attention to - * Query Asynchronous Event. - * Defined two new bits in the SlotStatus field of the SCSI - * Enclosure Processor Request and Reply. - * -------------------------------------------------------------------------- - -mpi2_ioc.h - * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. - * 06-04-07 02.00.01 In IOCFacts Reply structure, renamed MaxDevices to - * MaxTargets. - * Added TotalImageSize field to FWDownload Request. - * Added reserved words to FWUpload Request. - * 06-26-07 02.00.02 Added IR Configuration Change List Event. - * 08-31-07 02.00.03 Removed SystemReplyQueueDepth field from the IOCInit - * request and replaced it with - * ReplyDescriptorPostQueueDepth and ReplyFreeQueueDepth. - * Replaced the MinReplyQueueDepth field of the IOCFacts - * reply with MaxReplyDescriptorPostQueueDepth. - * Added MPI2_RDPQ_DEPTH_MIN define to specify the minimum - * depth for the Reply Descriptor Post Queue. - * Added SASAddress field to Initiator Device Table - * Overflow Event data. - * 10-31-07 02.00.04 Added ReasonCode MPI2_EVENT_SAS_INIT_RC_NOT_RESPONDING - * for SAS Initiator Device Status Change Event data. - * Modified Reason Code defines for SAS Topology Change - * List Event data, including adding a bit for PHY Vacant - * status, and adding a mask for the Reason Code. - * Added define for - * MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING. - * Added define for MPI2_EXT_IMAGE_TYPE_MEGARAID. - * 12-18-07 02.00.05 Added Boot Status defines for the IOCExceptions field of - * the IOCFacts Reply. - * Removed MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER define. - * Moved MPI2_VERSION_UNION to mpi2.h. - * Changed MPI2_EVENT_NOTIFICATION_REQUEST to use masks - * instead of enables, and added SASBroadcastPrimitiveMasks - * field. - * Added Log Entry Added Event and related structure. - * 02-29-08 02.00.06 Added define MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID. - * Removed define MPI2_IOCFACTS_PROTOCOL_SMP_TARGET. - * Added MaxVolumes and MaxPersistentEntries fields to - * IOCFacts reply. - * Added ProtocalFlags and IOCCapabilities fields to - * MPI2_FW_IMAGE_HEADER. - * Removed MPI2_PORTENABLE_FLAGS_ENABLE_SINGLE_PORT. - * 03-03-08 02.00.07 Fixed MPI2_FW_IMAGE_HEADER by changing Reserved26 to - * a U16 (from a U32). - * Removed extra 's' from EventMasks name. - * 06-27-08 02.00.08 Fixed an offset in a comment. - * 10-02-08 02.00.09 Removed SystemReplyFrameSize from MPI2_IOC_INIT_REQUEST. - * Removed CurReplyFrameSize from MPI2_IOC_FACTS_REPLY and - * renamed MinReplyFrameSize to ReplyFrameSize. - * Added MPI2_IOCFACTS_EXCEPT_IR_FOREIGN_CONFIG_MAX. - * Added two new RAIDOperation values for Integrated RAID - * Operations Status Event data. - * Added four new IR Configuration Change List Event data - * ReasonCode values. - * Added two new ReasonCode defines for SAS Device Status - * Change Event data. - * Added three new DiscoveryStatus bits for the SAS - * Discovery event data. - * Added Multiplexing Status Change bit to the PhyStatus - * field of the SAS Topology Change List event data. - * Removed define for MPI2_INIT_IMAGE_BOOTFLAGS_XMEMCOPY. - * BootFlags are now product-specific. - * Added defines for the indivdual signature bytes - * for MPI2_INIT_IMAGE_FOOTER. - * 01-19-09 02.00.10 Added MPI2_IOCFACTS_CAPABILITY_EVENT_REPLAY define. - * Added MPI2_EVENT_SAS_DISC_DS_DOWNSTREAM_INITIATOR - * define. - * Added MPI2_EVENT_SAS_DEV_STAT_RC_SATA_INIT_FAILURE - * define. - * Removed MPI2_EVENT_SAS_DISC_DS_SATA_INIT_FAILURE define. - * 05-06-09 02.00.11 Added MPI2_IOCFACTS_CAPABILITY_RAID_ACCELERATOR define. - * Added MPI2_IOCFACTS_CAPABILITY_MSI_X_INDEX define. - * Added two new reason codes for SAS Device Status Change - * Event. - * Added new event: SAS PHY Counter. - * -------------------------------------------------------------------------- - -mpi2_raid.h - * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. - * 08-31-07 02.00.01 Modifications to RAID Action request and reply, - * including the Actions and ActionData. - * 02-29-08 02.00.02 Added MPI2_RAID_ACTION_ADATA_DISABL_FULL_REBUILD. - * 05-21-08 02.00.03 Added MPI2_RAID_VOL_CREATION_NUM_PHYSDISKS so that - * the PhysDisk array in MPI2_RAID_VOLUME_CREATION_STRUCT - * can be sized by the build environment. - * -------------------------------------------------------------------------- - -mpi2_sas.h - * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. - * 06-26-07 02.00.01 Added Clear All Persistent Operation to SAS IO Unit - * Control Request. - * 10-02-08 02.00.02 Added Set IOC Parameter Operation to SAS IO Unit Control - * Request. - * -------------------------------------------------------------------------- - -mpi2_targ.h - * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. - * 08-31-07 02.00.01 Added Command Buffer Data Location Address Space bits to - * BufferPostFlags field of CommandBufferPostBase Request. - * 02-29-08 02.00.02 Modified various names to make them 32-character unique. - * 10-02-08 02.00.03 Removed NextCmdBufferOffset from - * MPI2_TARGET_CMD_BUF_POST_BASE_REQUEST. - * Target Status Send Request only takes a single SGE for - * response data. - * -------------------------------------------------------------------------- - -mpi2_tool.h - * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. - * 12-18-07 02.00.01 Added Diagnostic Buffer Post and Diagnostic Release - * structures and defines. - * 02-29-08 02.00.02 Modified various names to make them 32-character unique. - * 05-06-09 02.00.03 Added ISTWI Read Write Tool and Diagnostic CLI Tool. - * -------------------------------------------------------------------------- - -mpi2_type.h - * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. - * -------------------------------------------------------------------------- - -mpi2_ra.h - * 05-06-09 02.00.00 Initial version. - * -------------------------------------------------------------------------- - -mpi2_history.txt Parts list history - -Filename 02.00.12 ----------- -------- -mpi2.h 02.00.12 -mpi2_cnfg.h 02.00.11 -mpi2_init.h 02.00.07 -mpi2_ioc.h 02.00.11 -mpi2_raid.h 02.00.03 -mpi2_sas.h 02.00.02 -mpi2_targ.h 02.00.03 -mpi2_tool.h 02.00.03 -mpi2_type.h 02.00.00 -mpi2_ra.h 02.00.00 - -Filename 02.00.11 02.00.10 02.00.09 02.00.08 02.00.07 02.00.06 ----------- -------- -------- -------- -------- -------- -------- -mpi2.h 02.00.11 02.00.10 02.00.09 02.00.08 02.00.07 02.00.06 -mpi2_cnfg.h 02.00.10 02.00.09 02.00.08 02.00.07 02.00.06 02.00.06 -mpi2_init.h 02.00.06 02.00.06 02.00.05 02.00.05 02.00.04 02.00.03 -mpi2_ioc.h 02.00.10 02.00.09 02.00.08 02.00.07 02.00.07 02.00.06 -mpi2_raid.h 02.00.03 02.00.03 02.00.03 02.00.03 02.00.02 02.00.02 -mpi2_sas.h 02.00.02 02.00.02 02.00.01 02.00.01 02.00.01 02.00.01 -mpi2_targ.h 02.00.03 02.00.03 02.00.02 02.00.02 02.00.02 02.00.02 -mpi2_tool.h 02.00.02 02.00.02 02.00.02 02.00.02 02.00.02 02.00.02 -mpi2_type.h 02.00.00 02.00.00 02.00.00 02.00.00 02.00.00 02.00.00 - -Filename 02.00.05 02.00.04 02.00.03 02.00.02 02.00.01 02.00.00 ----------- -------- -------- -------- -------- -------- -------- -mpi2.h 02.00.05 02.00.04 02.00.03 02.00.02 02.00.01 02.00.00 -mpi2_cnfg.h 02.00.05 02.00.04 02.00.03 02.00.02 02.00.01 02.00.00 -mpi2_init.h 02.00.02 02.00.01 02.00.00 02.00.00 02.00.00 02.00.00 -mpi2_ioc.h 02.00.05 02.00.04 02.00.03 02.00.02 02.00.01 02.00.00 -mpi2_raid.h 02.00.01 02.00.01 02.00.01 02.00.00 02.00.00 02.00.00 -mpi2_sas.h 02.00.01 02.00.01 02.00.01 02.00.01 02.00.00 02.00.00 -mpi2_targ.h 02.00.01 02.00.01 02.00.01 02.00.00 02.00.00 02.00.00 -mpi2_tool.h 02.00.01 02.00.00 02.00.00 02.00.00 02.00.00 02.00.00 -mpi2_type.h 02.00.00 02.00.00 02.00.00 02.00.00 02.00.00 02.00.00 - diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_init.h b/drivers/scsi/mpt2sas/mpi/mpi2_init.h index 563e56d2e94..9d284dae655 100644 --- a/drivers/scsi/mpt2sas/mpi/mpi2_init.h +++ b/drivers/scsi/mpt2sas/mpi/mpi2_init.h @@ -1,12 +1,12 @@ /* - * Copyright (c) 2000-2009 LSI Corporation. + * Copyright (c) 2000-2013 LSI Corporation. * * * Name: mpi2_init.h * Title: MPI SCSI initiator mode messages and structures * Creation Date: June 23, 2006 * - * mpi2_init.h Version: 02.00.07 + * mpi2_init.h Version: 02.00.14 * * Version History * --------------- @@ -21,12 +21,22 @@ * 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. * Defined two new bits in the SlotStatus field of the SCSI * Enclosure Processor Request and Reply. + * 10-28-09 02.00.08 Added defines for decoding the ResponseInfo bytes for + * both SCSI IO Error Reply and SCSI Task Management Reply. + * Added ResponseInfo field to MPI2_SCSI_TASK_MANAGE_REPLY. + * Added MPI2_SCSITASKMGMT_RSP_TM_OVERLAPPED_TAG define. + * 02-10-10 02.00.09 Removed unused structure that had "#if 0" around it. + * 05-12-10 02.00.10 Added optional vendor-unique region to SCSI IO Request. + * 11-10-10 02.00.11 Added MPI2_SCSIIO_NUM_SGLOFFSETS define. + * 02-06-12 02.00.13 Added alternate defines for Task Priority / Command + * Priority to match SAM-4. + * 07-10-12 02.00.14 Added MPI2_SCSIIO_CONTROL_SHIFT_DATADIRECTION. * -------------------------------------------------------------------------- */ @@ -53,20 +63,6 @@ typedef struct } MPI2_SCSI_IO_CDB_EEDP32, MPI2_POINTER PTR_MPI2_SCSI_IO_CDB_EEDP32, Mpi2ScsiIoCdbEedp32_t, MPI2_POINTER pMpi2ScsiIoCdbEedp32_t; -/* TBD: I don't think this is needed for MPI2/Gen2 */ -#if 0 -typedef struct -{ - U8 CDB[16]; /* 0x00 */ - U32 DataLength; /* 0x10 */ - U32 PrimaryReferenceTag; /* 0x14 */ - U16 PrimaryApplicationTag; /* 0x18 */ - U16 PrimaryApplicationTagMask; /* 0x1A */ - U32 TransferLength; /* 0x1C */ -} MPI2_SCSI_IO32_CDB_EEDP16, MPI2_POINTER PTR_MPI2_SCSI_IO32_CDB_EEDP16, - Mpi2ScsiIo32CdbEedp16_t, MPI2_POINTER pMpi2ScsiIo32CdbEedp16_t; -#endif - typedef union { U8 CDB32[32]; @@ -107,7 +103,13 @@ typedef struct _MPI2_SCSI_IO_REQUEST U8 LUN[8]; /* 0x34 */ U32 Control; /* 0x3C */ MPI2_SCSI_IO_CDB_UNION CDB; /* 0x40 */ + +#ifdef MPI2_SCSI_IO_VENDOR_UNIQUE_REGION /* typically this is left undefined */ + MPI2_SCSI_IO_VENDOR_UNIQUE VendorRegion; +#endif + MPI2_SGE_IO_UNION SGL; /* 0x60 */ + } MPI2_SCSI_IO_REQUEST, MPI2_POINTER PTR_MPI2_SCSI_IO_REQUEST, Mpi2SCSIIORequest_t, MPI2_POINTER pMpi2SCSIIORequest_t; @@ -141,6 +143,9 @@ typedef struct _MPI2_SCSI_IO_REQUEST #define MPI2_SCSIIO_SGLFLAGS_SGL1_SHIFT (4) #define MPI2_SCSIIO_SGLFLAGS_SGL0_SHIFT (0) +/* number of SGLOffset fields */ +#define MPI2_SCSIIO_NUM_SGLOFFSETS (4) + /* SCSI IO IoFlags bits */ /* Large CDB Address Space */ @@ -185,6 +190,7 @@ typedef struct _MPI2_SCSI_IO_REQUEST #define MPI2_SCSIIO_CONTROL_ADDCDBLEN_SHIFT (26) #define MPI2_SCSIIO_CONTROL_DATADIRECTION_MASK (0x03000000) +#define MPI2_SCSIIO_CONTROL_SHIFT_DATADIRECTION (24) #define MPI2_SCSIIO_CONTROL_NODATATRANSFER (0x00000000) #define MPI2_SCSIIO_CONTROL_WRITE (0x01000000) #define MPI2_SCSIIO_CONTROL_READ (0x02000000) @@ -192,6 +198,9 @@ typedef struct _MPI2_SCSI_IO_REQUEST #define MPI2_SCSIIO_CONTROL_TASKPRI_MASK (0x00007800) #define MPI2_SCSIIO_CONTROL_TASKPRI_SHIFT (11) +/* alternate name for the previous field; called Command Priority in SAM-4 */ +#define MPI2_SCSIIO_CONTROL_CMDPRI_MASK (0x00007800) +#define MPI2_SCSIIO_CONTROL_CMDPRI_SHIFT (11) #define MPI2_SCSIIO_CONTROL_TASKATTRIBUTE_MASK (0x00000700) #define MPI2_SCSIIO_CONTROL_SIMPLEQ (0x00000000) @@ -254,6 +263,11 @@ typedef struct _MPI2_SCSI_IO_REPLY #define MPI2_SCSI_STATE_AUTOSENSE_FAILED (0x02) #define MPI2_SCSI_STATE_AUTOSENSE_VALID (0x01) +/* masks and shifts for the ResponseInfo field */ + +#define MPI2_SCSI_RI_MASK_REASONCODE (0x000000FF) +#define MPI2_SCSI_RI_SHIFT_REASONCODE (0) + #define MPI2_SCSI_TASKTAG_UNKNOWN (0xFFFF) @@ -327,6 +341,7 @@ typedef struct _MPI2_SCSI_TASK_MANAGE_REPLY U16 IOCStatus; /* 0x0E */ U32 IOCLogInfo; /* 0x10 */ U32 TerminationCount; /* 0x14 */ + U32 ResponseInfo; /* 0x18 */ } MPI2_SCSI_TASK_MANAGE_REPLY, MPI2_POINTER PTR_MPI2_SCSI_TASK_MANAGE_REPLY, Mpi2SCSITaskManagementReply_t, MPI2_POINTER pMpi2SCSIManagementReply_t; @@ -339,8 +354,20 @@ typedef struct _MPI2_SCSI_TASK_MANAGE_REPLY #define MPI2_SCSITASKMGMT_RSP_TM_FAILED (0x05) #define MPI2_SCSITASKMGMT_RSP_TM_SUCCEEDED (0x08) #define MPI2_SCSITASKMGMT_RSP_TM_INVALID_LUN (0x09) +#define MPI2_SCSITASKMGMT_RSP_TM_OVERLAPPED_TAG (0x0A) #define MPI2_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC (0x80) +/* masks and shifts for the ResponseInfo field */ + +#define MPI2_SCSITASKMGMT_RI_MASK_REASONCODE (0x000000FF) +#define MPI2_SCSITASKMGMT_RI_SHIFT_REASONCODE (0) +#define MPI2_SCSITASKMGMT_RI_MASK_ARI2 (0x0000FF00) +#define MPI2_SCSITASKMGMT_RI_SHIFT_ARI2 (8) +#define MPI2_SCSITASKMGMT_RI_MASK_ARI1 (0x00FF0000) +#define MPI2_SCSITASKMGMT_RI_SHIFT_ARI1 (16) +#define MPI2_SCSITASKMGMT_RI_MASK_ARI0 (0xFF000000) +#define MPI2_SCSITASKMGMT_RI_SHIFT_ARI0 (24) + /**************************************************************************** * SCSI Enclosure Processor messages diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h b/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h index ea51ce86869..d159c5f24aa 100644 --- a/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h +++ b/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h @@ -1,12 +1,12 @@ /* - * Copyright (c) 2000-2009 LSI Corporation. + * Copyright (c) 2000-2013 LSI Corporation. * * * Name: mpi2_ioc.h * Title: MPI IOC, Port, Event, FW Download, and FW Upload messages * Creation Date: October 11, 2006 * - * mpi2_ioc.h Version: 02.00.12 + * mpi2_ioc.h Version: 02.00.22 * * Version History * --------------- @@ -87,6 +87,40 @@ * 07-30-09 02.00.12 Added GPIO Interrupt event define and structure. * Added MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER define. * Added new product id family for 2208. + * 10-28-09 02.00.13 Added HostMSIxVectors field to MPI2_IOC_INIT_REQUEST. + * Added MaxMSIxVectors field to MPI2_IOC_FACTS_REPLY. + * Added MinDevHandle field to MPI2_IOC_FACTS_REPLY. + * Added MPI2_IOCFACTS_CAPABILITY_HOST_BASED_DISCOVERY. + * Added MPI2_EVENT_HOST_BASED_DISCOVERY_PHY define. + * Added MPI2_EVENT_SAS_TOPO_ES_NO_EXPANDER define. + * Added Host Based Discovery Phy Event data. + * Added defines for ProductID Product field + * (MPI2_FW_HEADER_PID_). + * Modified values for SAS ProductID Family + * (MPI2_FW_HEADER_PID_FAMILY_). + * 02-10-10 02.00.14 Added SAS Quiesce Event structure and defines. + * Added PowerManagementControl Request structures and + * defines. + * 05-12-10 02.00.15 Marked Task Set Full Event as obsolete. + * Added MPI2_EVENT_SAS_TOPO_LR_UNSUPPORTED_PHY define. + * 11-10-10 02.00.16 Added MPI2_FW_DOWNLOAD_ITYPE_MIN_PRODUCT_SPECIFIC. + * 02-23-11 02.00.17 Added SAS NOTIFY Primitive event, and added + * SASNotifyPrimitiveMasks field to + * MPI2_EVENT_NOTIFICATION_REQUEST. + * Added Temperature Threshold Event. + * Added Host Message Event. + * Added Send Host Message request and reply. + * 05-25-11 02.00.18 For Extended Image Header, added + * MPI2_EXT_IMAGE_TYPE_MIN_PRODUCT_SPECIFIC and + * MPI2_EXT_IMAGE_TYPE_MAX_PRODUCT_SPECIFIC defines. + * Deprecated MPI2_EXT_IMAGE_TYPE_MAX define. + * 08-24-11 02.00.19 Added PhysicalPort field to + * MPI2_EVENT_DATA_SAS_DEVICE_STATUS_CHANGE structure. + * Marked MPI2_PM_CONTROL_FEATURE_PCIE_LINK as obsolete. + * 03-29-12 02.00.21 Added a product specific range to event values. + * 07-26-12 02.00.22 Added MPI2_IOCFACTS_EXCEPT_PARTIAL_MEMORY_FAILURE. + * Added ElapsedSeconds field to + * MPI2_EVENT_DATA_IR_OPERATION_STATUS. * -------------------------------------------------------------------------- */ @@ -119,8 +153,10 @@ typedef struct _MPI2_IOC_INIT_REQUEST U16 MsgVersion; /* 0x0C */ U16 HeaderVersion; /* 0x0E */ U32 Reserved5; /* 0x10 */ - U32 Reserved6; /* 0x14 */ - U16 Reserved7; /* 0x18 */ + U16 Reserved6; /* 0x14 */ + U8 Reserved7; /* 0x16 */ + U8 HostMSIxVectors; /* 0x17 */ + U16 Reserved8; /* 0x18 */ U16 SystemRequestFrameSize; /* 0x1A */ U16 ReplyDescriptorPostQueueDepth; /* 0x1C */ U16 ReplyFreeQueueDepth; /* 0x1E */ @@ -215,7 +251,7 @@ typedef struct _MPI2_IOC_FACTS_REPLY U8 MaxChainDepth; /* 0x14 */ U8 WhoInit; /* 0x15 */ U8 NumberOfPorts; /* 0x16 */ - U8 Reserved2; /* 0x17 */ + U8 MaxMSIxVectors; /* 0x17 */ U16 RequestCredit; /* 0x18 */ U16 ProductID; /* 0x1A */ U32 IOCCapabilities; /* 0x1C */ @@ -233,7 +269,8 @@ typedef struct _MPI2_IOC_FACTS_REPLY U8 MaxVolumes; /* 0x37 */ U16 MaxDevHandle; /* 0x38 */ U16 MaxPersistentEntries; /* 0x3A */ - U32 Reserved4; /* 0x3C */ + U16 MinDevHandle; /* 0x3C */ + U16 Reserved4; /* 0x3E */ } MPI2_IOC_FACTS_REPLY, MPI2_POINTER PTR_MPI2_IOC_FACTS_REPLY, Mpi2IOCFactsReply_t, MPI2_POINTER pMpi2IOCFactsReply_t; @@ -250,6 +287,7 @@ typedef struct _MPI2_IOC_FACTS_REPLY #define MPI2_IOCFACTS_HDRVERSION_DEV_SHIFT (0) /* IOCExceptions */ +#define MPI2_IOCFACTS_EXCEPT_PARTIAL_MEMORY_FAILURE (0x0200) #define MPI2_IOCFACTS_EXCEPT_IR_FOREIGN_CONFIG_MAX (0x0100) #define MPI2_IOCFACTS_EXCEPT_BOOTSTAT_MASK (0x00E0) @@ -269,6 +307,7 @@ typedef struct _MPI2_IOC_FACTS_REPLY /* ProductID field uses MPI2_FW_HEADER_PID_ */ /* IOCCapabilities */ +#define MPI2_IOCFACTS_CAPABILITY_HOST_BASED_DISCOVERY (0x00010000) #define MPI2_IOCFACTS_CAPABILITY_MSI_X_INDEX (0x00008000) #define MPI2_IOCFACTS_CAPABILITY_RAID_ACCELERATOR (0x00004000) #define MPI2_IOCFACTS_CAPABILITY_EVENT_REPLAY (0x00002000) @@ -400,7 +439,7 @@ typedef struct _MPI2_EVENT_NOTIFICATION_REQUEST U32 Reserved6; /* 0x10 */ U32 EventMasks[MPI2_EVENT_NOTIFY_EVENTMASK_WORDS];/* 0x14 */ U16 SASBroadcastPrimitiveMasks; /* 0x24 */ - U16 Reserved7; /* 0x26 */ + U16 SASNotifyPrimitiveMasks; /* 0x26 */ U32 Reserved8; /* 0x28 */ } MPI2_EVENT_NOTIFICATION_REQUEST, MPI2_POINTER PTR_MPI2_EVENT_NOTIFICATION_REQUEST, @@ -438,7 +477,7 @@ typedef struct _MPI2_EVENT_NOTIFICATION_REPLY #define MPI2_EVENT_STATE_CHANGE (0x0002) #define MPI2_EVENT_HARD_RESET_RECEIVED (0x0005) #define MPI2_EVENT_EVENT_CHANGE (0x000A) -#define MPI2_EVENT_TASK_SET_FULL (0x000E) +#define MPI2_EVENT_TASK_SET_FULL (0x000E) /* obsolete */ #define MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE (0x000F) #define MPI2_EVENT_IR_OPERATION_STATUS (0x0014) #define MPI2_EVENT_SAS_DISCOVERY (0x0016) @@ -453,7 +492,13 @@ typedef struct _MPI2_EVENT_NOTIFICATION_REPLY #define MPI2_EVENT_LOG_ENTRY_ADDED (0x0021) #define MPI2_EVENT_SAS_PHY_COUNTER (0x0022) #define MPI2_EVENT_GPIO_INTERRUPT (0x0023) - +#define MPI2_EVENT_HOST_BASED_DISCOVERY_PHY (0x0024) +#define MPI2_EVENT_SAS_QUIESCE (0x0025) +#define MPI2_EVENT_SAS_NOTIFY_PRIMITIVE (0x0026) +#define MPI2_EVENT_TEMP_THRESHOLD (0x0027) +#define MPI2_EVENT_HOST_MESSAGE (0x0028) +#define MPI2_EVENT_MIN_PRODUCT_SPECIFIC (0x006E) +#define MPI2_EVENT_MAX_PRODUCT_SPECIFIC (0x007F) /* Log Entry Added Event data */ @@ -484,6 +529,39 @@ typedef struct _MPI2_EVENT_DATA_GPIO_INTERRUPT { MPI2_POINTER PTR_MPI2_EVENT_DATA_GPIO_INTERRUPT, Mpi2EventDataGpioInterrupt_t, MPI2_POINTER pMpi2EventDataGpioInterrupt_t; +/* Temperature Threshold Event data */ + +typedef struct _MPI2_EVENT_DATA_TEMPERATURE { + U16 Status; /* 0x00 */ + U8 SensorNum; /* 0x02 */ + U8 Reserved1; /* 0x03 */ + U16 CurrentTemperature; /* 0x04 */ + U16 Reserved2; /* 0x06 */ + U32 Reserved3; /* 0x08 */ + U32 Reserved4; /* 0x0C */ +} MPI2_EVENT_DATA_TEMPERATURE, +MPI2_POINTER PTR_MPI2_EVENT_DATA_TEMPERATURE, +Mpi2EventDataTemperature_t, MPI2_POINTER pMpi2EventDataTemperature_t; + +/* Temperature Threshold Event data Status bits */ +#define MPI2_EVENT_TEMPERATURE3_EXCEEDED (0x0008) +#define MPI2_EVENT_TEMPERATURE2_EXCEEDED (0x0004) +#define MPI2_EVENT_TEMPERATURE1_EXCEEDED (0x0002) +#define MPI2_EVENT_TEMPERATURE0_EXCEEDED (0x0001) + + +/* Host Message Event data */ + +typedef struct _MPI2_EVENT_DATA_HOST_MESSAGE { + U8 SourceVF_ID; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U16 Reserved2; /* 0x02 */ + U32 Reserved3; /* 0x04 */ + U32 HostData[1]; /* 0x08 */ +} MPI2_EVENT_DATA_HOST_MESSAGE, MPI2_POINTER PTR_MPI2_EVENT_DATA_HOST_MESSAGE, +Mpi2EventDataHostMessage_t, MPI2_POINTER pMpi2EventDataHostMessage_t; + + /* Hard Reset Received Event data */ typedef struct _MPI2_EVENT_DATA_HARD_RESET_RECEIVED @@ -497,6 +575,7 @@ typedef struct _MPI2_EVENT_DATA_HARD_RESET_RECEIVED MPI2_POINTER pMpi2EventDataHardResetReceived_t; /* Task Set Full Event data */ +/* this event is obsolete */ typedef struct _MPI2_EVENT_DATA_TASK_SET_FULL { @@ -512,7 +591,7 @@ typedef struct _MPI2_EVENT_DATA_SAS_DEVICE_STATUS_CHANGE { U16 TaskTag; /* 0x00 */ U8 ReasonCode; /* 0x02 */ - U8 Reserved1; /* 0x03 */ + U8 PhysicalPort; /* 0x03 */ U8 ASC; /* 0x04 */ U8 ASCQ; /* 0x05 */ U16 DevHandle; /* 0x06 */ @@ -549,7 +628,7 @@ typedef struct _MPI2_EVENT_DATA_IR_OPERATION_STATUS U8 RAIDOperation; /* 0x04 */ U8 PercentComplete; /* 0x05 */ U16 Reserved2; /* 0x06 */ - U32 Resereved3; /* 0x08 */ + U32 ElapsedSeconds; /* 0x08 */ } MPI2_EVENT_DATA_IR_OPERATION_STATUS, MPI2_POINTER PTR_MPI2_EVENT_DATA_IR_OPERATION_STATUS, Mpi2EventDataIrOperationStatus_t, @@ -725,6 +804,24 @@ typedef struct _MPI2_EVENT_DATA_SAS_BROADCAST_PRIMITIVE #define MPI2_EVENT_PRIMITIVE_CHANGE0_RESERVED (0x07) #define MPI2_EVENT_PRIMITIVE_CHANGE1_RESERVED (0x08) +/* SAS Notify Primitive Event data */ + +typedef struct _MPI2_EVENT_DATA_SAS_NOTIFY_PRIMITIVE { + U8 PhyNum; /* 0x00 */ + U8 Port; /* 0x01 */ + U8 Reserved1; /* 0x02 */ + U8 Primitive; /* 0x03 */ +} MPI2_EVENT_DATA_SAS_NOTIFY_PRIMITIVE, +MPI2_POINTER PTR_MPI2_EVENT_DATA_SAS_NOTIFY_PRIMITIVE, +Mpi2EventDataSasNotifyPrimitive_t, +MPI2_POINTER pMpi2EventDataSasNotifyPrimitive_t; + +/* defines for the Primitive field */ +#define MPI2_EVENT_NOTIFY_ENABLE_SPINUP (0x01) +#define MPI2_EVENT_NOTIFY_POWER_LOSS_EXPECTED (0x02) +#define MPI2_EVENT_NOTIFY_RESERVED1 (0x03) +#define MPI2_EVENT_NOTIFY_RESERVED2 (0x04) + /* SAS Initiator Device Status Change Event data */ @@ -793,6 +890,7 @@ typedef struct _MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST MPI2_POINTER pMpi2EventDataSasTopologyChangeList_t; /* values for the ExpStatus field */ +#define MPI2_EVENT_SAS_TOPO_ES_NO_EXPANDER (0x00) #define MPI2_EVENT_SAS_TOPO_ES_ADDED (0x01) #define MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING (0x02) #define MPI2_EVENT_SAS_TOPO_ES_RESPONDING (0x03) @@ -810,6 +908,7 @@ typedef struct _MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST #define MPI2_EVENT_SAS_TOPO_LR_SATA_OOB_COMPLETE (0x03) #define MPI2_EVENT_SAS_TOPO_LR_PORT_SELECTOR (0x04) #define MPI2_EVENT_SAS_TOPO_LR_SMP_RESET_IN_PROGRESS (0x05) +#define MPI2_EVENT_SAS_TOPO_LR_UNSUPPORTED_PHY (0x06) #define MPI2_EVENT_SAS_TOPO_LR_RATE_1_5 (0x08) #define MPI2_EVENT_SAS_TOPO_LR_RATE_3_0 (0x09) #define MPI2_EVENT_SAS_TOPO_LR_RATE_6_0 (0x0A) @@ -878,6 +977,60 @@ typedef struct _MPI2_EVENT_DATA_SAS_PHY_COUNTER { * */ +/* SAS Quiesce Event data */ + +typedef struct _MPI2_EVENT_DATA_SAS_QUIESCE { + U8 ReasonCode; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U16 Reserved2; /* 0x02 */ + U32 Reserved3; /* 0x04 */ +} MPI2_EVENT_DATA_SAS_QUIESCE, + MPI2_POINTER PTR_MPI2_EVENT_DATA_SAS_QUIESCE, + Mpi2EventDataSasQuiesce_t, MPI2_POINTER pMpi2EventDataSasQuiesce_t; + +/* SAS Quiesce Event data ReasonCode values */ +#define MPI2_EVENT_SAS_QUIESCE_RC_STARTED (0x01) +#define MPI2_EVENT_SAS_QUIESCE_RC_COMPLETED (0x02) + + +/* Host Based Discovery Phy Event data */ + +typedef struct _MPI2_EVENT_HBD_PHY_SAS { + U8 Flags; /* 0x00 */ + U8 NegotiatedLinkRate; /* 0x01 */ + U8 PhyNum; /* 0x02 */ + U8 PhysicalPort; /* 0x03 */ + U32 Reserved1; /* 0x04 */ + U8 InitialFrame[28]; /* 0x08 */ +} MPI2_EVENT_HBD_PHY_SAS, MPI2_POINTER PTR_MPI2_EVENT_HBD_PHY_SAS, + Mpi2EventHbdPhySas_t, MPI2_POINTER pMpi2EventHbdPhySas_t; + +/* values for the Flags field */ +#define MPI2_EVENT_HBD_SAS_FLAGS_FRAME_VALID (0x02) +#define MPI2_EVENT_HBD_SAS_FLAGS_SATA_FRAME (0x01) + +/* use MPI2_SAS_NEG_LINK_RATE_ defines from mpi2_cnfg.h for + * the NegotiatedLinkRate field */ + +typedef union _MPI2_EVENT_HBD_DESCRIPTOR { + MPI2_EVENT_HBD_PHY_SAS Sas; +} MPI2_EVENT_HBD_DESCRIPTOR, MPI2_POINTER PTR_MPI2_EVENT_HBD_DESCRIPTOR, + Mpi2EventHbdDescriptor_t, MPI2_POINTER pMpi2EventHbdDescriptor_t; + +typedef struct _MPI2_EVENT_DATA_HBD_PHY { + U8 DescriptorType; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U16 Reserved2; /* 0x02 */ + U32 Reserved3; /* 0x04 */ + MPI2_EVENT_HBD_DESCRIPTOR Descriptor; /* 0x08 */ +} MPI2_EVENT_DATA_HBD_PHY, MPI2_POINTER PTR_MPI2_EVENT_DATA_HBD_PHY, + Mpi2EventDataHbdPhy_t, MPI2_POINTER pMpi2EventDataMpi2EventDataHbdPhy_t; + +/* values for the DescriptorType field */ +#define MPI2_EVENT_HBD_DT_SAS (0x01) + + + /**************************************************************************** * EventAck message ****************************************************************************/ @@ -921,6 +1074,53 @@ typedef struct _MPI2_EVENT_ACK_REPLY /**************************************************************************** +* SendHostMessage message +****************************************************************************/ + +/* SendHostMessage Request message */ +typedef struct _MPI2_SEND_HOST_MESSAGE_REQUEST { + U16 HostDataLength; /* 0x00 */ + U8 ChainOffset; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 Reserved1; /* 0x04 */ + U8 Reserved2; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved3; /* 0x0A */ + U8 Reserved4; /* 0x0C */ + U8 DestVF_ID; /* 0x0D */ + U16 Reserved5; /* 0x0E */ + U32 Reserved6; /* 0x10 */ + U32 Reserved7; /* 0x14 */ + U32 Reserved8; /* 0x18 */ + U32 Reserved9; /* 0x1C */ + U32 Reserved10; /* 0x20 */ + U32 HostData[1]; /* 0x24 */ +} MPI2_SEND_HOST_MESSAGE_REQUEST, +MPI2_POINTER PTR_MPI2_SEND_HOST_MESSAGE_REQUEST, +Mpi2SendHostMessageRequest_t, MPI2_POINTER pMpi2SendHostMessageRequest_t; + + +/* SendHostMessage Reply message */ +typedef struct _MPI2_SEND_HOST_MESSAGE_REPLY { + U16 HostDataLength; /* 0x00 */ + U8 MsgLength; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 Reserved1; /* 0x04 */ + U8 Reserved2; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved3; /* 0x0A */ + U16 Reserved4; /* 0x0C */ + U16 IOCStatus; /* 0x0E */ + U32 IOCLogInfo; /* 0x10 */ +} MPI2_SEND_HOST_MESSAGE_REPLY, MPI2_POINTER PTR_MPI2_SEND_HOST_MESSAGE_REPLY, +Mpi2SendHostMessageReply_t, MPI2_POINTER pMpi2SendHostMessageReply_t; + + +/**************************************************************************** * FWDownload message ****************************************************************************/ @@ -951,7 +1151,9 @@ typedef struct _MPI2_FW_DOWNLOAD_REQUEST #define MPI2_FW_DOWNLOAD_ITYPE_CONFIG_1 (0x07) #define MPI2_FW_DOWNLOAD_ITYPE_CONFIG_2 (0x08) #define MPI2_FW_DOWNLOAD_ITYPE_MEGARAID (0x09) +#define MPI2_FW_DOWNLOAD_ITYPE_COMPLETE (0x0A) #define MPI2_FW_DOWNLOAD_ITYPE_COMMON_BOOT_BLOCK (0x0B) +#define MPI2_FW_DOWNLOAD_ITYPE_MIN_PRODUCT_SPECIFIC (0xF0) /* FWDownload TransactionContext Element */ typedef struct _MPI2_FW_DOWNLOAD_TCSGE @@ -1126,13 +1328,16 @@ typedef struct _MPI2_FW_IMAGE_HEADER #define MPI2_FW_HEADER_PID_TYPE_MASK (0xF000) #define MPI2_FW_HEADER_PID_TYPE_SAS (0x2000) -#define MPI2_FW_HEADER_PID_PROD_MASK (0x0F00) -#define MPI2_FW_HEADER_PID_PROD_A (0x0000) +#define MPI2_FW_HEADER_PID_PROD_MASK (0x0F00) +#define MPI2_FW_HEADER_PID_PROD_A (0x0000) +#define MPI2_FW_HEADER_PID_PROD_TARGET_INITIATOR_SCSI (0x0200) +#define MPI2_FW_HEADER_PID_PROD_IR_SCSI (0x0700) + #define MPI2_FW_HEADER_PID_FAMILY_MASK (0x00FF) /* SAS */ -#define MPI2_FW_HEADER_PID_FAMILY_2108_SAS (0x0010) -#define MPI2_FW_HEADER_PID_FAMILY_2208_SAS (0x0011) +#define MPI2_FW_HEADER_PID_FAMILY_2108_SAS (0x0013) +#define MPI2_FW_HEADER_PID_FAMILY_2208_SAS (0x0014) /* use MPI2_IOCFACTS_PROTOCOL_ defines for ProtocolFlags field */ @@ -1174,16 +1379,18 @@ typedef struct _MPI2_EXT_IMAGE_HEADER #define MPI2_EXT_IMAGE_HEADER_SIZE (0x40) /* defines for the ImageType field */ -#define MPI2_EXT_IMAGE_TYPE_UNSPECIFIED (0x00) -#define MPI2_EXT_IMAGE_TYPE_FW (0x01) -#define MPI2_EXT_IMAGE_TYPE_NVDATA (0x03) -#define MPI2_EXT_IMAGE_TYPE_BOOTLOADER (0x04) -#define MPI2_EXT_IMAGE_TYPE_INITIALIZATION (0x05) -#define MPI2_EXT_IMAGE_TYPE_FLASH_LAYOUT (0x06) -#define MPI2_EXT_IMAGE_TYPE_SUPPORTED_DEVICES (0x07) -#define MPI2_EXT_IMAGE_TYPE_MEGARAID (0x08) - -#define MPI2_EXT_IMAGE_TYPE_MAX (MPI2_EXT_IMAGE_TYPE_MEGARAID) +#define MPI2_EXT_IMAGE_TYPE_UNSPECIFIED (0x00) +#define MPI2_EXT_IMAGE_TYPE_FW (0x01) +#define MPI2_EXT_IMAGE_TYPE_NVDATA (0x03) +#define MPI2_EXT_IMAGE_TYPE_BOOTLOADER (0x04) +#define MPI2_EXT_IMAGE_TYPE_INITIALIZATION (0x05) +#define MPI2_EXT_IMAGE_TYPE_FLASH_LAYOUT (0x06) +#define MPI2_EXT_IMAGE_TYPE_SUPPORTED_DEVICES (0x07) +#define MPI2_EXT_IMAGE_TYPE_MEGARAID (0x08) +#define MPI2_EXT_IMAGE_TYPE_MIN_PRODUCT_SPECIFIC (0x80) +#define MPI2_EXT_IMAGE_TYPE_MAX_PRODUCT_SPECIFIC (0xFF) +#define MPI2_EXT_IMAGE_TYPE_MAX \ + (MPI2_EXT_IMAGE_TYPE_MAX_PRODUCT_SPECIFIC) /* deprecated */ @@ -1348,5 +1555,100 @@ typedef struct _MPI2_INIT_IMAGE_FOOTER #define MPI2_INIT_IMAGE_RESETVECTOR_OFFSET (0x14) +/**************************************************************************** +* PowerManagementControl message +****************************************************************************/ + +/* PowerManagementControl Request message */ +typedef struct _MPI2_PWR_MGMT_CONTROL_REQUEST { + U8 Feature; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U8 ChainOffset; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 Reserved2; /* 0x04 */ + U8 Reserved3; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved4; /* 0x0A */ + U8 Parameter1; /* 0x0C */ + U8 Parameter2; /* 0x0D */ + U8 Parameter3; /* 0x0E */ + U8 Parameter4; /* 0x0F */ + U32 Reserved5; /* 0x10 */ + U32 Reserved6; /* 0x14 */ +} MPI2_PWR_MGMT_CONTROL_REQUEST, MPI2_POINTER PTR_MPI2_PWR_MGMT_CONTROL_REQUEST, + Mpi2PwrMgmtControlRequest_t, MPI2_POINTER pMpi2PwrMgmtControlRequest_t; + +/* defines for the Feature field */ +#define MPI2_PM_CONTROL_FEATURE_DA_PHY_POWER_COND (0x01) +#define MPI2_PM_CONTROL_FEATURE_PORT_WIDTH_MODULATION (0x02) +#define MPI2_PM_CONTROL_FEATURE_PCIE_LINK (0x03) /* obsolete */ +#define MPI2_PM_CONTROL_FEATURE_IOC_SPEED (0x04) +#define MPI2_PM_CONTROL_FEATURE_MIN_PRODUCT_SPECIFIC (0x80) +#define MPI2_PM_CONTROL_FEATURE_MAX_PRODUCT_SPECIFIC (0xFF) + +/* parameter usage for the MPI2_PM_CONTROL_FEATURE_DA_PHY_POWER_COND Feature */ +/* Parameter1 contains a PHY number */ +/* Parameter2 indicates power condition action using these defines */ +#define MPI2_PM_CONTROL_PARAM2_PARTIAL (0x01) +#define MPI2_PM_CONTROL_PARAM2_SLUMBER (0x02) +#define MPI2_PM_CONTROL_PARAM2_EXIT_PWR_MGMT (0x03) +/* Parameter3 and Parameter4 are reserved */ + +/* parameter usage for the MPI2_PM_CONTROL_FEATURE_PORT_WIDTH_MODULATION + * Feature */ +/* Parameter1 contains SAS port width modulation group number */ +/* Parameter2 indicates IOC action using these defines */ +#define MPI2_PM_CONTROL_PARAM2_REQUEST_OWNERSHIP (0x01) +#define MPI2_PM_CONTROL_PARAM2_CHANGE_MODULATION (0x02) +#define MPI2_PM_CONTROL_PARAM2_RELINQUISH_OWNERSHIP (0x03) +/* Parameter3 indicates desired modulation level using these defines */ +#define MPI2_PM_CONTROL_PARAM3_25_PERCENT (0x00) +#define MPI2_PM_CONTROL_PARAM3_50_PERCENT (0x01) +#define MPI2_PM_CONTROL_PARAM3_75_PERCENT (0x02) +#define MPI2_PM_CONTROL_PARAM3_100_PERCENT (0x03) +/* Parameter4 is reserved */ + +/* parameter usage for the MPI2_PM_CONTROL_FEATURE_PCIE_LINK Feature */ +/* Parameter1 indicates desired PCIe link speed using these defines */ +#define MPI2_PM_CONTROL_PARAM1_PCIE_2_5_GBPS (0x00) /* obsolete */ +#define MPI2_PM_CONTROL_PARAM1_PCIE_5_0_GBPS (0x01) /* obsolete */ +#define MPI2_PM_CONTROL_PARAM1_PCIE_8_0_GBPS (0x02) /* obsolete */ +/* Parameter2 indicates desired PCIe link width using these defines */ +#define MPI2_PM_CONTROL_PARAM2_WIDTH_X1 (0x01) /* obsolete */ +#define MPI2_PM_CONTROL_PARAM2_WIDTH_X2 (0x02) /* obsolete */ +#define MPI2_PM_CONTROL_PARAM2_WIDTH_X4 (0x04) /* obsolete */ +#define MPI2_PM_CONTROL_PARAM2_WIDTH_X8 (0x08) /* obsolete */ +/* Parameter3 and Parameter4 are reserved */ + +/* parameter usage for the MPI2_PM_CONTROL_FEATURE_IOC_SPEED Feature */ +/* Parameter1 indicates desired IOC hardware clock speed using these defines */ +#define MPI2_PM_CONTROL_PARAM1_FULL_IOC_SPEED (0x01) +#define MPI2_PM_CONTROL_PARAM1_HALF_IOC_SPEED (0x02) +#define MPI2_PM_CONTROL_PARAM1_QUARTER_IOC_SPEED (0x04) +#define MPI2_PM_CONTROL_PARAM1_EIGHTH_IOC_SPEED (0x08) +/* Parameter2, Parameter3, and Parameter4 are reserved */ + + +/* PowerManagementControl Reply message */ +typedef struct _MPI2_PWR_MGMT_CONTROL_REPLY { + U8 Feature; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U8 MsgLength; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 Reserved2; /* 0x04 */ + U8 Reserved3; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved4; /* 0x0A */ + U16 Reserved5; /* 0x0C */ + U16 IOCStatus; /* 0x0E */ + U32 IOCLogInfo; /* 0x10 */ +} MPI2_PWR_MGMT_CONTROL_REPLY, MPI2_POINTER PTR_MPI2_PWR_MGMT_CONTROL_REPLY, + Mpi2PwrMgmtControlReply_t, MPI2_POINTER pMpi2PwrMgmtControlReply_t; + + #endif diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_raid.h b/drivers/scsi/mpt2sas/mpi/mpi2_raid.h index 5160c33d2a0..0d202a2c6db 100644 --- a/drivers/scsi/mpt2sas/mpi/mpi2_raid.h +++ b/drivers/scsi/mpt2sas/mpi/mpi2_raid.h @@ -1,12 +1,12 @@ /* - * Copyright (c) 2000-2008 LSI Corporation. + * Copyright (c) 2000-2013 LSI Corporation. * * * Name: mpi2_raid.h * Title: MPI Integrated RAID messages and structures * Creation Date: April 26, 2007 * - * mpi2_raid.h Version: 02.00.04 + * mpi2_raid.h Version: 02.00.09 * * Version History * --------------- @@ -22,6 +22,13 @@ * can be sized by the build environment. * 07-30-09 02.00.04 Added proper define for the Use Default Settings bit of * VolumeCreationFlags and marked the old one as obsolete. + * 05-12-10 02.00.05 Added MPI2_RAID_VOL_FLAGS_OP_MDC define. + * 08-24-10 02.00.06 Added MPI2_RAID_ACTION_COMPATIBILITY_CHECK along with + * related structures and defines. + * Added product-specific range to RAID Action values. + * 02-06-12 02.00.08 Added MPI2_RAID_ACTION_PHYSDISK_HIDDEN. + * 07-26-12 02.00.09 Added ElapsedSeconds field to MPI2_RAID_VOL_INDICATOR. + * Added MPI2_RAID_VOL_FLAGS_ELAPSED_SECONDS_VALID define. * -------------------------------------------------------------------------- */ @@ -175,7 +182,10 @@ typedef struct _MPI2_RAID_ACTION_REQUEST #define MPI2_RAID_ACTION_SYSTEM_SHUTDOWN_INITIATED (0x20) #define MPI2_RAID_ACTION_START_RAID_FUNCTION (0x21) #define MPI2_RAID_ACTION_STOP_RAID_FUNCTION (0x22) - +#define MPI2_RAID_ACTION_COMPATIBILITY_CHECK (0x23) +#define MPI2_RAID_ACTION_PHYSDISK_HIDDEN (0x24) +#define MPI2_RAID_ACTION_MIN_PRODUCT_SPECIFIC (0x80) +#define MPI2_RAID_ACTION_MAX_PRODUCT_SPECIFIC (0xFF) /* RAID Volume Creation Structure */ @@ -243,6 +253,23 @@ typedef struct _MPI2_RAID_ONLINE_CAPACITY_EXPANSION Mpi2RaidOnlineCapacityExpansion_t, MPI2_POINTER pMpi2RaidOnlineCapacityExpansion_t; +/* RAID Compatibility Input Structure */ + +typedef struct _MPI2_RAID_COMPATIBILITY_INPUT_STRUCT { + U16 SourceDevHandle; /* 0x00 */ + U16 CandidateDevHandle; /* 0x02 */ + U32 Flags; /* 0x04 */ + U32 Reserved1; /* 0x08 */ + U32 Reserved2; /* 0x0C */ +} MPI2_RAID_COMPATIBILITY_INPUT_STRUCT, +MPI2_POINTER PTR_MPI2_RAID_COMPATIBILITY_INPUT_STRUCT, +Mpi2RaidCompatibilityInputStruct_t, +MPI2_POINTER pMpi2RaidCompatibilityInputStruct_t; + +/* defines for RAID Compatibility Structure Flags field */ +#define MPI2_RAID_COMPAT_SOURCE_IS_VOLUME_FLAG (0x00000002) +#define MPI2_RAID_COMPAT_REPORT_SOURCE_INFO_FLAG (0x00000001) + /* RAID Volume Indicator Structure */ @@ -251,25 +278,59 @@ typedef struct _MPI2_RAID_VOL_INDICATOR U64 TotalBlocks; /* 0x00 */ U64 BlocksRemaining; /* 0x08 */ U32 Flags; /* 0x10 */ + U32 ElapsedSeconds; /* 0x14 */ } MPI2_RAID_VOL_INDICATOR, MPI2_POINTER PTR_MPI2_RAID_VOL_INDICATOR, Mpi2RaidVolIndicator_t, MPI2_POINTER pMpi2RaidVolIndicator_t; /* defines for RAID Volume Indicator Flags field */ +#define MPI2_RAID_VOL_FLAGS_ELAPSED_SECONDS_VALID (0x80000000) + #define MPI2_RAID_VOL_FLAGS_OP_MASK (0x0000000F) #define MPI2_RAID_VOL_FLAGS_OP_BACKGROUND_INIT (0x00000000) #define MPI2_RAID_VOL_FLAGS_OP_ONLINE_CAP_EXPANSION (0x00000001) #define MPI2_RAID_VOL_FLAGS_OP_CONSISTENCY_CHECK (0x00000002) #define MPI2_RAID_VOL_FLAGS_OP_RESYNC (0x00000003) - +#define MPI2_RAID_VOL_FLAGS_OP_MDC (0x00000004) + +/* RAID Compatibility Result Structure */ + +typedef struct _MPI2_RAID_COMPATIBILITY_RESULT_STRUCT { + U8 State; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U16 Reserved2; /* 0x02 */ + U32 GenericAttributes; /* 0x04 */ + U32 OEMSpecificAttributes; /* 0x08 */ + U32 Reserved3; /* 0x0C */ + U32 Reserved4; /* 0x10 */ +} MPI2_RAID_COMPATIBILITY_RESULT_STRUCT, +MPI2_POINTER PTR_MPI2_RAID_COMPATIBILITY_RESULT_STRUCT, +Mpi2RaidCompatibilityResultStruct_t, +MPI2_POINTER pMpi2RaidCompatibilityResultStruct_t; + +/* defines for RAID Compatibility Result Structure State field */ +#define MPI2_RAID_COMPAT_STATE_COMPATIBLE (0x00) +#define MPI2_RAID_COMPAT_STATE_NOT_COMPATIBLE (0x01) + +/* defines for RAID Compatibility Result Structure GenericAttributes field */ +#define MPI2_RAID_COMPAT_GENATTRIB_4K_SECTOR (0x00000010) + +#define MPI2_RAID_COMPAT_GENATTRIB_MEDIA_MASK (0x0000000C) +#define MPI2_RAID_COMPAT_GENATTRIB_SOLID_STATE_DRIVE (0x00000008) +#define MPI2_RAID_COMPAT_GENATTRIB_HARD_DISK_DRIVE (0x00000004) + +#define MPI2_RAID_COMPAT_GENATTRIB_PROTOCOL_MASK (0x00000003) +#define MPI2_RAID_COMPAT_GENATTRIB_SAS_PROTOCOL (0x00000002) +#define MPI2_RAID_COMPAT_GENATTRIB_SATA_PROTOCOL (0x00000001) /* RAID Action Reply ActionData union */ typedef union _MPI2_RAID_ACTION_REPLY_DATA { - U32 Word[5]; - MPI2_RAID_VOL_INDICATOR RaidVolumeIndicator; - U16 VolDevHandle; - U8 VolumeState; - U8 PhysDiskNum; + U32 Word[6]; + MPI2_RAID_VOL_INDICATOR RaidVolumeIndicator; + U16 VolDevHandle; + U8 VolumeState; + U8 PhysDiskNum; + MPI2_RAID_COMPATIBILITY_RESULT_STRUCT RaidCompatibilityResult; } MPI2_RAID_ACTION_REPLY_DATA, MPI2_POINTER PTR_MPI2_RAID_ACTION_REPLY_DATA, Mpi2RaidActionReplyData_t, MPI2_POINTER pMpi2RaidActionReplyData_t; diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_sas.h b/drivers/scsi/mpt2sas/mpi/mpi2_sas.h index 8a42b136cf5..50b39ccd526 100644 --- a/drivers/scsi/mpt2sas/mpi/mpi2_sas.h +++ b/drivers/scsi/mpt2sas/mpi/mpi2_sas.h @@ -1,12 +1,12 @@ /* - * Copyright (c) 2000-2007 LSI Corporation. + * Copyright (c) 2000-2013 LSI Corporation. * * * Name: mpi2_sas.h * Title: MPI Serial Attached SCSI structures and definitions * Creation Date: February 9, 2007 * - * mpi2.h Version: 02.00.02 + * mpi2_sas.h Version: 02.00.05 * * Version History * --------------- @@ -18,6 +18,10 @@ * Control Request. * 10-02-08 02.00.02 Added Set IOC Parameter Operation to SAS IO Unit Control * Request. + * 10-28-09 02.00.03 Changed the type of SGL in MPI2_SATA_PASSTHROUGH_REQUEST + * to MPI2_SGE_IO_UNION since it supports chained SGLs. + * 05-12-10 02.00.04 Modified some comments. + * 08-11-10 02.00.05 Added NCQ operations to SAS IO Unit Control. * -------------------------------------------------------------------------- */ @@ -108,7 +112,7 @@ typedef struct _MPI2_SMP_PASSTHROUGH_REQUEST /* values for PassthroughFlags field */ #define MPI2_SMP_PT_REQ_PT_FLAGS_IMMEDIATE (0x80) -/* values for SGLFlags field are in the SGL section of mpi2.h */ +/* use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */ /* SMP Passthrough Reply Message */ @@ -160,7 +164,7 @@ typedef struct _MPI2_SATA_PASSTHROUGH_REQUEST U32 Reserved4; /* 0x14 */ U32 DataLength; /* 0x18 */ U8 CommandFIS[20]; /* 0x1C */ - MPI2_SIMPLE_SGE_UNION SGL; /* 0x20 */ + MPI2_SGE_IO_UNION SGL; /* 0x30 */ } MPI2_SATA_PASSTHROUGH_REQUEST, MPI2_POINTER PTR_MPI2_SATA_PASSTHROUGH_REQUEST, Mpi2SataPassthroughRequest_t, MPI2_POINTER pMpi2SataPassthroughRequest_t; @@ -172,7 +176,7 @@ typedef struct _MPI2_SATA_PASSTHROUGH_REQUEST #define MPI2_SATA_PT_REQ_PT_FLAGS_WRITE (0x0002) #define MPI2_SATA_PT_REQ_PT_FLAGS_READ (0x0001) -/* values for SGLFlags field are in the SGL section of mpi2.h */ +/* use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */ /* SATA Passthrough Reply Message */ @@ -243,6 +247,8 @@ typedef struct _MPI2_SAS_IOUNIT_CONTROL_REQUEST #define MPI2_SAS_OP_REMOVE_DEVICE (0x0D) #define MPI2_SAS_OP_LOOKUP_MAPPING (0x0E) #define MPI2_SAS_OP_SET_IOC_PARAMETER (0x0F) +#define MPI2_SAS_OP_DEV_ENABLE_NCQ (0x14) +#define MPI2_SAS_OP_DEV_DISABLE_NCQ (0x15) #define MPI2_SAS_OP_PRODUCT_SPECIFIC_MIN (0x80) /* values for the PrimFlags field */ diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_tool.h b/drivers/scsi/mpt2sas/mpi/mpi2_tool.h index 73fcdbf9263..11b2ac4e7c6 100644 --- a/drivers/scsi/mpt2sas/mpi/mpi2_tool.h +++ b/drivers/scsi/mpt2sas/mpi/mpi2_tool.h @@ -1,12 +1,12 @@ /* - * Copyright (c) 2000-2009 LSI Corporation. + * Copyright (c) 2000-2013 LSI Corporation. * * * Name: mpi2_tool.h * Title: MPI diagnostic tool structures and definitions * Creation Date: March 26, 2007 * - * mpi2_tool.h Version: 02.00.04 + * mpi2_tool.h Version: 02.00.10 * * Version History * --------------- @@ -22,6 +22,13 @@ * and reply messages. * Added MPI2_DIAG_BUF_TYPE_EXTENDED. * Incremented MPI2_DIAG_BUF_TYPE_COUNT. + * 05-12-10 02.00.05 Added Diagnostic Data Upload tool. + * 08-11-10 02.00.06 Added defines that were missing for Diagnostic Buffer + * Post Request. + * 05-25-11 02.00.07 Added Flags field and related defines to + * MPI2_TOOLBOX_ISTWI_READ_WRITE_REQUEST. + * 07-26-12 02.00.10 Modified MPI2_TOOLBOX_DIAGNOSTIC_CLI_REQUEST so that + * it uses MPI Chain SGE as well as MPI Simple SGE. * -------------------------------------------------------------------------- */ @@ -37,6 +44,7 @@ /* defines for the Tools */ #define MPI2_TOOLBOX_CLEAN_TOOL (0x00) #define MPI2_TOOLBOX_MEMORY_MOVE_TOOL (0x01) +#define MPI2_TOOLBOX_DIAG_DATA_UPLOAD_TOOL (0x02) #define MPI2_TOOLBOX_ISTWI_READ_WRITE_TOOL (0x03) #define MPI2_TOOLBOX_BEACON_TOOL (0x05) #define MPI2_TOOLBOX_DIAGNOSTIC_CLI_TOOL (0x06) @@ -102,8 +110,7 @@ typedef struct _MPI2_TOOLBOX_CLEAN_REQUEST * Toolbox Memory Move request ****************************************************************************/ -typedef struct _MPI2_TOOLBOX_MEM_MOVE_REQUEST -{ +typedef struct _MPI2_TOOLBOX_MEM_MOVE_REQUEST { U8 Tool; /* 0x00 */ U8 Reserved1; /* 0x01 */ U8 ChainOffset; /* 0x02 */ @@ -120,6 +127,44 @@ typedef struct _MPI2_TOOLBOX_MEM_MOVE_REQUEST /**************************************************************************** +* Toolbox Diagnostic Data Upload request +****************************************************************************/ + +typedef struct _MPI2_TOOLBOX_DIAG_DATA_UPLOAD_REQUEST { + U8 Tool; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U8 ChainOffset; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 Reserved2; /* 0x04 */ + U8 Reserved3; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved4; /* 0x0A */ + U8 SGLFlags; /* 0x0C */ + U8 Reserved5; /* 0x0D */ + U16 Reserved6; /* 0x0E */ + U32 Flags; /* 0x10 */ + U32 DataLength; /* 0x14 */ + MPI2_SGE_SIMPLE_UNION SGL; /* 0x18 */ +} MPI2_TOOLBOX_DIAG_DATA_UPLOAD_REQUEST, +MPI2_POINTER PTR_MPI2_TOOLBOX_DIAG_DATA_UPLOAD_REQUEST, +Mpi2ToolboxDiagDataUploadRequest_t, +MPI2_POINTER pMpi2ToolboxDiagDataUploadRequest_t; + +/* use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */ + + +typedef struct _MPI2_DIAG_DATA_UPLOAD_HEADER { + U32 DiagDataLength; /* 00h */ + U8 FormatCode; /* 04h */ + U8 Reserved1; /* 05h */ + U16 Reserved2; /* 06h */ +} MPI2_DIAG_DATA_UPLOAD_HEADER, MPI2_POINTER PTR_MPI2_DIAG_DATA_UPLOAD_HEADER, +Mpi2DiagDataUploadHeader_t, MPI2_POINTER pMpi2DiagDataUploadHeader_t; + + +/**************************************************************************** * Toolbox ISTWI Read Write Tool ****************************************************************************/ @@ -140,7 +185,7 @@ typedef struct _MPI2_TOOLBOX_ISTWI_READ_WRITE_REQUEST { U8 DevIndex; /* 0x14 */ U8 Action; /* 0x15 */ U8 SGLFlags; /* 0x16 */ - U8 Reserved7; /* 0x17 */ + U8 Flags; /* 0x17 */ U16 TxDataLength; /* 0x18 */ U16 RxDataLength; /* 0x1A */ U32 Reserved8; /* 0x1C */ @@ -162,8 +207,11 @@ typedef struct _MPI2_TOOLBOX_ISTWI_READ_WRITE_REQUEST { #define MPI2_TOOL_ISTWI_ACTION_RELEASE_BUS (0x11) #define MPI2_TOOL_ISTWI_ACTION_RESET (0x12) -/* values for SGLFlags field are in the SGL section of mpi2.h */ +/* use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */ +/* values for the Flags field */ +#define MPI2_TOOL_ISTWI_FLAG_AUTO_RESERVE_RELEASE (0x80) +#define MPI2_TOOL_ISTWI_FLAG_PAGE_ADDR_MASK (0x07) /* Toolbox ISTWI Read Write Tool reply message */ typedef struct _MPI2_TOOLBOX_ISTWI_REPLY { @@ -224,7 +272,7 @@ typedef struct _MPI2_TOOLBOX_BEACON_REQUEST #define MPI2_TOOLBOX_DIAG_CLI_CMD_LENGTH (0x5C) -/* Toolbox Diagnostic CLI Tool request message */ +/* MPI v2.0 Toolbox Diagnostic CLI Tool request message */ typedef struct _MPI2_TOOLBOX_DIAGNOSTIC_CLI_REQUEST { U8 Tool; /* 0x00 */ U8 Reserved1; /* 0x01 */ @@ -242,13 +290,13 @@ typedef struct _MPI2_TOOLBOX_DIAGNOSTIC_CLI_REQUEST { U32 DataLength; /* 0x10 */ U8 DiagnosticCliCommand [MPI2_TOOLBOX_DIAG_CLI_CMD_LENGTH]; /* 0x14 */ - MPI2_SGE_SIMPLE_UNION SGL; /* 0x70 */ + MPI2_MPI_SGE_IO_UNION SGL; /* 0x70 */ } MPI2_TOOLBOX_DIAGNOSTIC_CLI_REQUEST, MPI2_POINTER PTR_MPI2_TOOLBOX_DIAGNOSTIC_CLI_REQUEST, Mpi2ToolboxDiagnosticCliRequest_t, MPI2_POINTER pMpi2ToolboxDiagnosticCliRequest_t; -/* values for SGLFlags field are in the SGL section of mpi2.h */ +/* use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */ /* Toolbox Diagnostic CLI Tool reply message */ @@ -315,6 +363,10 @@ typedef struct _MPI2_DIAG_BUFFER_POST_REQUEST /* count of the number of buffer types */ #define MPI2_DIAG_BUF_TYPE_COUNT (0x03) +/* values for the Flags field */ +#define MPI2_DIAG_BUF_FLAG_RELEASE_ON_FULL (0x00000002) +#define MPI2_DIAG_BUF_FLAG_IMMEDIATE_RELEASE (0x00000001) + /**************************************************************************** * Diagnostic Buffer Post reply diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_type.h b/drivers/scsi/mpt2sas/mpi/mpi2_type.h index cfde017bf16..0b128b68a5e 100644 --- a/drivers/scsi/mpt2sas/mpi/mpi2_type.h +++ b/drivers/scsi/mpt2sas/mpi/mpi2_type.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2007 LSI Corporation. + * Copyright (c) 2000-2013 LSI Corporation. * * * Name: mpi2_type.h diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.c b/drivers/scsi/mpt2sas/mpt2sas_base.c index 89d02401b9e..8b88118e20e 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_base.c +++ b/drivers/scsi/mpt2sas/mpt2sas_base.c @@ -3,7 +3,7 @@ * for access to MPT (Message Passing Technology) firmware. * * This code is based on drivers/scsi/mpt2sas/mpt2_base.c - * Copyright (C) 2007-2009 LSI Corporation + * Copyright (C) 2007-2013 LSI Corporation * (mailto:DL-MPTFusionLinux@lsi.com) * * This program is free software; you can redistribute it and/or @@ -42,7 +42,6 @@ * USA. */ -#include <linux/version.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/errno.h> @@ -58,14 +57,17 @@ #include <linux/sort.h> #include <linux/io.h> #include <linux/time.h> +#include <linux/kthread.h> +#include <linux/aer.h> #include "mpt2sas_base.h" static MPT_CALLBACK mpt_callbacks[MPT_MAX_CALLBACKS]; #define FAULT_POLLING_INTERVAL 1000 /* in milliseconds */ -#define MPT2SAS_MAX_REQUEST_QUEUE 600 /* maximum controller queue depth */ +#define MAX_HBA_QUEUE_DEPTH 30000 +#define MAX_CHAIN_DEPTH 100000 static int max_queue_depth = -1; module_param(max_queue_depth, int, 0); MODULE_PARM_DESC(max_queue_depth, " max controller queue depth "); @@ -78,21 +80,13 @@ static int msix_disable = -1; module_param(msix_disable, int, 0); MODULE_PARM_DESC(msix_disable, " disable msix routed interrupts (default=0)"); -/* diag_buffer_enable is bitwise - * bit 0 set = TRACE - * bit 1 set = SNAPSHOT - * bit 2 set = EXTENDED - * - * Either bit can be set, or both - */ -static int diag_buffer_enable; -module_param(diag_buffer_enable, int, 0); -MODULE_PARM_DESC(diag_buffer_enable, " post diag buffers " - "(TRACE=1/SNAPSHOT=2/EXTENDED=4/default=0)"); - -int mpt2sas_fwfault_debug; +static int mpt2sas_fwfault_debug; MODULE_PARM_DESC(mpt2sas_fwfault_debug, " enable detection of firmware fault " - "and halt firmware - (default=0)"); + "and halt firmware - (default=0)"); + +static int disable_discovery = -1; +module_param(disable_discovery, int, 0); +MODULE_PARM_DESC(disable_discovery, " disable discovery "); /** * _scsih_set_fwfault_debug - global setting of ioc->fwfault_debug. @@ -107,16 +101,39 @@ _scsih_set_fwfault_debug(const char *val, struct kernel_param *kp) if (ret) return ret; - printk(KERN_INFO "setting logging_level(0x%08x)\n", - mpt2sas_fwfault_debug); + printk(KERN_INFO "setting fwfault_debug(%d)\n", mpt2sas_fwfault_debug); list_for_each_entry(ioc, &mpt2sas_ioc_list, list) ioc->fwfault_debug = mpt2sas_fwfault_debug; return 0; } + module_param_call(mpt2sas_fwfault_debug, _scsih_set_fwfault_debug, param_get_int, &mpt2sas_fwfault_debug, 0644); /** + * mpt2sas_remove_dead_ioc_func - kthread context to remove dead ioc + * @arg: input argument, used to derive ioc + * + * Return 0 if controller is removed from pci subsystem. + * Return -1 for other case. + */ +static int mpt2sas_remove_dead_ioc_func(void *arg) +{ + struct MPT2SAS_ADAPTER *ioc = (struct MPT2SAS_ADAPTER *)arg; + struct pci_dev *pdev; + + if ((ioc == NULL)) + return -1; + + pdev = ioc->pdev; + if ((pdev == NULL)) + return -1; + pci_stop_and_remove_bus_device_locked(pdev); + return 0; +} + + +/** * _base_fault_reset_work - workq handling ioc fault conditions * @work: input argument, used to derive ioc * Context: sleep. @@ -131,13 +148,63 @@ _base_fault_reset_work(struct work_struct *work) unsigned long flags; u32 doorbell; int rc; + struct task_struct *p; spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); - if (ioc->shost_recovery) + if (ioc->shost_recovery || ioc->pci_error_recovery) goto rearm_timer; spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); doorbell = mpt2sas_base_get_iocstate(ioc, 0); + if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_MASK) { + printk(MPT2SAS_INFO_FMT "%s : SAS host is non-operational !!!!\n", + ioc->name, __func__); + + /* It may be possible that EEH recovery can resolve some of + * pci bus failure issues rather removing the dead ioc function + * by considering controller is in a non-operational state. So + * here priority is given to the EEH recovery. If it doesn't + * not resolve this issue, mpt2sas driver will consider this + * controller to non-operational state and remove the dead ioc + * function. + */ + if (ioc->non_operational_loop++ < 5) { + spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, + flags); + goto rearm_timer; + } + + /* + * Call _scsih_flush_pending_cmds callback so that we flush all + * pending commands back to OS. This call is required to aovid + * deadlock at block layer. Dead IOC will fail to do diag reset, + * and this call is safe since dead ioc will never return any + * command back from HW. + */ + ioc->schedule_dead_ioc_flush_running_cmds(ioc); + /* + * Set remove_host flag early since kernel thread will + * take some time to execute. + */ + ioc->remove_host = 1; + /*Remove the Dead Host */ + p = kthread_run(mpt2sas_remove_dead_ioc_func, ioc, + "mpt2sas_dead_ioc_%d", ioc->id); + if (IS_ERR(p)) { + printk(MPT2SAS_ERR_FMT + "%s: Running mpt2sas_dead_ioc thread failed !!!!\n", + ioc->name, __func__); + } else { + printk(MPT2SAS_ERR_FMT + "%s: Running mpt2sas_dead_ioc thread success !!!!\n", + ioc->name, __func__); + } + + return; /* don't rearm timer */ + } + + ioc->non_operational_loop = 0; + if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) { rc = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, FORCE_BIG_HAMMER); @@ -286,6 +353,9 @@ _base_sas_ioc_info(struct MPT2SAS_ADAPTER *ioc, MPI2DefaultReply_t *mpi_reply, request_hdr->Function == MPI2_FUNCTION_EVENT_NOTIFICATION) return; + if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) + return; + switch (ioc_status) { /**************************************************************************** @@ -508,18 +578,26 @@ _base_display_event_data(struct MPT2SAS_ADAPTER *ioc, case MPI2_EVENT_EVENT_CHANGE: desc = "Event Change"; break; - case MPI2_EVENT_TASK_SET_FULL: - desc = "Task Set Full"; - break; case MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE: desc = "Device Status Change"; break; case MPI2_EVENT_IR_OPERATION_STATUS: - desc = "IR Operation Status"; + if (!ioc->hide_ir_msg) + desc = "IR Operation Status"; break; case MPI2_EVENT_SAS_DISCOVERY: - desc = "Discovery"; - break; + { + Mpi2EventDataSasDiscovery_t *event_data = + (Mpi2EventDataSasDiscovery_t *)mpi_reply->EventData; + printk(MPT2SAS_INFO_FMT "Discovery: (%s)", ioc->name, + (event_data->ReasonCode == MPI2_EVENT_SAS_DISC_RC_STARTED) ? + "start" : "stop"); + if (event_data->DiscoveryStatus) + printk("discovery_status(0x%08x)", + le32_to_cpu(event_data->DiscoveryStatus)); + printk("\n"); + return; + } case MPI2_EVENT_SAS_BROADCAST_PRIMITIVE: desc = "SAS Broadcast Primitive"; break; @@ -536,16 +614,20 @@ _base_display_event_data(struct MPT2SAS_ADAPTER *ioc, desc = "SAS Enclosure Device Status Change"; break; case MPI2_EVENT_IR_VOLUME: - desc = "IR Volume"; + if (!ioc->hide_ir_msg) + desc = "IR Volume"; break; case MPI2_EVENT_IR_PHYSICAL_DISK: - desc = "IR Physical Disk"; + if (!ioc->hide_ir_msg) + desc = "IR Physical Disk"; break; case MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST: - desc = "IR Configuration Change List"; + if (!ioc->hide_ir_msg) + desc = "IR Configuration Change List"; break; case MPI2_EVENT_LOG_ENTRY_ADDED: - desc = "Log Entry Added"; + if (!ioc->hide_ir_msg) + desc = "Log Entry Added"; break; } @@ -587,7 +669,7 @@ _base_sas_log_info(struct MPT2SAS_ADAPTER *ioc , u32 log_info) return; /* eat the loginfos associated with task aborts */ - if (ioc->ignore_loginfos && (log_info == 30050000 || log_info == + if (ioc->ignore_loginfos && (log_info == 0x30050000 || log_info == 0x31140000 || log_info == 0x31130000)) return; @@ -599,7 +681,10 @@ _base_sas_log_info(struct MPT2SAS_ADAPTER *ioc , u32 log_info) originator_str = "PL"; break; case 2: - originator_str = "IR"; + if (!ioc->hide_ir_msg) + originator_str = "IR"; + else + originator_str = "WarpDrive"; break; } @@ -626,6 +711,11 @@ _base_display_reply_info(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u16 ioc_status; mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); + if (unlikely(!mpi_reply)) { + printk(MPT2SAS_ERR_FMT "mpi_reply not valid at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return; + } ioc_status = le16_to_cpu(mpi_reply->IOCStatus); #ifdef CONFIG_SCSI_MPT2SAS_LOGGING if ((ioc_status & MPI2_IOCSTATUS_MASK) && @@ -667,6 +757,7 @@ mpt2sas_base_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, memcpy(ioc->base_cmds.reply, mpi_reply, mpi_reply->MsgLength*4); } ioc->base_cmds.status &= ~MPT2_CMD_PENDING; + complete(&ioc->base_cmds.done); return 1; } @@ -677,10 +768,9 @@ mpt2sas_base_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, * @msix_index: MSIX table index supplied by the OS * @reply: reply message frame(lower 32bit addr) * - * Return 1 meaning mf should be freed from _base_interrupt - * 0 means the mf is freed from this function. + * Returns void. */ -static u8 +static void _base_async_event(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, u32 reply) { Mpi2EventNotificationReply_t *mpi_reply; @@ -689,9 +779,9 @@ _base_async_event(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, u32 reply) mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); if (!mpi_reply) - return 1; + return; if (mpi_reply->Function != MPI2_FUNCTION_EVENT_NOTIFICATION) - return 1; + return; #ifdef CONFIG_SCSI_MPT2SAS_LOGGING _base_display_event_data(ioc, mpi_reply); #endif @@ -721,7 +811,7 @@ _base_async_event(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, u32 reply) /* ctl callback handler */ mpt2sas_ctl_event_callback(ioc, msix_index, reply); - return 1; + return; } /** @@ -735,20 +825,19 @@ static u8 _base_get_cb_idx(struct MPT2SAS_ADAPTER *ioc, u16 smid) { int i; - u8 cb_idx = 0xFF; + u8 cb_idx; - if (smid >= ioc->hi_priority_smid) { - if (smid < ioc->internal_smid) { - i = smid - ioc->hi_priority_smid; - cb_idx = ioc->hpr_lookup[i].cb_idx; - } else { - i = smid - ioc->internal_smid; - cb_idx = ioc->internal_lookup[i].cb_idx; - } - } else { + if (smid < ioc->hi_priority_smid) { i = smid - 1; cb_idx = ioc->scsi_lookup[i].cb_idx; - } + } else if (smid < ioc->internal_smid) { + i = smid - ioc->hi_priority_smid; + cb_idx = ioc->hpr_lookup[i].cb_idx; + } else if (smid <= ioc->hba_queue_depth) { + i = smid - ioc->internal_smid; + cb_idx = ioc->internal_lookup[i].cb_idx; + } else + cb_idx = 0xFF; return cb_idx; } @@ -810,55 +899,65 @@ union reply_descriptor { static irqreturn_t _base_interrupt(int irq, void *bus_id) { + struct adapter_reply_queue *reply_q = bus_id; union reply_descriptor rd; u32 completed_cmds; u8 request_desript_type; u16 smid; u8 cb_idx; u32 reply; - u8 msix_index; - struct MPT2SAS_ADAPTER *ioc = bus_id; + u8 msix_index = reply_q->msix_index; + struct MPT2SAS_ADAPTER *ioc = reply_q->ioc; Mpi2ReplyDescriptorsUnion_t *rpf; u8 rc; if (ioc->mask_interrupts) return IRQ_NONE; - rpf = &ioc->reply_post_free[ioc->reply_post_host_index]; + if (!atomic_add_unless(&reply_q->busy, 1, 1)) + return IRQ_NONE; + + rpf = &reply_q->reply_post_free[reply_q->reply_post_host_index]; request_desript_type = rpf->Default.ReplyFlags & MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK; - if (request_desript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED) + if (request_desript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED) { + atomic_dec(&reply_q->busy); return IRQ_NONE; + } completed_cmds = 0; + cb_idx = 0xFF; do { - rd.word = rpf->Words; + rd.word = le64_to_cpu(rpf->Words); if (rd.u.low == UINT_MAX || rd.u.high == UINT_MAX) goto out; reply = 0; - cb_idx = 0xFF; smid = le16_to_cpu(rpf->Default.DescriptorTypeDependent1); - msix_index = rpf->Default.MSIxIndex; if (request_desript_type == MPI2_RPY_DESCRIPT_FLAGS_ADDRESS_REPLY) { reply = le32_to_cpu (rpf->AddressReply.ReplyFrameAddress); + if (reply > ioc->reply_dma_max_address || + reply < ioc->reply_dma_min_address) + reply = 0; } else if (request_desript_type == MPI2_RPY_DESCRIPT_FLAGS_TARGET_COMMAND_BUFFER) goto next; else if (request_desript_type == MPI2_RPY_DESCRIPT_FLAGS_TARGETASSIST_SUCCESS) goto next; - if (smid) + if (smid) { cb_idx = _base_get_cb_idx(ioc, smid); - if (smid && cb_idx != 0xFF) { - rc = mpt_callbacks[cb_idx](ioc, smid, msix_index, - reply); + if ((likely(cb_idx < MPT_MAX_CALLBACKS)) + && (likely(mpt_callbacks[cb_idx] != NULL))) { + rc = mpt_callbacks[cb_idx](ioc, smid, + msix_index, reply); if (reply) - _base_display_reply_info(ioc, smid, msix_index, - reply); + _base_display_reply_info(ioc, smid, + msix_index, reply); if (rc) mpt2sas_base_free_smid(ioc, smid); + } } if (!smid) _base_async_event(ioc, msix_index, reply); @@ -878,34 +977,88 @@ _base_interrupt(int irq, void *bus_id) next: - rpf->Words = ULLONG_MAX; - ioc->reply_post_host_index = (ioc->reply_post_host_index == + rpf->Words = cpu_to_le64(ULLONG_MAX); + reply_q->reply_post_host_index = + (reply_q->reply_post_host_index == (ioc->reply_post_queue_depth - 1)) ? 0 : - ioc->reply_post_host_index + 1; + reply_q->reply_post_host_index + 1; request_desript_type = - ioc->reply_post_free[ioc->reply_post_host_index].Default. - ReplyFlags & MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK; + reply_q->reply_post_free[reply_q->reply_post_host_index]. + Default.ReplyFlags & MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK; completed_cmds++; if (request_desript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED) goto out; - if (!ioc->reply_post_host_index) - rpf = ioc->reply_post_free; + if (!reply_q->reply_post_host_index) + rpf = reply_q->reply_post_free; else rpf++; } while (1); out: - if (!completed_cmds) + if (!completed_cmds) { + atomic_dec(&reply_q->busy); return IRQ_NONE; - + } wmb(); - writel(ioc->reply_post_host_index, &ioc->chip->ReplyPostHostIndex); + if (ioc->is_warpdrive) { + writel(reply_q->reply_post_host_index, + ioc->reply_post_host_index[msix_index]); + atomic_dec(&reply_q->busy); + return IRQ_HANDLED; + } + writel(reply_q->reply_post_host_index | (msix_index << + MPI2_RPHI_MSIX_INDEX_SHIFT), &ioc->chip->ReplyPostHostIndex); + atomic_dec(&reply_q->busy); return IRQ_HANDLED; } /** - * mpt2sas_base_release_callback_handler - clear interupt callback handler + * _base_is_controller_msix_enabled - is controller support muli-reply queues + * @ioc: per adapter object + * + */ +static inline int +_base_is_controller_msix_enabled(struct MPT2SAS_ADAPTER *ioc) +{ + return (ioc->facts.IOCCapabilities & + MPI2_IOCFACTS_CAPABILITY_MSI_X_INDEX) && ioc->msix_enable; +} + +/** + * mpt2sas_base_flush_reply_queues - flushing the MSIX reply queues + * @ioc: per adapter object + * Context: ISR conext + * + * Called when a Task Management request has completed. We want + * to flush the other reply queues so all the outstanding IO has been + * completed back to OS before we process the TM completetion. + * + * Return nothing. + */ +void +mpt2sas_base_flush_reply_queues(struct MPT2SAS_ADAPTER *ioc) +{ + struct adapter_reply_queue *reply_q; + + /* If MSIX capability is turned off + * then multi-queues are not enabled + */ + if (!_base_is_controller_msix_enabled(ioc)) + return; + + list_for_each_entry(reply_q, &ioc->reply_queue_list, list) { + if (ioc->shost_recovery) + return; + /* TMs are on msix_index == 0 */ + if (reply_q->msix_index == 0) + continue; + _base_interrupt(reply_q->vector, (void *)reply_q); + } +} + +/** + * mpt2sas_base_release_callback_handler - clear interrupt callback handler * @cb_idx: callback index * * Return nothing. @@ -1054,74 +1207,178 @@ _base_config_dma_addressing(struct MPT2SAS_ADAPTER *ioc, struct pci_dev *pdev) } /** - * _base_save_msix_table - backup msix vector table + * _base_check_enable_msix - checks MSIX capabable. * @ioc: per adapter object * - * This address an errata where diag reset clears out the table + * Check to see if card is capable of MSIX, and set number + * of available msix vectors */ -static void -_base_save_msix_table(struct MPT2SAS_ADAPTER *ioc) +static int +_base_check_enable_msix(struct MPT2SAS_ADAPTER *ioc) { - int i; + int base; + u16 message_control; - if (!ioc->msix_enable || ioc->msix_table_backup == NULL) - return; - for (i = 0; i < ioc->msix_vector_count; i++) - ioc->msix_table_backup[i] = ioc->msix_table[i]; + /* Check whether controller SAS2008 B0 controller, + if it is SAS2008 B0 controller use IO-APIC instead of MSIX */ + if (ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2008 && + ioc->pdev->revision == 0x01) { + return -EINVAL; + } + + base = pci_find_capability(ioc->pdev, PCI_CAP_ID_MSIX); + if (!base) { + dfailprintk(ioc, printk(MPT2SAS_INFO_FMT "msix not " + "supported\n", ioc->name)); + return -EINVAL; + } + + /* get msix vector count */ + /* NUMA_IO not supported for older controllers */ + if (ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2004 || + ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2008 || + ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2108_1 || + ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2108_2 || + ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2108_3 || + ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2116_1 || + ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2116_2) + ioc->msix_vector_count = 1; + else { + pci_read_config_word(ioc->pdev, base + 2, &message_control); + ioc->msix_vector_count = (message_control & 0x3FF) + 1; + } + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "msix is supported, " + "vector_count(%d)\n", ioc->name, ioc->msix_vector_count)); + + return 0; } /** - * _base_restore_msix_table - this restores the msix vector table + * _base_free_irq - free irq * @ioc: per adapter object * + * Freeing respective reply_queue from the list. */ static void -_base_restore_msix_table(struct MPT2SAS_ADAPTER *ioc) +_base_free_irq(struct MPT2SAS_ADAPTER *ioc) { - int i; + struct adapter_reply_queue *reply_q, *next; - if (!ioc->msix_enable || ioc->msix_table_backup == NULL) + if (list_empty(&ioc->reply_queue_list)) return; - for (i = 0; i < ioc->msix_vector_count; i++) - ioc->msix_table[i] = ioc->msix_table_backup[i]; + list_for_each_entry_safe(reply_q, next, &ioc->reply_queue_list, list) { + list_del(&reply_q->list); + synchronize_irq(reply_q->vector); + free_irq(reply_q->vector, reply_q); + kfree(reply_q); + } } /** - * _base_check_enable_msix - checks MSIX capabable. + * _base_request_irq - request irq * @ioc: per adapter object + * @index: msix index into vector table + * @vector: irq vector * - * Check to see if card is capable of MSIX, and set number - * of avaliable msix vectors + * Inserting respective reply_queue into the list. */ static int -_base_check_enable_msix(struct MPT2SAS_ADAPTER *ioc) +_base_request_irq(struct MPT2SAS_ADAPTER *ioc, u8 index, u32 vector) { - int base; - u16 message_control; - u32 msix_table_offset; + struct adapter_reply_queue *reply_q; + int r; - base = pci_find_capability(ioc->pdev, PCI_CAP_ID_MSIX); - if (!base) { - dfailprintk(ioc, printk(MPT2SAS_INFO_FMT "msix not " - "supported\n", ioc->name)); - return -EINVAL; + reply_q = kzalloc(sizeof(struct adapter_reply_queue), GFP_KERNEL); + if (!reply_q) { + printk(MPT2SAS_ERR_FMT "unable to allocate memory %d!\n", + ioc->name, (int)sizeof(struct adapter_reply_queue)); + return -ENOMEM; + } + reply_q->ioc = ioc; + reply_q->msix_index = index; + reply_q->vector = vector; + atomic_set(&reply_q->busy, 0); + if (ioc->msix_enable) + snprintf(reply_q->name, MPT_NAME_LENGTH, "%s%d-msix%d", + MPT2SAS_DRIVER_NAME, ioc->id, index); + else + snprintf(reply_q->name, MPT_NAME_LENGTH, "%s%d", + MPT2SAS_DRIVER_NAME, ioc->id); + r = request_irq(vector, _base_interrupt, IRQF_SHARED, reply_q->name, + reply_q); + if (r) { + printk(MPT2SAS_ERR_FMT "unable to allocate interrupt %d!\n", + reply_q->name, vector); + kfree(reply_q); + return -EBUSY; } - /* get msix vector count */ - pci_read_config_word(ioc->pdev, base + 2, &message_control); - ioc->msix_vector_count = (message_control & 0x3FF) + 1; + INIT_LIST_HEAD(&reply_q->list); + list_add_tail(&reply_q->list, &ioc->reply_queue_list); + return 0; +} - /* get msix table */ - pci_read_config_dword(ioc->pdev, base + 4, &msix_table_offset); - msix_table_offset &= 0xFFFFFFF8; - ioc->msix_table = (u32 *)((void *)ioc->chip + msix_table_offset); +/** + * _base_assign_reply_queues - assigning msix index for each cpu + * @ioc: per adapter object + * + * The enduser would need to set the affinity via /proc/irq/#/smp_affinity + * + * It would nice if we could call irq_set_affinity, however it is not + * an exported symbol + */ +static void +_base_assign_reply_queues(struct MPT2SAS_ADAPTER *ioc) +{ + struct adapter_reply_queue *reply_q; + int cpu_id; + int cpu_grouping, loop, grouping, grouping_mod; - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "msix is supported, " - "vector_count(%d), table_offset(0x%08x), table(%p)\n", ioc->name, - ioc->msix_vector_count, msix_table_offset, ioc->msix_table)); - return 0; + if (!_base_is_controller_msix_enabled(ioc)) + return; + + memset(ioc->cpu_msix_table, 0, ioc->cpu_msix_table_sz); + /* when there are more cpus than available msix vectors, + * then group cpus togeather on same irq + */ + if (ioc->cpu_count > ioc->msix_vector_count) { + grouping = ioc->cpu_count / ioc->msix_vector_count; + grouping_mod = ioc->cpu_count % ioc->msix_vector_count; + if (grouping < 2 || (grouping == 2 && !grouping_mod)) + cpu_grouping = 2; + else if (grouping < 4 || (grouping == 4 && !grouping_mod)) + cpu_grouping = 4; + else if (grouping < 8 || (grouping == 8 && !grouping_mod)) + cpu_grouping = 8; + else + cpu_grouping = 16; + } else + cpu_grouping = 0; + + loop = 0; + reply_q = list_entry(ioc->reply_queue_list.next, + struct adapter_reply_queue, list); + for_each_online_cpu(cpu_id) { + if (!cpu_grouping) { + ioc->cpu_msix_table[cpu_id] = reply_q->msix_index; + reply_q = list_entry(reply_q->list.next, + struct adapter_reply_queue, list); + } else { + if (loop < cpu_grouping) { + ioc->cpu_msix_table[cpu_id] = + reply_q->msix_index; + loop++; + } else { + reply_q = list_entry(reply_q->list.next, + struct adapter_reply_queue, list); + ioc->cpu_msix_table[cpu_id] = + reply_q->msix_index; + loop = 1; + } + } + } } /** @@ -1134,8 +1391,6 @@ _base_disable_msix(struct MPT2SAS_ADAPTER *ioc) { if (ioc->msix_enable) { pci_disable_msix(ioc->pdev); - kfree(ioc->msix_table_backup); - ioc->msix_table_backup = NULL; ioc->msix_enable = 0; } } @@ -1148,8 +1403,9 @@ _base_disable_msix(struct MPT2SAS_ADAPTER *ioc) static int _base_enable_msix(struct MPT2SAS_ADAPTER *ioc) { - struct msix_entry entries; + struct msix_entry *entries, *a; int r; + int i; u8 try_msix = 0; if (msix_disable == -1 || msix_disable == 0) @@ -1161,51 +1417,48 @@ _base_enable_msix(struct MPT2SAS_ADAPTER *ioc) if (_base_check_enable_msix(ioc) != 0) goto try_ioapic; - ioc->msix_table_backup = kcalloc(ioc->msix_vector_count, - sizeof(u32), GFP_KERNEL); - if (!ioc->msix_table_backup) { - dfailprintk(ioc, printk(MPT2SAS_INFO_FMT "allocation for " - "msix_table_backup failed!!!\n", ioc->name)); + ioc->reply_queue_count = min_t(int, ioc->cpu_count, + ioc->msix_vector_count); + + entries = kcalloc(ioc->reply_queue_count, sizeof(struct msix_entry), + GFP_KERNEL); + if (!entries) { + dfailprintk(ioc, printk(MPT2SAS_INFO_FMT "kcalloc " + "failed @ at %s:%d/%s() !!!\n", ioc->name, __FILE__, + __LINE__, __func__)); goto try_ioapic; } - memset(&entries, 0, sizeof(struct msix_entry)); - r = pci_enable_msix(ioc->pdev, &entries, 1); + for (i = 0, a = entries; i < ioc->reply_queue_count; i++, a++) + a->entry = i; + + r = pci_enable_msix(ioc->pdev, entries, ioc->reply_queue_count); if (r) { dfailprintk(ioc, printk(MPT2SAS_INFO_FMT "pci_enable_msix " "failed (r=%d) !!!\n", ioc->name, r)); + kfree(entries); goto try_ioapic; } - r = request_irq(entries.vector, _base_interrupt, IRQF_SHARED, - ioc->name, ioc); - if (r) { - dfailprintk(ioc, printk(MPT2SAS_INFO_FMT "unable to allocate " - "interrupt %d !!!\n", ioc->name, entries.vector)); - pci_disable_msix(ioc->pdev); - goto try_ioapic; + ioc->msix_enable = 1; + for (i = 0, a = entries; i < ioc->reply_queue_count; i++, a++) { + r = _base_request_irq(ioc, i, a->vector); + if (r) { + _base_free_irq(ioc); + _base_disable_msix(ioc); + kfree(entries); + goto try_ioapic; + } } - ioc->pci_irq = entries.vector; - ioc->msix_enable = 1; + kfree(entries); return 0; /* failback to io_apic interrupt routing */ try_ioapic: - r = request_irq(ioc->pdev->irq, _base_interrupt, IRQF_SHARED, - ioc->name, ioc); - if (r) { - printk(MPT2SAS_ERR_FMT "unable to allocate interrupt %d!\n", - ioc->name, ioc->pdev->irq); - r = -EBUSY; - goto out_fail; - } + r = _base_request_irq(ioc, 0, ioc->pdev->irq); - ioc->pci_irq = ioc->pdev->irq; - return 0; - - out_fail: return r; } @@ -1222,14 +1475,18 @@ mpt2sas_base_map_resources(struct MPT2SAS_ADAPTER *ioc) u32 memap_sz; u32 pio_sz; int i, r = 0; + u64 pio_chip = 0; + u64 chip_phys = 0; + struct adapter_reply_queue *reply_q; - dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__)); ioc->bars = pci_select_bars(pdev, IORESOURCE_MEM); if (pci_enable_device_mem(pdev)) { printk(MPT2SAS_WARN_FMT "pci_enable_device_mem: " "failed\n", ioc->name); + ioc->bars = 0; return -ENODEV; } @@ -1238,10 +1495,14 @@ mpt2sas_base_map_resources(struct MPT2SAS_ADAPTER *ioc) MPT2SAS_DRIVER_NAME)) { printk(MPT2SAS_WARN_FMT "pci_request_selected_regions: " "failed\n", ioc->name); + ioc->bars = 0; r = -ENODEV; goto out_fail; } + /* AER (Advanced Error Reporting) hooks */ + pci_enable_pcie_error_reporting(pdev); + pci_set_master(pdev); if (_base_config_dma_addressing(ioc, pdev) != 0) { @@ -1252,22 +1513,26 @@ mpt2sas_base_map_resources(struct MPT2SAS_ADAPTER *ioc) } for (i = 0, memap_sz = 0, pio_sz = 0 ; i < DEVICE_COUNT_RESOURCE; i++) { - if (pci_resource_flags(pdev, i) & PCI_BASE_ADDRESS_SPACE_IO) { + if (pci_resource_flags(pdev, i) & IORESOURCE_IO) { if (pio_sz) continue; - ioc->pio_chip = pci_resource_start(pdev, i); + pio_chip = (u64)pci_resource_start(pdev, i); pio_sz = pci_resource_len(pdev, i); } else { if (memap_sz) continue; - ioc->chip_phys = pci_resource_start(pdev, i); - memap_sz = pci_resource_len(pdev, i); - ioc->chip = ioremap(ioc->chip_phys, memap_sz); - if (ioc->chip == NULL) { - printk(MPT2SAS_ERR_FMT "unable to map adapter " - "memory!\n", ioc->name); - r = -EINVAL; - goto out_fail; + /* verify memory resource is valid before using */ + if (pci_resource_flags(pdev, i) & IORESOURCE_MEM) { + ioc->chip_phys = pci_resource_start(pdev, i); + chip_phys = (u64)ioc->chip_phys; + memap_sz = pci_resource_len(pdev, i); + ioc->chip = ioremap(ioc->chip_phys, memap_sz); + if (ioc->chip == NULL) { + printk(MPT2SAS_ERR_FMT "unable to map " + "adapter memory!\n", ioc->name); + r = -EINVAL; + goto out_fail; + } } } } @@ -1277,13 +1542,18 @@ mpt2sas_base_map_resources(struct MPT2SAS_ADAPTER *ioc) if (r) goto out_fail; - printk(MPT2SAS_INFO_FMT "%s: IRQ %d\n", - ioc->name, ((ioc->msix_enable) ? "PCI-MSI-X enabled" : - "IO-APIC enabled"), ioc->pci_irq); - printk(MPT2SAS_INFO_FMT "iomem(0x%lx), mapped(0x%p), size(%d)\n", - ioc->name, ioc->chip_phys, ioc->chip, memap_sz); - printk(MPT2SAS_INFO_FMT "ioport(0x%lx), size(%d)\n", - ioc->name, ioc->pio_chip, pio_sz); + list_for_each_entry(reply_q, &ioc->reply_queue_list, list) + printk(MPT2SAS_INFO_FMT "%s: IRQ %d\n", + reply_q->name, ((ioc->msix_enable) ? "PCI-MSI-X enabled" : + "IO-APIC enabled"), reply_q->vector); + + printk(MPT2SAS_INFO_FMT "iomem(0x%016llx), mapped(0x%p), size(%d)\n", + ioc->name, (unsigned long long)chip_phys, ioc->chip, memap_sz); + printk(MPT2SAS_INFO_FMT "ioport(0x%016llx), size(%d)\n", + ioc->name, (unsigned long long)pio_chip, pio_sz); + + /* Save PCI configuration state for recovery from PCI AER/EEH errors */ + pci_save_state(pdev); return 0; @@ -1291,8 +1561,8 @@ mpt2sas_base_map_resources(struct MPT2SAS_ADAPTER *ioc) if (ioc->chip_phys) iounmap(ioc->chip); ioc->chip_phys = 0; - ioc->pci_irq = -1; pci_release_selected_regions(ioc->pdev, ioc->bars); + pci_disable_pcie_error_reporting(pdev); pci_disable_device(pdev); return r; } @@ -1396,7 +1666,7 @@ mpt2sas_base_get_smid_scsiio(struct MPT2SAS_ADAPTER *ioc, u8 cb_idx, struct scsi_cmnd *scmd) { unsigned long flags; - struct request_tracker *request; + struct scsiio_tracker *request; u16 smid; spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); @@ -1408,7 +1678,7 @@ mpt2sas_base_get_smid_scsiio(struct MPT2SAS_ADAPTER *ioc, u8 cb_idx, } request = list_entry(ioc->free_list.next, - struct request_tracker, tracker_list); + struct scsiio_tracker, tracker_list); request->scmd = scmd; request->cb_idx = cb_idx; smid = request->smid; @@ -1459,42 +1729,51 @@ mpt2sas_base_free_smid(struct MPT2SAS_ADAPTER *ioc, u16 smid) { unsigned long flags; int i; + struct chain_tracker *chain_req, *next; spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); - if (smid >= ioc->hi_priority_smid) { - if (smid < ioc->internal_smid) { - /* hi-priority */ - i = smid - ioc->hi_priority_smid; - ioc->hpr_lookup[i].cb_idx = 0xFF; - list_add_tail(&ioc->hpr_lookup[i].tracker_list, - &ioc->hpr_free_list); - } else { - /* internal queue */ - i = smid - ioc->internal_smid; - ioc->internal_lookup[i].cb_idx = 0xFF; - list_add_tail(&ioc->internal_lookup[i].tracker_list, - &ioc->internal_free_list); + if (smid < ioc->hi_priority_smid) { + /* scsiio queue */ + i = smid - 1; + if (!list_empty(&ioc->scsi_lookup[i].chain_list)) { + list_for_each_entry_safe(chain_req, next, + &ioc->scsi_lookup[i].chain_list, tracker_list) { + list_del_init(&chain_req->tracker_list); + list_add(&chain_req->tracker_list, + &ioc->free_chain_list); + } } + ioc->scsi_lookup[i].cb_idx = 0xFF; + ioc->scsi_lookup[i].scmd = NULL; + ioc->scsi_lookup[i].direct_io = 0; + list_add(&ioc->scsi_lookup[i].tracker_list, + &ioc->free_list); spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); + + /* + * See _wait_for_commands_to_complete() call with regards + * to this code. + */ + if (ioc->shost_recovery && ioc->pending_io_count) { + if (ioc->pending_io_count == 1) + wake_up(&ioc->reset_wq); + ioc->pending_io_count--; + } return; + } else if (smid < ioc->internal_smid) { + /* hi-priority */ + i = smid - ioc->hi_priority_smid; + ioc->hpr_lookup[i].cb_idx = 0xFF; + list_add(&ioc->hpr_lookup[i].tracker_list, + &ioc->hpr_free_list); + } else if (smid <= ioc->hba_queue_depth) { + /* internal queue */ + i = smid - ioc->internal_smid; + ioc->internal_lookup[i].cb_idx = 0xFF; + list_add(&ioc->internal_lookup[i].tracker_list, + &ioc->internal_free_list); } - - /* scsiio queue */ - i = smid - 1; - ioc->scsi_lookup[i].cb_idx = 0xFF; - ioc->scsi_lookup[i].scmd = NULL; - list_add_tail(&ioc->scsi_lookup[i].tracker_list, - &ioc->free_list); spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); - - /* - * See _wait_for_commands_to_complete() call with regards to this code. - */ - if (ioc->shost_recovery && ioc->pending_io_count) { - if (ioc->pending_io_count == 1) - wake_up(&ioc->reset_wq); - ioc->pending_io_count--; - } } /** @@ -1528,6 +1807,12 @@ static inline void _base_writeq(__u64 b, volatile void __iomem *addr, } #endif +static inline u8 +_base_get_msix_index(struct MPT2SAS_ADAPTER *ioc) +{ + return ioc->cpu_msix_table[raw_smp_processor_id()]; +} + /** * mpt2sas_base_put_smid_scsi_io - send SCSI_IO request to firmware * @ioc: per adapter object @@ -1544,7 +1829,7 @@ mpt2sas_base_put_smid_scsi_io(struct MPT2SAS_ADAPTER *ioc, u16 smid, u16 handle) descriptor.SCSIIO.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO; - descriptor.SCSIIO.MSIxIndex = 0; /* TODO */ + descriptor.SCSIIO.MSIxIndex = _base_get_msix_index(ioc); descriptor.SCSIIO.SMID = cpu_to_le16(smid); descriptor.SCSIIO.DevHandle = cpu_to_le16(handle); descriptor.SCSIIO.LMID = 0; @@ -1554,7 +1839,7 @@ mpt2sas_base_put_smid_scsi_io(struct MPT2SAS_ADAPTER *ioc, u16 smid, u16 handle) /** - * mpt2sas_base_put_smid_hi_priority - send Task Managment request to firmware + * mpt2sas_base_put_smid_hi_priority - send Task Management request to firmware * @ioc: per adapter object * @smid: system request message index * @@ -1568,7 +1853,7 @@ mpt2sas_base_put_smid_hi_priority(struct MPT2SAS_ADAPTER *ioc, u16 smid) descriptor.HighPriority.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY; - descriptor.HighPriority.MSIxIndex = 0; /* TODO */ + descriptor.HighPriority.MSIxIndex = 0; descriptor.HighPriority.SMID = cpu_to_le16(smid); descriptor.HighPriority.LMID = 0; descriptor.HighPriority.Reserved1 = 0; @@ -1590,7 +1875,7 @@ mpt2sas_base_put_smid_default(struct MPT2SAS_ADAPTER *ioc, u16 smid) u64 *request = (u64 *)&descriptor; descriptor.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; - descriptor.Default.MSIxIndex = 0; /* TODO */ + descriptor.Default.MSIxIndex = _base_get_msix_index(ioc); descriptor.Default.SMID = cpu_to_le16(smid); descriptor.Default.LMID = 0; descriptor.Default.DescriptorTypeDependent = 0; @@ -1615,7 +1900,7 @@ mpt2sas_base_put_smid_target_assist(struct MPT2SAS_ADAPTER *ioc, u16 smid, descriptor.SCSITarget.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_SCSI_TARGET; - descriptor.SCSITarget.MSIxIndex = 0; /* TODO */ + descriptor.SCSITarget.MSIxIndex = _base_get_msix_index(ioc); descriptor.SCSITarget.SMID = cpu_to_le16(smid); descriptor.SCSITarget.LMID = 0; descriptor.SCSITarget.IoIndex = cpu_to_le16(io_index); @@ -1682,6 +1967,122 @@ _base_display_dell_branding(struct MPT2SAS_ADAPTER *ioc) } /** + * _base_display_intel_branding - Display branding string + * @ioc: per adapter object + * + * Return nothing. + */ +static void +_base_display_intel_branding(struct MPT2SAS_ADAPTER *ioc) +{ + if (ioc->pdev->subsystem_vendor != PCI_VENDOR_ID_INTEL) + return; + + switch (ioc->pdev->device) { + case MPI2_MFGPAGE_DEVID_SAS2008: + switch (ioc->pdev->subsystem_device) { + case MPT2SAS_INTEL_RMS2LL080_SSDID: + printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, + MPT2SAS_INTEL_RMS2LL080_BRANDING); + break; + case MPT2SAS_INTEL_RMS2LL040_SSDID: + printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, + MPT2SAS_INTEL_RMS2LL040_BRANDING); + break; + case MPT2SAS_INTEL_SSD910_SSDID: + printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, + MPT2SAS_INTEL_SSD910_BRANDING); + break; + default: + break; + } + case MPI2_MFGPAGE_DEVID_SAS2308_2: + switch (ioc->pdev->subsystem_device) { + case MPT2SAS_INTEL_RS25GB008_SSDID: + printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, + MPT2SAS_INTEL_RS25GB008_BRANDING); + break; + case MPT2SAS_INTEL_RMS25JB080_SSDID: + printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, + MPT2SAS_INTEL_RMS25JB080_BRANDING); + break; + case MPT2SAS_INTEL_RMS25JB040_SSDID: + printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, + MPT2SAS_INTEL_RMS25JB040_BRANDING); + break; + case MPT2SAS_INTEL_RMS25KB080_SSDID: + printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, + MPT2SAS_INTEL_RMS25KB080_BRANDING); + break; + case MPT2SAS_INTEL_RMS25KB040_SSDID: + printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, + MPT2SAS_INTEL_RMS25KB040_BRANDING); + break; + case MPT2SAS_INTEL_RMS25LB040_SSDID: + printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, + MPT2SAS_INTEL_RMS25LB040_BRANDING); + break; + case MPT2SAS_INTEL_RMS25LB080_SSDID: + printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, + MPT2SAS_INTEL_RMS25LB080_BRANDING); + break; + default: + break; + } + default: + break; + } +} + +/** + * _base_display_hp_branding - Display branding string + * @ioc: per adapter object + * + * Return nothing. + */ +static void +_base_display_hp_branding(struct MPT2SAS_ADAPTER *ioc) +{ + if (ioc->pdev->subsystem_vendor != MPT2SAS_HP_3PAR_SSVID) + return; + + switch (ioc->pdev->device) { + case MPI2_MFGPAGE_DEVID_SAS2004: + switch (ioc->pdev->subsystem_device) { + case MPT2SAS_HP_DAUGHTER_2_4_INTERNAL_SSDID: + printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, + MPT2SAS_HP_DAUGHTER_2_4_INTERNAL_BRANDING); + break; + default: + break; + } + case MPI2_MFGPAGE_DEVID_SAS2308_2: + switch (ioc->pdev->subsystem_device) { + case MPT2SAS_HP_2_4_INTERNAL_SSDID: + printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, + MPT2SAS_HP_2_4_INTERNAL_BRANDING); + break; + case MPT2SAS_HP_2_4_EXTERNAL_SSDID: + printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, + MPT2SAS_HP_2_4_EXTERNAL_BRANDING); + break; + case MPT2SAS_HP_1_4_INTERNAL_1_4_EXTERNAL_SSDID: + printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, + MPT2SAS_HP_1_4_INTERNAL_1_4_EXTERNAL_BRANDING); + break; + case MPT2SAS_HP_EMBEDDED_2_4_INTERNAL_SSDID: + printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, + MPT2SAS_HP_EMBEDDED_2_4_INTERNAL_BRANDING); + break; + default: + break; + } + default: + break; + } +} + +/** * _base_display_ioc_capabilities - Disply IOC's capabilities. * @ioc: per adapter object * @@ -1692,10 +2093,10 @@ _base_display_ioc_capabilities(struct MPT2SAS_ADAPTER *ioc) { int i = 0; char desc[16]; - u8 revision; u32 iounit_pg1_flags; + u32 bios_version; - pci_read_config_byte(ioc->pdev, PCI_CLASS_REVISION, &revision); + bios_version = le32_to_cpu(ioc->bios_pg3.BiosVersion); strncpy(desc, ioc->manu_pg0.ChipName, 16); printk(MPT2SAS_INFO_FMT "%s: FWVersion(%02d.%02d.%02d.%02d), " "ChipRevision(0x%02x), BiosVersion(%02d.%02d.%02d.%02d)\n", @@ -1704,13 +2105,15 @@ _base_display_ioc_capabilities(struct MPT2SAS_ADAPTER *ioc) (ioc->facts.FWVersion.Word & 0x00FF0000) >> 16, (ioc->facts.FWVersion.Word & 0x0000FF00) >> 8, ioc->facts.FWVersion.Word & 0x000000FF, - revision, - (ioc->bios_pg3.BiosVersion & 0xFF000000) >> 24, - (ioc->bios_pg3.BiosVersion & 0x00FF0000) >> 16, - (ioc->bios_pg3.BiosVersion & 0x0000FF00) >> 8, - ioc->bios_pg3.BiosVersion & 0x000000FF); + ioc->pdev->revision, + (bios_version & 0xFF000000) >> 24, + (bios_version & 0x00FF0000) >> 16, + (bios_version & 0x0000FF00) >> 8, + bios_version & 0x000000FF); _base_display_dell_branding(ioc); + _base_display_intel_branding(ioc); + _base_display_hp_branding(ioc); printk(MPT2SAS_INFO_FMT "Protocol=(", ioc->name); @@ -1728,10 +2131,12 @@ _base_display_ioc_capabilities(struct MPT2SAS_ADAPTER *ioc) printk("), "); printk("Capabilities=("); - if (ioc->facts.IOCCapabilities & - MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID) { - printk("Raid"); - i++; + if (!ioc->hide_ir_msg) { + if (ioc->facts.IOCCapabilities & + MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID) { + printk("Raid"); + i++; + } } if (ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_TLR) { @@ -1789,6 +2194,97 @@ _base_display_ioc_capabilities(struct MPT2SAS_ADAPTER *ioc) } /** + * mpt2sas_base_update_missing_delay - change the missing delay timers + * @ioc: per adapter object + * @device_missing_delay: amount of time till device is reported missing + * @io_missing_delay: interval IO is returned when there is a missing device + * + * Return nothing. + * + * Passed on the command line, this function will modify the device missing + * delay, as well as the io missing delay. This should be called at driver + * load time. + */ +void +mpt2sas_base_update_missing_delay(struct MPT2SAS_ADAPTER *ioc, + u16 device_missing_delay, u8 io_missing_delay) +{ + u16 dmd, dmd_new, dmd_orignal; + u8 io_missing_delay_original; + u16 sz; + Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL; + Mpi2ConfigReply_t mpi_reply; + u8 num_phys = 0; + u16 ioc_status; + + mpt2sas_config_get_number_hba_phys(ioc, &num_phys); + if (!num_phys) + return; + + sz = offsetof(Mpi2SasIOUnitPage1_t, PhyData) + (num_phys * + sizeof(Mpi2SasIOUnit1PhyData_t)); + sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL); + if (!sas_iounit_pg1) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + goto out; + } + if ((mpt2sas_config_get_sas_iounit_pg1(ioc, &mpi_reply, + sas_iounit_pg1, sz))) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + goto out; + } + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + goto out; + } + + /* device missing delay */ + dmd = sas_iounit_pg1->ReportDeviceMissingDelay; + if (dmd & MPI2_SASIOUNIT1_REPORT_MISSING_UNIT_16) + dmd = (dmd & MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK) * 16; + else + dmd = dmd & MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK; + dmd_orignal = dmd; + if (device_missing_delay > 0x7F) { + dmd = (device_missing_delay > 0x7F0) ? 0x7F0 : + device_missing_delay; + dmd = dmd / 16; + dmd |= MPI2_SASIOUNIT1_REPORT_MISSING_UNIT_16; + } else + dmd = device_missing_delay; + sas_iounit_pg1->ReportDeviceMissingDelay = dmd; + + /* io missing delay */ + io_missing_delay_original = sas_iounit_pg1->IODeviceMissingDelay; + sas_iounit_pg1->IODeviceMissingDelay = io_missing_delay; + + if (!mpt2sas_config_set_sas_iounit_pg1(ioc, &mpi_reply, sas_iounit_pg1, + sz)) { + if (dmd & MPI2_SASIOUNIT1_REPORT_MISSING_UNIT_16) + dmd_new = (dmd & + MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK) * 16; + else + dmd_new = + dmd & MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK; + printk(MPT2SAS_INFO_FMT "device_missing_delay: old(%d), " + "new(%d)\n", ioc->name, dmd_orignal, dmd_new); + printk(MPT2SAS_INFO_FMT "ioc_missing_delay: old(%d), " + "new(%d)\n", ioc->name, io_missing_delay_original, + io_missing_delay); + ioc->device_missing_delay = dmd_new; + ioc->io_missing_delay = io_missing_delay; + } + +out: + kfree(sas_iounit_pg1); +} + +/** * _base_static_config_pages - static start of day config pages * @ioc: per adapter object * @@ -1825,6 +2321,7 @@ _base_static_config_pages(struct MPT2SAS_ADAPTER *ioc) MPI2_IOUNITPAGE1_DISABLE_TASK_SET_FULL_HANDLING; ioc->iounit_pg1.Flags = cpu_to_le32(iounit_pg1_flags); mpt2sas_config_set_iounit_pg1(ioc, &mpi_reply, &ioc->iounit_pg1); + } /** @@ -1838,7 +2335,9 @@ _base_static_config_pages(struct MPT2SAS_ADAPTER *ioc) static void _base_release_memory_pools(struct MPT2SAS_ADAPTER *ioc) { - dexitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, + int i; + + dexitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__)); if (ioc->request) { @@ -1896,9 +2395,24 @@ _base_release_memory_pools(struct MPT2SAS_ADAPTER *ioc) ioc->config_page, ioc->config_page_dma); } - kfree(ioc->scsi_lookup); + if (ioc->scsi_lookup) { + free_pages((ulong)ioc->scsi_lookup, ioc->scsi_lookup_pages); + ioc->scsi_lookup = NULL; + } kfree(ioc->hpr_lookup); kfree(ioc->internal_lookup); + if (ioc->chain_lookup) { + for (i = 0; i < ioc->chain_depth; i++) { + if (ioc->chain_lookup[i].chain_buffer) + pci_pool_free(ioc->chain_dma_pool, + ioc->chain_lookup[i].chain_buffer, + ioc->chain_lookup[i].chain_buffer_dma); + } + if (ioc->chain_dma_pool) + pci_pool_destroy(ioc->chain_dma_pool); + free_pages((ulong)ioc->chain_lookup, ioc->chain_pages); + ioc->chain_lookup = NULL; + } } @@ -1912,16 +2426,15 @@ _base_release_memory_pools(struct MPT2SAS_ADAPTER *ioc) static int _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) { - Mpi2IOCFactsReply_t *facts; - u32 queue_size, queue_diff; + struct mpt2sas_facts *facts; u16 max_sge_elements; - u16 num_of_reply_frames; u16 chains_needed_per_io; - u32 sz, total_sz; + u32 sz, total_sz, reply_post_free_sz; u32 retry_sz; u16 max_request_credit; + int i; - dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__)); retry_sz = 0; @@ -1937,14 +2450,15 @@ _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) } /* command line tunables for max controller queue depth */ - if (max_queue_depth != -1) { - max_request_credit = (max_queue_depth < facts->RequestCredit) - ? max_queue_depth : facts->RequestCredit; - } else { - max_request_credit = (facts->RequestCredit > - MPT2SAS_MAX_REQUEST_QUEUE) ? MPT2SAS_MAX_REQUEST_QUEUE : - facts->RequestCredit; - } + if (max_queue_depth != -1 && max_queue_depth != 0) { + max_request_credit = min_t(u16, max_queue_depth + + ioc->hi_priority_depth + ioc->internal_depth, + facts->RequestCredit); + if (max_request_credit > MAX_HBA_QUEUE_DEPTH) + max_request_credit = MAX_HBA_QUEUE_DEPTH; + } else + max_request_credit = min_t(u16, facts->RequestCredit, + MAX_HBA_QUEUE_DEPTH); ioc->hba_queue_depth = max_request_credit; ioc->hi_priority_depth = facts->HighPriorityCredit; @@ -1985,50 +2499,27 @@ _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) } ioc->chains_needed_per_io = chains_needed_per_io; - /* reply free queue sizing - taking into account for events */ - num_of_reply_frames = ioc->hba_queue_depth + 32; - - /* number of replies frames can't be a multiple of 16 */ - /* decrease number of reply frames by 1 */ - if (!(num_of_reply_frames % 16)) - num_of_reply_frames--; - - /* calculate number of reply free queue entries - * (must be multiple of 16) - */ - - /* (we know reply_free_queue_depth is not a multiple of 16) */ - queue_size = num_of_reply_frames; - queue_size += 16 - (queue_size % 16); - ioc->reply_free_queue_depth = queue_size; - - /* reply descriptor post queue sizing */ - /* this size should be the number of request frames + number of reply - * frames - */ - - queue_size = ioc->hba_queue_depth + num_of_reply_frames + 1; - /* round up to 16 byte boundary */ - if (queue_size % 16) - queue_size += 16 - (queue_size % 16); - - /* check against IOC maximum reply post queue depth */ - if (queue_size > facts->MaxReplyDescriptorPostQueueDepth) { - queue_diff = queue_size - - facts->MaxReplyDescriptorPostQueueDepth; - - /* round queue_diff up to multiple of 16 */ - if (queue_diff % 16) - queue_diff += 16 - (queue_diff % 16); - - /* adjust hba_queue_depth, reply_free_queue_depth, - * and queue_size - */ - ioc->hba_queue_depth -= queue_diff; - ioc->reply_free_queue_depth -= queue_diff; - queue_size -= queue_diff; + /* reply free queue sizing - taking into account for 64 FW events */ + ioc->reply_free_queue_depth = ioc->hba_queue_depth + 64; + + /* calculate reply descriptor post queue depth */ + ioc->reply_post_queue_depth = ioc->hba_queue_depth + + ioc->reply_free_queue_depth + 1; + /* align the reply post queue on the next 16 count boundary */ + if (ioc->reply_post_queue_depth % 16) + ioc->reply_post_queue_depth += 16 - + (ioc->reply_post_queue_depth % 16); + + + if (ioc->reply_post_queue_depth > + facts->MaxReplyDescriptorPostQueueDepth) { + ioc->reply_post_queue_depth = + facts->MaxReplyDescriptorPostQueueDepth - + (facts->MaxReplyDescriptorPostQueueDepth % 16); + ioc->hba_queue_depth = + ((ioc->reply_post_queue_depth - 64) / 2) - 1; + ioc->reply_free_queue_depth = ioc->hba_queue_depth + 64; } - ioc->reply_post_queue_depth = queue_size; dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "scatter gather: " "sge_in_main_msg(%d), sge_per_chain(%d), sge_per_io(%d), " @@ -2042,7 +2533,7 @@ _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) /* set the scsi host can_queue depth * with some internal commands that could be outstanding */ - ioc->shost->can_queue = ioc->scsiio_depth - (2); + ioc->shost->can_queue = ioc->scsiio_depth; dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "scsi host: " "can_queue depth (%d)\n", ioc->name, ioc->shost->can_queue)); @@ -2050,7 +2541,7 @@ _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) * "frame for smid=0 */ ioc->chain_depth = ioc->chains_needed_per_io * ioc->scsiio_depth; - sz = ((ioc->scsiio_depth + 1 + ioc->chain_depth) * ioc->request_sz); + sz = ((ioc->scsiio_depth + 1) * ioc->request_sz); /* hi-priority queue */ sz += (ioc->hi_priority_depth * ioc->request_sz); @@ -2091,28 +2582,22 @@ _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) ioc->internal_dma = ioc->hi_priority_dma + (ioc->hi_priority_depth * ioc->request_sz); - ioc->chain = ioc->internal + (ioc->internal_depth * - ioc->request_sz); - ioc->chain_dma = ioc->internal_dma + (ioc->internal_depth * - ioc->request_sz); dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "request pool(0x%p): " "depth(%d), frame_size(%d), pool_size(%d kB)\n", ioc->name, ioc->request, ioc->hba_queue_depth, ioc->request_sz, (ioc->hba_queue_depth * ioc->request_sz)/1024)); - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "chain pool(0x%p): depth" - "(%d), frame_size(%d), pool_size(%d kB)\n", ioc->name, ioc->chain, - ioc->chain_depth, ioc->request_sz, ((ioc->chain_depth * - ioc->request_sz))/1024)); dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "request pool: dma(0x%llx)\n", ioc->name, (unsigned long long) ioc->request_dma)); total_sz += sz; - ioc->scsi_lookup = kcalloc(ioc->scsiio_depth, - sizeof(struct request_tracker), GFP_KERNEL); + sz = ioc->scsiio_depth * sizeof(struct scsiio_tracker); + ioc->scsi_lookup_pages = get_order(sz); + ioc->scsi_lookup = (struct scsiio_tracker *)__get_free_pages( + GFP_KERNEL, ioc->scsi_lookup_pages); if (!ioc->scsi_lookup) { - printk(MPT2SAS_ERR_FMT "scsi_lookup: kcalloc failed\n", - ioc->name); + printk(MPT2SAS_ERR_FMT "scsi_lookup: get_free_pages failed, " + "sz(%d)\n", ioc->name, (int)sz); goto out; } @@ -2120,6 +2605,40 @@ _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) "depth(%d)\n", ioc->name, ioc->request, ioc->scsiio_depth)); + ioc->chain_depth = min_t(u32, ioc->chain_depth, MAX_CHAIN_DEPTH); + sz = ioc->chain_depth * sizeof(struct chain_tracker); + ioc->chain_pages = get_order(sz); + + ioc->chain_lookup = (struct chain_tracker *)__get_free_pages( + GFP_KERNEL, ioc->chain_pages); + if (!ioc->chain_lookup) { + printk(MPT2SAS_ERR_FMT "chain_lookup: get_free_pages failed, " + "sz(%d)\n", ioc->name, (int)sz); + goto out; + } + ioc->chain_dma_pool = pci_pool_create("chain pool", ioc->pdev, + ioc->request_sz, 16, 0); + if (!ioc->chain_dma_pool) { + printk(MPT2SAS_ERR_FMT "chain_dma_pool: pci_pool_create " + "failed\n", ioc->name); + goto out; + } + for (i = 0; i < ioc->chain_depth; i++) { + ioc->chain_lookup[i].chain_buffer = pci_pool_alloc( + ioc->chain_dma_pool , GFP_KERNEL, + &ioc->chain_lookup[i].chain_buffer_dma); + if (!ioc->chain_lookup[i].chain_buffer) { + ioc->chain_depth = i; + goto chain_done; + } + total_sz += ioc->request_sz; + } +chain_done: + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "chain pool depth" + "(%d), frame_size(%d), pool_size(%d kB)\n", ioc->name, + ioc->chain_depth, ioc->request_sz, ((ioc->chain_depth * + ioc->request_sz))/1024)); + /* initialize hi-priority queue smid's */ ioc->hpr_lookup = kcalloc(ioc->hi_priority_depth, sizeof(struct request_tracker), GFP_KERNEL); @@ -2186,6 +2705,8 @@ _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) ioc->name); goto out; } + ioc->reply_dma_min_address = (u32)(ioc->reply_dma); + ioc->reply_dma_max_address = (u32)(ioc->reply_dma) + sz; dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "reply pool(0x%p): depth" "(%d), frame_size(%d), pool_size(%d kB)\n", ioc->name, ioc->reply, ioc->reply_free_queue_depth, ioc->reply_sz, sz/1024)); @@ -2218,7 +2739,12 @@ _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) total_sz += sz; /* reply post queue, 16 byte align */ - sz = ioc->reply_post_queue_depth * sizeof(Mpi2DefaultReplyDescriptor_t); + reply_post_free_sz = ioc->reply_post_queue_depth * + sizeof(Mpi2DefaultReplyDescriptor_t); + if (_base_is_controller_msix_enabled(ioc)) + sz = reply_post_free_sz * ioc->reply_queue_count; + else + sz = reply_post_free_sz; ioc->reply_post_free_dma_pool = pci_pool_create("reply_post_free pool", ioc->pdev, sz, 16, 0); if (!ioc->reply_post_free_dma_pool) { @@ -2267,7 +2793,6 @@ _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) return 0; out: - _base_release_memory_pools(ioc); return -ENOMEM; } @@ -2346,8 +2871,8 @@ _base_wait_for_doorbell_int(struct MPT2SAS_ADAPTER *ioc, int timeout, do { int_status = readl(&ioc->chip->HostInterruptStatus); if (int_status & MPI2_HIS_IOC2SYS_DB_STATUS) { - dhsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " - "successfull count(%d), timeout(%d)\n", ioc->name, + dhsprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: " + "successful count(%d), timeout(%d)\n", ioc->name, __func__, count, timeout)); return 0; } @@ -2387,8 +2912,8 @@ _base_wait_for_doorbell_ack(struct MPT2SAS_ADAPTER *ioc, int timeout, do { int_status = readl(&ioc->chip->HostInterruptStatus); if (!(int_status & MPI2_HIS_SYS2IOC_DB_STATUS)) { - dhsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " - "successfull count(%d), timeout(%d)\n", ioc->name, + dhsprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: " + "successful count(%d), timeout(%d)\n", ioc->name, __func__, count, timeout)); return 0; } else if (int_status & MPI2_HIS_IOC2SYS_DB_STATUS) { @@ -2435,8 +2960,8 @@ _base_wait_for_doorbell_not_used(struct MPT2SAS_ADAPTER *ioc, int timeout, do { doorbell_reg = readl(&ioc->chip->Doorbell); if (!(doorbell_reg & MPI2_DOORBELL_USED)) { - dhsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " - "successfull count(%d), timeout(%d)\n", ioc->name, + dhsprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: " + "successful count(%d), timeout(%d)\n", ioc->name, __func__, count, timeout)); return 0; } @@ -2520,7 +3045,7 @@ _base_handshake_req_reply_wait(struct MPT2SAS_ADAPTER *ioc, int request_bytes, int i; u8 failed; u16 dummy; - u32 *mfp; + __le32 *mfp; /* make sure doorbell is not in use */ if ((readl(&ioc->chip->Doorbell) & MPI2_DOORBELL_USED)) { @@ -2608,10 +3133,10 @@ _base_handshake_req_reply_wait(struct MPT2SAS_ADAPTER *ioc, int request_bytes, writel(0, &ioc->chip->HostInterruptStatus); if (ioc->logging_level & MPT_DEBUG_INIT) { - mfp = (u32 *)reply; - printk(KERN_DEBUG "\toffset:data\n"); + mfp = (__le32 *)reply; + printk(KERN_INFO "\toffset:data\n"); for (i = 0; i < reply_bytes/4; i++) - printk(KERN_DEBUG "\t[0x%02x]:%08x\n", i*4, + printk(KERN_INFO "\t[0x%02x]:%08x\n", i*4, le32_to_cpu(mfp[i])); } return 0; @@ -2644,7 +3169,7 @@ mpt2sas_base_sas_iounit_control(struct MPT2SAS_ADAPTER *ioc, void *request; u16 wait_state_count; - dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__)); mutex_lock(&ioc->base_cmds.mutex); @@ -2689,8 +3214,8 @@ mpt2sas_base_sas_iounit_control(struct MPT2SAS_ADAPTER *ioc, if (mpi_request->Operation == MPI2_SAS_OP_PHY_HARD_RESET || mpi_request->Operation == MPI2_SAS_OP_PHY_LINK_RESET) ioc->ioc_link_reset_in_progress = 1; - mpt2sas_base_put_smid_default(ioc, smid); init_completion(&ioc->base_cmds.done); + mpt2sas_base_put_smid_default(ioc, smid); timeleft = wait_for_completion_timeout(&ioc->base_cmds.done, msecs_to_jiffies(10000)); if ((mpi_request->Operation == MPI2_SAS_OP_PHY_HARD_RESET || @@ -2749,7 +3274,7 @@ mpt2sas_base_scsi_enclosure_processor(struct MPT2SAS_ADAPTER *ioc, void *request; u16 wait_state_count; - dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__)); mutex_lock(&ioc->base_cmds.mutex); @@ -2791,8 +3316,8 @@ mpt2sas_base_scsi_enclosure_processor(struct MPT2SAS_ADAPTER *ioc, request = mpt2sas_base_get_msg_frame(ioc, smid); ioc->base_cmds.smid = smid; memcpy(request, mpi_request, sizeof(Mpi2SepReply_t)); - mpt2sas_base_put_smid_default(ioc, smid); init_completion(&ioc->base_cmds.done); + mpt2sas_base_put_smid_default(ioc, smid); timeleft = wait_for_completion_timeout(&ioc->base_cmds.done, msecs_to_jiffies(10000)); if (!(ioc->base_cmds.status & MPT2_CMD_COMPLETE)) { @@ -2834,10 +3359,11 @@ static int _base_get_port_facts(struct MPT2SAS_ADAPTER *ioc, int port, int sleep_flag) { Mpi2PortFactsRequest_t mpi_request; - Mpi2PortFactsReply_t mpi_reply, *pfacts; + Mpi2PortFactsReply_t mpi_reply; + struct mpt2sas_port_facts *pfacts; int mpi_reply_sz, mpi_request_sz, r; - dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__)); mpi_reply_sz = sizeof(Mpi2PortFactsReply_t); @@ -2855,7 +3381,7 @@ _base_get_port_facts(struct MPT2SAS_ADAPTER *ioc, int port, int sleep_flag) } pfacts = &ioc->pfacts[port]; - memset(pfacts, 0, sizeof(Mpi2PortFactsReply_t)); + memset(pfacts, 0, sizeof(struct mpt2sas_port_facts)); pfacts->PortNumber = mpi_reply.PortNumber; pfacts->VP_ID = mpi_reply.VP_ID; pfacts->VF_ID = mpi_reply.VF_ID; @@ -2876,10 +3402,11 @@ static int _base_get_ioc_facts(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) { Mpi2IOCFactsRequest_t mpi_request; - Mpi2IOCFactsReply_t mpi_reply, *facts; + Mpi2IOCFactsReply_t mpi_reply; + struct mpt2sas_facts *facts; int mpi_reply_sz, mpi_request_sz, r; - dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__)); mpi_reply_sz = sizeof(Mpi2IOCFactsReply_t); @@ -2896,7 +3423,7 @@ _base_get_ioc_facts(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) } facts = &ioc->facts; - memset(facts, 0, sizeof(Mpi2IOCFactsReply_t)); + memset(facts, 0, sizeof(struct mpt2sas_facts)); facts->MsgVersion = le16_to_cpu(mpi_reply.MsgVersion); facts->HeaderVersion = le16_to_cpu(mpi_reply.HeaderVersion); facts->VP_ID = mpi_reply.VP_ID; @@ -2905,6 +3432,7 @@ _base_get_ioc_facts(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) facts->MaxChainDepth = mpi_reply.MaxChainDepth; facts->WhoInit = mpi_reply.WhoInit; facts->NumberOfPorts = mpi_reply.NumberOfPorts; + facts->MaxMSIxVectors = mpi_reply.MaxMSIxVectors; facts->RequestCredit = le16_to_cpu(mpi_reply.RequestCredit); facts->MaxReplyDescriptorPostQueueDepth = le16_to_cpu(mpi_reply.MaxReplyDescriptorPostQueueDepth); @@ -2951,7 +3479,7 @@ _base_send_ioc_init(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) struct timeval current_time; u16 ioc_status; - dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__)); memset(&mpi_request, 0, sizeof(Mpi2IOCInitRequest_t)); @@ -2962,59 +3490,41 @@ _base_send_ioc_init(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) mpi_request.MsgVersion = cpu_to_le16(MPI2_VERSION); mpi_request.HeaderVersion = cpu_to_le16(MPI2_HEADER_VERSION); - /* In MPI Revision I (0xA), the SystemReplyFrameSize(offset 0x18) was - * removed and made reserved. For those with older firmware will need - * this fix. It was decided that the Reply and Request frame sizes are - * the same. - */ - if ((ioc->facts.HeaderVersion >> 8) < 0xA) { - mpi_request.Reserved7 = cpu_to_le16(ioc->reply_sz); -/* mpi_request.SystemReplyFrameSize = - * cpu_to_le16(ioc->reply_sz); - */ - } - + if (_base_is_controller_msix_enabled(ioc)) + mpi_request.HostMSIxVectors = ioc->reply_queue_count; mpi_request.SystemRequestFrameSize = cpu_to_le16(ioc->request_sz/4); mpi_request.ReplyDescriptorPostQueueDepth = cpu_to_le16(ioc->reply_post_queue_depth); mpi_request.ReplyFreeQueueDepth = cpu_to_le16(ioc->reply_free_queue_depth); -#if BITS_PER_LONG > 32 mpi_request.SenseBufferAddressHigh = - cpu_to_le32(ioc->sense_dma >> 32); + cpu_to_le32((u64)ioc->sense_dma >> 32); mpi_request.SystemReplyAddressHigh = - cpu_to_le32(ioc->reply_dma >> 32); + cpu_to_le32((u64)ioc->reply_dma >> 32); mpi_request.SystemRequestFrameBaseAddress = - cpu_to_le64(ioc->request_dma); + cpu_to_le64((u64)ioc->request_dma); mpi_request.ReplyFreeQueueAddress = - cpu_to_le64(ioc->reply_free_dma); + cpu_to_le64((u64)ioc->reply_free_dma); mpi_request.ReplyDescriptorPostQueueAddress = - cpu_to_le64(ioc->reply_post_free_dma); -#else - mpi_request.SystemRequestFrameBaseAddress = - cpu_to_le32(ioc->request_dma); - mpi_request.ReplyFreeQueueAddress = - cpu_to_le32(ioc->reply_free_dma); - mpi_request.ReplyDescriptorPostQueueAddress = - cpu_to_le32(ioc->reply_post_free_dma); -#endif + cpu_to_le64((u64)ioc->reply_post_free_dma); + /* This time stamp specifies number of milliseconds * since epoch ~ midnight January 1, 1970. */ do_gettimeofday(¤t_time); - mpi_request.TimeStamp = (current_time.tv_sec * 1000) + - (current_time.tv_usec >> 3); + mpi_request.TimeStamp = cpu_to_le64((u64)current_time.tv_sec * 1000 + + (current_time.tv_usec / 1000)); if (ioc->logging_level & MPT_DEBUG_INIT) { - u32 *mfp; + __le32 *mfp; int i; - mfp = (u32 *)&mpi_request; - printk(KERN_DEBUG "\toffset:data\n"); + mfp = (__le32 *)&mpi_request; + printk(KERN_INFO "\toffset:data\n"); for (i = 0; i < sizeof(Mpi2IOCInitRequest_t)/4; i++) - printk(KERN_DEBUG "\t[0x%02x]:%08x\n", i*4, + printk(KERN_INFO "\t[0x%02x]:%08x\n", i*4, le32_to_cpu(mfp[i])); } @@ -3040,6 +3550,58 @@ _base_send_ioc_init(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) } /** + * mpt2sas_port_enable_done - command completion routine for port enable + * @ioc: per adapter object + * @smid: system request message index + * @msix_index: MSIX table index supplied by the OS + * @reply: reply message frame(lower 32bit addr) + * + * Return 1 meaning mf should be freed from _base_interrupt + * 0 means the mf is freed from this function. + */ +u8 +mpt2sas_port_enable_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, + u32 reply) +{ + MPI2DefaultReply_t *mpi_reply; + u16 ioc_status; + + mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); + if (mpi_reply && mpi_reply->Function == MPI2_FUNCTION_EVENT_ACK) + return 1; + + if (ioc->port_enable_cmds.status == MPT2_CMD_NOT_USED) + return 1; + + ioc->port_enable_cmds.status |= MPT2_CMD_COMPLETE; + if (mpi_reply) { + ioc->port_enable_cmds.status |= MPT2_CMD_REPLY_VALID; + memcpy(ioc->port_enable_cmds.reply, mpi_reply, + mpi_reply->MsgLength*4); + } + ioc->port_enable_cmds.status &= ~MPT2_CMD_PENDING; + + ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK; + + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) + ioc->port_enable_failed = 1; + + if (ioc->is_driver_loading) { + if (ioc_status == MPI2_IOCSTATUS_SUCCESS) { + mpt2sas_port_enable_complete(ioc); + return 1; + } else { + ioc->start_scan_failed = ioc_status; + ioc->start_scan = 0; + return 1; + } + } + complete(&ioc->port_enable_cmds.done); + return 1; +} + + +/** * _base_send_port_enable - send port_enable(discovery stuff) to firmware * @ioc: per adapter object * @sleep_flag: CAN_SLEEP or NO_SLEEP @@ -3050,67 +3612,151 @@ static int _base_send_port_enable(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) { Mpi2PortEnableRequest_t *mpi_request; - u32 ioc_state; + Mpi2PortEnableReply_t *mpi_reply; unsigned long timeleft; int r = 0; u16 smid; + u16 ioc_status; printk(MPT2SAS_INFO_FMT "sending port enable !!\n", ioc->name); - if (ioc->base_cmds.status & MPT2_CMD_PENDING) { + if (ioc->port_enable_cmds.status & MPT2_CMD_PENDING) { printk(MPT2SAS_ERR_FMT "%s: internal command already in use\n", ioc->name, __func__); return -EAGAIN; } - smid = mpt2sas_base_get_smid(ioc, ioc->base_cb_idx); + smid = mpt2sas_base_get_smid(ioc, ioc->port_enable_cb_idx); if (!smid) { printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", ioc->name, __func__); return -EAGAIN; } - ioc->base_cmds.status = MPT2_CMD_PENDING; + ioc->port_enable_cmds.status = MPT2_CMD_PENDING; mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); - ioc->base_cmds.smid = smid; + ioc->port_enable_cmds.smid = smid; memset(mpi_request, 0, sizeof(Mpi2PortEnableRequest_t)); mpi_request->Function = MPI2_FUNCTION_PORT_ENABLE; - mpi_request->VF_ID = 0; /* TODO */ - mpi_request->VP_ID = 0; + init_completion(&ioc->port_enable_cmds.done); mpt2sas_base_put_smid_default(ioc, smid); - init_completion(&ioc->base_cmds.done); - timeleft = wait_for_completion_timeout(&ioc->base_cmds.done, + timeleft = wait_for_completion_timeout(&ioc->port_enable_cmds.done, 300*HZ); - if (!(ioc->base_cmds.status & MPT2_CMD_COMPLETE)) { + if (!(ioc->port_enable_cmds.status & MPT2_CMD_COMPLETE)) { printk(MPT2SAS_ERR_FMT "%s: timeout\n", ioc->name, __func__); _debug_dump_mf(mpi_request, sizeof(Mpi2PortEnableRequest_t)/4); - if (ioc->base_cmds.status & MPT2_CMD_RESET) + if (ioc->port_enable_cmds.status & MPT2_CMD_RESET) r = -EFAULT; else r = -ETIME; goto out; - } else - dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: complete\n", - ioc->name, __func__)); + } + mpi_reply = ioc->port_enable_cmds.reply; - ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_OPERATIONAL, - 60, sleep_flag); - if (ioc_state) { - printk(MPT2SAS_ERR_FMT "%s: failed going to operational state " - " (ioc_state=0x%x)\n", ioc->name, __func__, ioc_state); + ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + printk(MPT2SAS_ERR_FMT "%s: failed with (ioc_status=0x%08x)\n", + ioc->name, __func__, ioc_status); r = -EFAULT; + goto out; } out: - ioc->base_cmds.status = MPT2_CMD_NOT_USED; - printk(MPT2SAS_INFO_FMT "port enable: %s\n", - ioc->name, ((r == 0) ? "SUCCESS" : "FAILED")); + ioc->port_enable_cmds.status = MPT2_CMD_NOT_USED; + printk(MPT2SAS_INFO_FMT "port enable: %s\n", ioc->name, ((r == 0) ? + "SUCCESS" : "FAILED")); return r; } /** + * mpt2sas_port_enable - initiate firmware discovery (don't wait for reply) + * @ioc: per adapter object + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt2sas_port_enable(struct MPT2SAS_ADAPTER *ioc) +{ + Mpi2PortEnableRequest_t *mpi_request; + u16 smid; + + printk(MPT2SAS_INFO_FMT "sending port enable !!\n", ioc->name); + + if (ioc->port_enable_cmds.status & MPT2_CMD_PENDING) { + printk(MPT2SAS_ERR_FMT "%s: internal command already in use\n", + ioc->name, __func__); + return -EAGAIN; + } + + smid = mpt2sas_base_get_smid(ioc, ioc->port_enable_cb_idx); + if (!smid) { + printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", + ioc->name, __func__); + return -EAGAIN; + } + + ioc->port_enable_cmds.status = MPT2_CMD_PENDING; + mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); + ioc->port_enable_cmds.smid = smid; + memset(mpi_request, 0, sizeof(Mpi2PortEnableRequest_t)); + mpi_request->Function = MPI2_FUNCTION_PORT_ENABLE; + + mpt2sas_base_put_smid_default(ioc, smid); + return 0; +} + +/** + * _base_determine_wait_on_discovery - desposition + * @ioc: per adapter object + * + * Decide whether to wait on discovery to complete. Used to either + * locate boot device, or report volumes ahead of physical devices. + * + * Returns 1 for wait, 0 for don't wait + */ +static int +_base_determine_wait_on_discovery(struct MPT2SAS_ADAPTER *ioc) +{ + /* We wait for discovery to complete if IR firmware is loaded. + * The sas topology events arrive before PD events, so we need time to + * turn on the bit in ioc->pd_handles to indicate PD + * Also, it maybe required to report Volumes ahead of physical + * devices when MPI2_IOCPAGE8_IRFLAGS_LOW_VOLUME_MAPPING is set. + */ + if (ioc->ir_firmware) + return 1; + + /* if no Bios, then we don't need to wait */ + if (!ioc->bios_pg3.BiosVersion) + return 0; + + /* Bios is present, then we drop down here. + * + * If there any entries in the Bios Page 2, then we wait + * for discovery to complete. + */ + + /* Current Boot Device */ + if ((ioc->bios_pg2.CurrentBootDeviceForm & + MPI2_BIOSPAGE2_FORM_MASK) == + MPI2_BIOSPAGE2_FORM_NO_DEVICE_SPECIFIED && + /* Request Boot Device */ + (ioc->bios_pg2.ReqBootDeviceForm & + MPI2_BIOSPAGE2_FORM_MASK) == + MPI2_BIOSPAGE2_FORM_NO_DEVICE_SPECIFIED && + /* Alternate Request Boot Device */ + (ioc->bios_pg2.ReqAltBootDeviceForm & + MPI2_BIOSPAGE2_FORM_MASK) == + MPI2_BIOSPAGE2_FORM_NO_DEVICE_SPECIFIED) + return 0; + + return 1; +} + + +/** * _base_unmask_events - turn on notification for this event * @ioc: per adapter object * @event: firmware event @@ -3153,7 +3799,7 @@ _base_event_notification(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) int r = 0; int i; - dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__)); if (ioc->base_cmds.status & MPT2_CMD_PENDING) { @@ -3177,9 +3823,9 @@ _base_event_notification(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) mpi_request->VP_ID = 0; for (i = 0; i < MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++) mpi_request->EventMasks[i] = - le32_to_cpu(ioc->event_masks[i]); - mpt2sas_base_put_smid_default(ioc, smid); + cpu_to_le32(ioc->event_masks[i]); init_completion(&ioc->base_cmds.done); + mpt2sas_base_put_smid_default(ioc, smid); timeleft = wait_for_completion_timeout(&ioc->base_cmds.done, 30*HZ); if (!(ioc->base_cmds.status & MPT2_CMD_COMPLETE)) { printk(MPT2SAS_ERR_FMT "%s: timeout\n", @@ -3191,7 +3837,7 @@ _base_event_notification(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) else r = -ETIME; } else - dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: complete\n", + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: complete\n", ioc->name, __func__)); ioc->base_cmds.status = MPT2_CMD_NOT_USED; return r; @@ -3250,10 +3896,7 @@ _base_diag_reset(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) u32 hcb_size; printk(MPT2SAS_INFO_FMT "sending diag reset !!\n", ioc->name); - - _base_save_msix_table(ioc); - - drsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "clear interrupts\n", + drsprintk(ioc, printk(MPT2SAS_INFO_FMT "clear interrupts\n", ioc->name)); count = 0; @@ -3261,7 +3904,7 @@ _base_diag_reset(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) /* Write magic sequence to WriteSequence register * Loop until in diagnostic mode */ - drsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "write magic " + drsprintk(ioc, printk(MPT2SAS_INFO_FMT "write magic " "sequence\n", ioc->name)); writel(MPI2_WRSEQ_FLUSH_KEY_VALUE, &ioc->chip->WriteSequence); writel(MPI2_WRSEQ_1ST_KEY_VALUE, &ioc->chip->WriteSequence); @@ -3281,7 +3924,7 @@ _base_diag_reset(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) goto out; host_diagnostic = readl(&ioc->chip->HostDiagnostic); - drsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "wrote magic " + drsprintk(ioc, printk(MPT2SAS_INFO_FMT "wrote magic " "sequence: count(%d), host_diagnostic(0x%08x)\n", ioc->name, count, host_diagnostic)); @@ -3289,16 +3932,20 @@ _base_diag_reset(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) hcb_size = readl(&ioc->chip->HCBSize); - drsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "diag reset: issued\n", + drsprintk(ioc, printk(MPT2SAS_INFO_FMT "diag reset: issued\n", ioc->name)); writel(host_diagnostic | MPI2_DIAG_RESET_ADAPTER, &ioc->chip->HostDiagnostic); - /* don't access any registers for 50 milliseconds */ - msleep(50); + /* This delay allows the chip PCIe hardware time to finish reset tasks*/ + if (sleep_flag == CAN_SLEEP) + msleep(MPI2_HARD_RESET_PCIE_FIRST_READ_DELAY_MICRO_SEC/1000); + else + mdelay(MPI2_HARD_RESET_PCIE_FIRST_READ_DELAY_MICRO_SEC/1000); - /* 300 second max wait */ - for (count = 0; count < 3000000 ; count++) { + /* Approximately 300 second max wait */ + for (count = 0; count < (300000000 / + MPI2_HARD_RESET_PCIE_SECOND_READ_DELAY_MICRO_SEC); count++) { host_diagnostic = readl(&ioc->chip->HostDiagnostic); @@ -3307,38 +3954,40 @@ _base_diag_reset(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) if (!(host_diagnostic & MPI2_DIAG_RESET_ADAPTER)) break; - /* wait 100 msec */ + /* Wait to pass the second read delay window */ if (sleep_flag == CAN_SLEEP) - msleep(1); + msleep(MPI2_HARD_RESET_PCIE_SECOND_READ_DELAY_MICRO_SEC + /1000); else - mdelay(1); + mdelay(MPI2_HARD_RESET_PCIE_SECOND_READ_DELAY_MICRO_SEC + /1000); } if (host_diagnostic & MPI2_DIAG_HCB_MODE) { - drsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "restart the adapter " + drsprintk(ioc, printk(MPT2SAS_INFO_FMT "restart the adapter " "assuming the HCB Address points to good F/W\n", ioc->name)); host_diagnostic &= ~MPI2_DIAG_BOOT_DEVICE_SELECT_MASK; host_diagnostic |= MPI2_DIAG_BOOT_DEVICE_SELECT_HCDW; writel(host_diagnostic, &ioc->chip->HostDiagnostic); - drsprintk(ioc, printk(MPT2SAS_DEBUG_FMT + drsprintk(ioc, printk(MPT2SAS_INFO_FMT "re-enable the HCDW\n", ioc->name)); writel(hcb_size | MPI2_HCB_SIZE_HCB_ENABLE, &ioc->chip->HCBSize); } - drsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "restart the adapter\n", + drsprintk(ioc, printk(MPT2SAS_INFO_FMT "restart the adapter\n", ioc->name)); writel(host_diagnostic & ~MPI2_DIAG_HOLD_IOC_RESET, &ioc->chip->HostDiagnostic); - drsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "disable writes to the " + drsprintk(ioc, printk(MPT2SAS_INFO_FMT "disable writes to the " "diagnostic register\n", ioc->name)); writel(MPI2_WRSEQ_FLUSH_KEY_VALUE, &ioc->chip->WriteSequence); - drsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "Wait for FW to go to the " + drsprintk(ioc, printk(MPT2SAS_INFO_FMT "Wait for FW to go to the " "READY state\n", ioc->name)); ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_READY, 20, sleep_flag); @@ -3348,7 +3997,6 @@ _base_diag_reset(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) goto out; } - _base_restore_msix_table(ioc); printk(MPT2SAS_INFO_FMT "diag reset: SUCCESS\n", ioc->name); return 0; @@ -3370,19 +4018,23 @@ _base_make_ioc_ready(struct MPT2SAS_ADAPTER *ioc, int sleep_flag, enum reset_type type) { u32 ioc_state; + int rc; - dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__)); + if (ioc->pci_error_recovery) + return 0; + ioc_state = mpt2sas_base_get_iocstate(ioc, 0); - dhsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: ioc_state(0x%08x)\n", + dhsprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: ioc_state(0x%08x)\n", ioc->name, __func__, ioc_state)); if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_READY) return 0; if (ioc_state & MPI2_DOORBELL_USED) { - dhsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "unexpected doorbell " + dhsprintk(ioc, printk(MPT2SAS_INFO_FMT "unexpected doorbell " "active!\n", ioc->name)); goto issue_diag_reset; } @@ -3398,11 +4050,15 @@ _base_make_ioc_ready(struct MPT2SAS_ADAPTER *ioc, int sleep_flag, if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_OPERATIONAL) if (!(_base_send_ioc_reset(ioc, - MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET, 15, CAN_SLEEP))) + MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET, 15, CAN_SLEEP))) { + ioc->ioc_reset_count++; return 0; + } issue_diag_reset: - return _base_diag_reset(ioc, CAN_SLEEP); + rc = _base_diag_reset(ioc, CAN_SLEEP); + ioc->ioc_reset_count++; + return rc; } /** @@ -3420,8 +4076,12 @@ _base_make_ioc_operational(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) u32 reply_address; u16 smid; struct _tr_list *delayed_tr, *delayed_tr_next; + u8 hide_flag; + struct adapter_reply_queue *reply_q; + long reply_post_free; + u32 reply_post_free_sz; - dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__)); /* clean the delayed target reset list */ @@ -3431,14 +4091,22 @@ _base_make_ioc_operational(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) kfree(delayed_tr); } + list_for_each_entry_safe(delayed_tr, delayed_tr_next, + &ioc->delayed_tr_volume_list, list) { + list_del(&delayed_tr->list); + kfree(delayed_tr); + } + /* initialize the scsi lookup free list */ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); INIT_LIST_HEAD(&ioc->free_list); smid = 1; for (i = 0; i < ioc->scsiio_depth; i++, smid++) { + INIT_LIST_HEAD(&ioc->scsi_lookup[i].chain_list); ioc->scsi_lookup[i].cb_idx = 0xFF; ioc->scsi_lookup[i].smid = smid; ioc->scsi_lookup[i].scmd = NULL; + ioc->scsi_lookup[i].direct_io = 0; list_add_tail(&ioc->scsi_lookup[i].tracker_list, &ioc->free_list); } @@ -3462,6 +4130,13 @@ _base_make_ioc_operational(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) list_add_tail(&ioc->internal_lookup[i].tracker_list, &ioc->internal_free_list); } + + /* chain pool */ + INIT_LIST_HEAD(&ioc->free_chain_list); + for (i = 0; i < ioc->chain_depth; i++) + list_add_tail(&ioc->chain_lookup[i].tracker_list, + &ioc->free_chain_list); + spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); /* initialize Reply Free Queue */ @@ -3470,21 +4145,47 @@ _base_make_ioc_operational(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) ioc->reply_sz) ioc->reply_free[i] = cpu_to_le32(reply_address); + /* initialize reply queues */ + if (ioc->is_driver_loading) + _base_assign_reply_queues(ioc); + /* initialize Reply Post Free Queue */ - for (i = 0; i < ioc->reply_post_queue_depth; i++) - ioc->reply_post_free[i].Words = ULLONG_MAX; + reply_post_free = (long)ioc->reply_post_free; + reply_post_free_sz = ioc->reply_post_queue_depth * + sizeof(Mpi2DefaultReplyDescriptor_t); + list_for_each_entry(reply_q, &ioc->reply_queue_list, list) { + reply_q->reply_post_host_index = 0; + reply_q->reply_post_free = (Mpi2ReplyDescriptorsUnion_t *) + reply_post_free; + for (i = 0; i < ioc->reply_post_queue_depth; i++) + reply_q->reply_post_free[i].Words = + cpu_to_le64(ULLONG_MAX); + if (!_base_is_controller_msix_enabled(ioc)) + goto skip_init_reply_post_free_queue; + reply_post_free += reply_post_free_sz; + } + skip_init_reply_post_free_queue: r = _base_send_ioc_init(ioc, sleep_flag); if (r) return r; - /* initialize the index's */ + /* initialize reply free host index */ ioc->reply_free_host_index = ioc->reply_free_queue_depth - 1; - ioc->reply_post_host_index = 0; writel(ioc->reply_free_host_index, &ioc->chip->ReplyFreeHostIndex); - writel(0, &ioc->chip->ReplyPostHostIndex); + + /* initialize reply post host index */ + list_for_each_entry(reply_q, &ioc->reply_queue_list, list) { + writel(reply_q->msix_index << MPI2_RPHI_MSIX_INDEX_SHIFT, + &ioc->chip->ReplyPostHostIndex); + if (!_base_is_controller_msix_enabled(ioc)) + goto skip_init_reply_post_host_index; + } + + skip_init_reply_post_host_index: _base_unmask_interrupts(ioc); + r = _base_event_notification(ioc, sleep_flag); if (r) return r; @@ -3492,6 +4193,20 @@ _base_make_ioc_operational(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) if (sleep_flag == CAN_SLEEP) _base_static_config_pages(ioc); + + if (ioc->is_driver_loading) { + if (ioc->is_warpdrive && ioc->manu_pg10.OEMIdentifier + == 0x80) { + hide_flag = (u8) ( + le32_to_cpu(ioc->manu_pg10.OEMSpecificFlags0) & + MFG_PAGE10_HIDE_SSDS_MASK); + if (hide_flag != MFG_PAGE10_HIDE_SSDS_MASK) + ioc->mfg_pg10_hide_flag = hide_flag; + } + ioc->wait_for_discovery_to_complete = + _base_determine_wait_on_discovery(ioc); + return r; /* scan_start and scan_finished support */ + } r = _base_send_port_enable(ioc, sleep_flag); if (r) return r; @@ -3510,22 +4225,28 @@ mpt2sas_base_free_resources(struct MPT2SAS_ADAPTER *ioc) { struct pci_dev *pdev = ioc->pdev; - dexitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, + dexitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__)); - _base_mask_interrupts(ioc); - _base_make_ioc_ready(ioc, CAN_SLEEP, SOFT_RESET); - if (ioc->pci_irq) { - synchronize_irq(pdev->irq); - free_irq(ioc->pci_irq, ioc); + if (ioc->chip_phys && ioc->chip) { + _base_mask_interrupts(ioc); + ioc->shost_recovery = 1; + _base_make_ioc_ready(ioc, CAN_SLEEP, SOFT_RESET); + ioc->shost_recovery = 0; } + + _base_free_irq(ioc); _base_disable_msix(ioc); - if (ioc->chip_phys) + + if (ioc->chip_phys && ioc->chip) iounmap(ioc->chip); - ioc->pci_irq = -1; ioc->chip_phys = 0; - pci_release_selected_regions(ioc->pdev, ioc->bars); - pci_disable_device(pdev); + + if (pci_is_enabled(pdev)) { + pci_release_selected_regions(ioc->pdev, ioc->bars); + pci_disable_pcie_error_reporting(pdev); + pci_disable_device(pdev); + } return; } @@ -3539,13 +4260,49 @@ int mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc) { int r, i; + int cpu_id, last_cpu_id = 0; - dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__)); + /* setup cpu_msix_table */ + ioc->cpu_count = num_online_cpus(); + for_each_online_cpu(cpu_id) + last_cpu_id = cpu_id; + ioc->cpu_msix_table_sz = last_cpu_id + 1; + ioc->cpu_msix_table = kzalloc(ioc->cpu_msix_table_sz, GFP_KERNEL); + ioc->reply_queue_count = 1; + if (!ioc->cpu_msix_table) { + dfailprintk(ioc, printk(MPT2SAS_INFO_FMT "allocation for " + "cpu_msix_table failed!!!\n", ioc->name)); + r = -ENOMEM; + goto out_free_resources; + } + + if (ioc->is_warpdrive) { + ioc->reply_post_host_index = kcalloc(ioc->cpu_msix_table_sz, + sizeof(resource_size_t *), GFP_KERNEL); + if (!ioc->reply_post_host_index) { + dfailprintk(ioc, printk(MPT2SAS_INFO_FMT "allocation " + "for cpu_msix_table failed!!!\n", ioc->name)); + r = -ENOMEM; + goto out_free_resources; + } + } + r = mpt2sas_base_map_resources(ioc); if (r) - return r; + goto out_free_resources; + + if (ioc->is_warpdrive) { + ioc->reply_post_host_index[0] = + (resource_size_t *)&ioc->chip->ReplyPostHostIndex; + + for (i = 1; i < ioc->cpu_msix_table_sz; i++) + ioc->reply_post_host_index[i] = (resource_size_t *) + ((u8 *)&ioc->chip->Doorbell + (0x4000 + ((i - 1) + * 4))); + } pci_set_drvdata(ioc->pdev, ioc->shost); r = _base_get_ioc_facts(ioc, CAN_SLEEP); @@ -3557,9 +4314,11 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc) goto out_free_resources; ioc->pfacts = kcalloc(ioc->facts.NumberOfPorts, - sizeof(Mpi2PortFactsReply_t), GFP_KERNEL); - if (!ioc->pfacts) + sizeof(struct mpt2sas_port_facts), GFP_KERNEL); + if (!ioc->pfacts) { + r = -ENOMEM; goto out_free_resources; + } for (i = 0 ; i < ioc->facts.NumberOfPorts; i++) { r = _base_get_port_facts(ioc, i, CAN_SLEEP); @@ -3572,12 +4331,33 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc) goto out_free_resources; init_waitqueue_head(&ioc->reset_wq); + /* allocate memory pd handle bitmask list */ + ioc->pd_handles_sz = (ioc->facts.MaxDevHandle / 8); + if (ioc->facts.MaxDevHandle % 8) + ioc->pd_handles_sz++; + ioc->pd_handles = kzalloc(ioc->pd_handles_sz, + GFP_KERNEL); + if (!ioc->pd_handles) { + r = -ENOMEM; + goto out_free_resources; + } + ioc->blocking_handles = kzalloc(ioc->pd_handles_sz, + GFP_KERNEL); + if (!ioc->blocking_handles) { + r = -ENOMEM; + goto out_free_resources; + } + ioc->fwfault_debug = mpt2sas_fwfault_debug; /* base internal command bits */ mutex_init(&ioc->base_cmds.mutex); ioc->base_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL); ioc->base_cmds.status = MPT2_CMD_NOT_USED; + /* port_enable command bits */ + ioc->port_enable_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL); + ioc->port_enable_cmds.status = MPT2_CMD_NOT_USED; + /* transport internal command bits */ ioc->transport_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL); ioc->transport_cmds.status = MPT2_CMD_NOT_USED; @@ -3600,9 +4380,25 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc) /* ctl module internal command bits */ ioc->ctl_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL); + ioc->ctl_cmds.sense = kzalloc(SCSI_SENSE_BUFFERSIZE, GFP_KERNEL); ioc->ctl_cmds.status = MPT2_CMD_NOT_USED; mutex_init(&ioc->ctl_cmds.mutex); + if (!ioc->base_cmds.reply || !ioc->transport_cmds.reply || + !ioc->scsih_cmds.reply || !ioc->tm_cmds.reply || + !ioc->config_cmds.reply || !ioc->ctl_cmds.reply || + !ioc->ctl_cmds.sense) { + r = -ENOMEM; + goto out_free_resources; + } + + if (!ioc->base_cmds.reply || !ioc->transport_cmds.reply || + !ioc->scsih_cmds.reply || !ioc->tm_cmds.reply || + !ioc->config_cmds.reply || !ioc->ctl_cmds.reply) { + r = -ENOMEM; + goto out_free_resources; + } + for (i = 0; i < MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++) ioc->event_masks[i] = -1; @@ -3616,15 +4412,13 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc) _base_unmask_events(ioc, MPI2_EVENT_IR_VOLUME); _base_unmask_events(ioc, MPI2_EVENT_IR_PHYSICAL_DISK); _base_unmask_events(ioc, MPI2_EVENT_IR_OPERATION_STATUS); - _base_unmask_events(ioc, MPI2_EVENT_TASK_SET_FULL); _base_unmask_events(ioc, MPI2_EVENT_LOG_ENTRY_ADDED); r = _base_make_ioc_operational(ioc, CAN_SLEEP); if (r) goto out_free_resources; - mpt2sas_base_start_watchdog(ioc); - if (diag_buffer_enable != 0) - mpt2sas_enable_diag_buffer(ioc, diag_buffer_enable); + ioc->non_operational_loop = 0; + return 0; out_free_resources: @@ -3633,15 +4427,24 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc) mpt2sas_base_free_resources(ioc); _base_release_memory_pools(ioc); pci_set_drvdata(ioc->pdev, NULL); + kfree(ioc->cpu_msix_table); + if (ioc->is_warpdrive) + kfree(ioc->reply_post_host_index); + kfree(ioc->pd_handles); + kfree(ioc->blocking_handles); kfree(ioc->tm_cmds.reply); kfree(ioc->transport_cmds.reply); + kfree(ioc->scsih_cmds.reply); kfree(ioc->config_cmds.reply); kfree(ioc->base_cmds.reply); + kfree(ioc->port_enable_cmds.reply); kfree(ioc->ctl_cmds.reply); + kfree(ioc->ctl_cmds.sense); kfree(ioc->pfacts); ioc->ctl_cmds.reply = NULL; ioc->base_cmds.reply = NULL; ioc->tm_cmds.reply = NULL; + ioc->scsih_cmds.reply = NULL; ioc->transport_cmds.reply = NULL; ioc->config_cmds.reply = NULL; ioc->pfacts = NULL; @@ -3659,18 +4462,26 @@ void mpt2sas_base_detach(struct MPT2SAS_ADAPTER *ioc) { - dexitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, + dexitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__)); mpt2sas_base_stop_watchdog(ioc); mpt2sas_base_free_resources(ioc); _base_release_memory_pools(ioc); pci_set_drvdata(ioc->pdev, NULL); + kfree(ioc->cpu_msix_table); + if (ioc->is_warpdrive) + kfree(ioc->reply_post_host_index); + kfree(ioc->pd_handles); + kfree(ioc->blocking_handles); kfree(ioc->pfacts); kfree(ioc->ctl_cmds.reply); + kfree(ioc->ctl_cmds.sense); kfree(ioc->base_cmds.reply); + kfree(ioc->port_enable_cmds.reply); kfree(ioc->tm_cmds.reply); kfree(ioc->transport_cmds.reply); + kfree(ioc->scsih_cmds.reply); kfree(ioc->config_cmds.reply); } @@ -3689,13 +4500,15 @@ mpt2sas_base_detach(struct MPT2SAS_ADAPTER *ioc) static void _base_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase) { + mpt2sas_scsih_reset_handler(ioc, reset_phase); + mpt2sas_ctl_reset_handler(ioc, reset_phase); switch (reset_phase) { case MPT2_IOC_PRE_RESET: - dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " + dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: " "MPT2_IOC_PRE_RESET\n", ioc->name, __func__)); break; case MPT2_IOC_AFTER_RESET: - dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " + dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: " "MPT2_IOC_AFTER_RESET\n", ioc->name, __func__)); if (ioc->transport_cmds.status & MPT2_CMD_PENDING) { ioc->transport_cmds.status |= MPT2_CMD_RESET; @@ -3707,20 +4520,32 @@ _base_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase) mpt2sas_base_free_smid(ioc, ioc->base_cmds.smid); complete(&ioc->base_cmds.done); } + if (ioc->port_enable_cmds.status & MPT2_CMD_PENDING) { + ioc->port_enable_failed = 1; + ioc->port_enable_cmds.status |= MPT2_CMD_RESET; + mpt2sas_base_free_smid(ioc, ioc->port_enable_cmds.smid); + if (ioc->is_driver_loading) { + ioc->start_scan_failed = + MPI2_IOCSTATUS_INTERNAL_ERROR; + ioc->start_scan = 0; + ioc->port_enable_cmds.status = + MPT2_CMD_NOT_USED; + } else + complete(&ioc->port_enable_cmds.done); + + } if (ioc->config_cmds.status & MPT2_CMD_PENDING) { ioc->config_cmds.status |= MPT2_CMD_RESET; mpt2sas_base_free_smid(ioc, ioc->config_cmds.smid); - ioc->config_cmds.smid = USHORT_MAX; + ioc->config_cmds.smid = USHRT_MAX; complete(&ioc->config_cmds.done); } break; case MPT2_IOC_DONE_RESET: - dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " + dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: " "MPT2_IOC_DONE_RESET\n", ioc->name, __func__)); break; } - mpt2sas_scsih_reset_handler(ioc, reset_phase); - mpt2sas_ctl_reset_handler(ioc, reset_phase); } /** @@ -3757,7 +4582,7 @@ _wait_for_commands_to_complete(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) return; /* wait for pending commands to complete */ - wait_event_timeout(ioc->reset_wq, ioc->pending_io_count == 0, 3 * HZ); + wait_event_timeout(ioc->reset_wq, ioc->pending_io_count == 0, 10 * HZ); } /** @@ -3775,19 +4600,37 @@ mpt2sas_base_hard_reset_handler(struct MPT2SAS_ADAPTER *ioc, int sleep_flag, int r; unsigned long flags; - dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: enter\n", ioc->name, + dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter\n", ioc->name, __func__)); + if (ioc->pci_error_recovery) { + printk(MPT2SAS_ERR_FMT "%s: pci error recovery reset\n", + ioc->name, __func__); + r = 0; + goto out_unlocked; + } + if (mpt2sas_fwfault_debug) mpt2sas_halt_firmware(ioc); - spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); - if (ioc->shost_recovery) { - spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); - printk(MPT2SAS_ERR_FMT "%s: busy\n", - ioc->name, __func__); - return -EBUSY; + /* TODO - What we really should be doing is pulling + * out all the code associated with NO_SLEEP; its never used. + * That is legacy code from mpt fusion driver, ported over. + * I will leave this BUG_ON here for now till its been resolved. + */ + BUG_ON(sleep_flag == NO_SLEEP); + + /* wait for an active reset in progress to complete */ + if (!mutex_trylock(&ioc->reset_in_progress_mutex)) { + do { + ssleep(1); + } while (ioc->shost_recovery == 1); + dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: exit\n", ioc->name, + __func__)); + return ioc->ioc_reset_in_progress_status; } + + spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); ioc->shost_recovery = 1; spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); @@ -3798,18 +4641,30 @@ mpt2sas_base_hard_reset_handler(struct MPT2SAS_ADAPTER *ioc, int sleep_flag, if (r) goto out; _base_reset_handler(ioc, MPT2_IOC_AFTER_RESET); + + /* If this hard reset is called while port enable is active, then + * there is no reason to call make_ioc_operational + */ + if (ioc->is_driver_loading && ioc->port_enable_failed) { + ioc->remove_host = 1; + r = -EFAULT; + goto out; + } r = _base_make_ioc_operational(ioc, sleep_flag); if (!r) _base_reset_handler(ioc, MPT2_IOC_DONE_RESET); out: - dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: %s\n", + dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: %s\n", ioc->name, __func__, ((r == 0) ? "SUCCESS" : "FAILED"))); spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); + ioc->ioc_reset_in_progress_status = r; ioc->shost_recovery = 0; spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); + mutex_unlock(&ioc->reset_in_progress_mutex); - if (!r) - _base_reset_handler(ioc, MPT2_IOC_RUNNING); + out_unlocked: + dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: exit\n", ioc->name, + __func__)); return r; } diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.h b/drivers/scsi/mpt2sas/mpt2sas_base.h index bb4f14656af..fd3b998c75b 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_base.h +++ b/drivers/scsi/mpt2sas/mpt2sas_base.h @@ -3,7 +3,7 @@ * for access to MPT (Message Passing Technology) firmware. * * This code is based on drivers/scsi/mpt2sas/mpt2_base.h - * Copyright (C) 2007-2009 LSI Corporation + * Copyright (C) 2007-2013 LSI Corporation * (mailto:DL-MPTFusionLinux@lsi.com) * * This program is free software; you can redistribute it and/or @@ -69,10 +69,10 @@ #define MPT2SAS_DRIVER_NAME "mpt2sas" #define MPT2SAS_AUTHOR "LSI Corporation <DL-MPTFusionLinux@lsi.com>" #define MPT2SAS_DESCRIPTION "LSI MPT Fusion SAS 2.0 Device Driver" -#define MPT2SAS_DRIVER_VERSION "03.100.03.00" -#define MPT2SAS_MAJOR_VERSION 03 +#define MPT2SAS_DRIVER_VERSION "16.100.00.00" +#define MPT2SAS_MAJOR_VERSION 16 #define MPT2SAS_MINOR_VERSION 100 -#define MPT2SAS_BUILD_VERSION 03 +#define MPT2SAS_BUILD_VERSION 00 #define MPT2SAS_RELEASE_VERSION 00 /* @@ -101,7 +101,8 @@ #define MPT_NAME_LENGTH 32 /* generic length of strings */ #define MPT_STRING_LENGTH 64 -#define MPT_MAX_CALLBACKS 16 +#define MPT_MAX_CALLBACKS 16 + #define CAN_SLEEP 1 #define NO_SLEEP 0 @@ -119,13 +120,11 @@ #define MPT2_IOC_PRE_RESET 1 /* prior to host reset */ #define MPT2_IOC_AFTER_RESET 2 /* just after host reset */ #define MPT2_IOC_DONE_RESET 3 /* links re-initialized */ -#define MPT2_IOC_RUNNING 4 /* shost running */ /* * logging format */ #define MPT2SAS_FMT "%s: " -#define MPT2SAS_DEBUG_FMT KERN_DEBUG MPT2SAS_FMT #define MPT2SAS_INFO_FMT KERN_INFO MPT2SAS_FMT #define MPT2SAS_NOTE_FMT KERN_NOTICE MPT2SAS_FMT #define MPT2SAS_WARN_FMT KERN_WARNING MPT2SAS_FMT @@ -156,6 +155,72 @@ #define MPT2SAS_DELL_6GBPS_SAS_SSDID 0x1F22 /* + * Intel HBA branding + */ +#define MPT2SAS_INTEL_RMS25JB080_BRANDING \ + "Intel(R) Integrated RAID Module RMS25JB080" +#define MPT2SAS_INTEL_RMS25JB040_BRANDING \ + "Intel(R) Integrated RAID Module RMS25JB040" +#define MPT2SAS_INTEL_RMS25KB080_BRANDING \ + "Intel(R) Integrated RAID Module RMS25KB080" +#define MPT2SAS_INTEL_RMS25KB040_BRANDING \ + "Intel(R) Integrated RAID Module RMS25KB040" +#define MPT2SAS_INTEL_RMS25LB040_BRANDING \ + "Intel(R) Integrated RAID Module RMS25LB040" +#define MPT2SAS_INTEL_RMS25LB080_BRANDING \ + "Intel(R) Integrated RAID Module RMS25LB080" +#define MPT2SAS_INTEL_RMS2LL080_BRANDING \ + "Intel Integrated RAID Module RMS2LL080" +#define MPT2SAS_INTEL_RMS2LL040_BRANDING \ + "Intel Integrated RAID Module RMS2LL040" +#define MPT2SAS_INTEL_RS25GB008_BRANDING \ + "Intel(R) RAID Controller RS25GB008" +#define MPT2SAS_INTEL_SSD910_BRANDING \ + "Intel(R) SSD 910 Series" +/* + * Intel HBA SSDIDs + */ +#define MPT2SAS_INTEL_RMS25JB080_SSDID 0x3516 +#define MPT2SAS_INTEL_RMS25JB040_SSDID 0x3517 +#define MPT2SAS_INTEL_RMS25KB080_SSDID 0x3518 +#define MPT2SAS_INTEL_RMS25KB040_SSDID 0x3519 +#define MPT2SAS_INTEL_RMS25LB040_SSDID 0x351A +#define MPT2SAS_INTEL_RMS25LB080_SSDID 0x351B +#define MPT2SAS_INTEL_RMS2LL080_SSDID 0x350E +#define MPT2SAS_INTEL_RMS2LL040_SSDID 0x350F +#define MPT2SAS_INTEL_RS25GB008_SSDID 0x3000 +#define MPT2SAS_INTEL_SSD910_SSDID 0x3700 + +/* + * HP HBA branding + */ +#define MPT2SAS_HP_3PAR_SSVID 0x1590 +#define MPT2SAS_HP_2_4_INTERNAL_BRANDING "HP H220 Host Bus Adapter" +#define MPT2SAS_HP_2_4_EXTERNAL_BRANDING "HP H221 Host Bus Adapter" +#define MPT2SAS_HP_1_4_INTERNAL_1_4_EXTERNAL_BRANDING "HP H222 Host Bus Adapter" +#define MPT2SAS_HP_EMBEDDED_2_4_INTERNAL_BRANDING "HP H220i Host Bus Adapter" +#define MPT2SAS_HP_DAUGHTER_2_4_INTERNAL_BRANDING "HP H210i Host Bus Adapter" + +/* + * HO HBA SSDIDs + */ +#define MPT2SAS_HP_2_4_INTERNAL_SSDID 0x0041 +#define MPT2SAS_HP_2_4_EXTERNAL_SSDID 0x0042 +#define MPT2SAS_HP_1_4_INTERNAL_1_4_EXTERNAL_SSDID 0x0043 +#define MPT2SAS_HP_EMBEDDED_2_4_INTERNAL_SSDID 0x0044 +#define MPT2SAS_HP_DAUGHTER_2_4_INTERNAL_SSDID 0x0046 + +/* + * WarpDrive Specific Log codes + */ + +#define MPT2_WARPDRIVE_LOGENTRY (0x8002) +#define MPT2_WARPDRIVE_LC_SSDT (0x41) +#define MPT2_WARPDRIVE_LC_SSDLW (0x43) +#define MPT2_WARPDRIVE_LC_SSDLF (0x44) +#define MPT2_WARPDRIVE_LC_BRMF (0x4D) + +/* * per target private data */ #define MPT_TARGET_FLAGS_RAID_COMPONENT 0x01 @@ -166,6 +231,7 @@ * struct MPT2SAS_TARGET - starget private hostdata * @starget: starget object * @sas_address: target sas address + * @raid_device: raid_device pointer to access volume data * @handle: device handle * @num_luns: number luns * @flags: MPT_TARGET_FLAGS_XXX flags @@ -175,6 +241,7 @@ struct MPT2SAS_TARGET { struct scsi_target *starget; u64 sas_address; + struct _raid_device *raid_device; u16 handle; int num_luns; u32 flags; @@ -182,6 +249,7 @@ struct MPT2SAS_TARGET { u8 tm_busy; }; + /* * per device private data */ @@ -229,6 +297,12 @@ typedef struct _MPI2_CONFIG_PAGE_MAN_10 { MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_10, Mpi2ManufacturingPage10_t, MPI2_POINTER pMpi2ManufacturingPage10_t; +#define MFG_PAGE10_HIDE_SSDS_MASK (0x00000003) +#define MFG_PAGE10_HIDE_ALL_DISKS (0x00) +#define MFG_PAGE10_EXPOSE_ALL_DISKS (0x01) +#define MFG_PAGE10_HIDE_IF_VOL_PRESENT (0x02) + + struct MPT2SAS_DEVICE { struct MPT2SAS_TARGET *sas_target; unsigned int lun; @@ -249,6 +323,7 @@ struct MPT2SAS_DEVICE { * @mutex: mutex * @done: completion * @reply: reply message pointer + * @sense: sense data * @status: MPT2_CMD_XXX status * @smid: system message id */ @@ -256,20 +331,11 @@ struct _internal_cmd { struct mutex mutex; struct completion done; void *reply; + void *sense; u16 status; u16 smid; }; -/* - * SAS Topology Structures - */ - -#define MPTSAS_STATE_TR_SEND 0x0001 -#define MPTSAS_STATE_TR_COMPLETE 0x0002 -#define MPTSAS_STATE_CNTRL_SEND 0x0004 -#define MPTSAS_STATE_CNTRL_COMPLETE 0x0008 - -#define MPT2SAS_REQ_SAS_CNTRL 0x0010 /** * struct _sas_device - attached device information @@ -287,7 +353,7 @@ struct _internal_cmd { * @id: target id * @channel: target channel * @slot: number number - * @hidden_raid_component: set to 1 when this is a raid member + * @phy: phy identifier provided in sas device page 0 * @responding: used in _scsih_sas_device_mark_responding */ struct _sas_device { @@ -305,9 +371,8 @@ struct _sas_device { int id; int channel; u16 slot; - u8 hidden_raid_component; + u8 phy; u8 responding; - u16 state; }; /** @@ -317,25 +382,43 @@ struct _sas_device { * @sdev: scsi device struct (volumes are single lun) * @wwid: unique identifier for the volume * @handle: device handle + * @block_size: Block size of the volume * @id: target id * @channel: target channel * @volume_type: the raid level * @device_info: bitfield provides detailed info about the hidden components * @num_pds: number of hidden raid components * @responding: used in _scsih_raid_device_mark_responding + * @percent_complete: resync percent complete + * @direct_io_enabled: Whether direct io to PDs are allowed or not + * @stripe_exponent: X where 2powX is the stripe sz in blocks + * @block_exponent: X where 2powX is the block sz in bytes + * @max_lba: Maximum number of LBA in the volume + * @stripe_sz: Stripe Size of the volume + * @device_info: Device info of the volume member disk + * @pd_handle: Array of handles of the physical drives for direct I/O in le16 */ +#define MPT_MAX_WARPDRIVE_PDS 8 struct _raid_device { struct list_head list; struct scsi_target *starget; struct scsi_device *sdev; u64 wwid; u16 handle; + u16 block_sz; int id; int channel; u8 volume_type; - u32 device_info; u8 num_pds; u8 responding; + u8 percent_complete; + u8 direct_io_enabled; + u8 stripe_exponent; + u8 block_exponent; + u64 max_lba; + u32 stripe_sz; + u32 device_info; + u16 pd_handle[MPT_MAX_WARPDRIVE_PDS]; }; /** @@ -376,6 +459,7 @@ struct _sas_port { * @phy_id: unique phy id * @handle: device handle for this phy * @attached_handle: device handle for attached device + * @phy_belongs_to_port: port has been created for this phy */ struct _sas_phy { struct list_head port_siblings; @@ -385,6 +469,7 @@ struct _sas_phy { u8 phy_id; u16 handle; u16 attached_handle; + u8 phy_belongs_to_port; }; /** @@ -426,17 +511,44 @@ enum reset_type { }; /** - * struct request_tracker - firmware request tracker + * struct chain_tracker - firmware chain tracker + * @chain_buffer: chain buffer + * @chain_buffer_dma: physical address + * @tracker_list: list of free request (ioc->free_chain_list) + */ +struct chain_tracker { + void *chain_buffer; + dma_addr_t chain_buffer_dma; + struct list_head tracker_list; +}; + +/** + * struct scsiio_tracker - scsi mf request tracker * @smid: system message id * @scmd: scsi request pointer * @cb_idx: callback index + * @direct_io: To indicate whether I/O is direct (WARPDRIVE) * @chain_list: list of chains associated to this IO * @tracker_list: list of free request (ioc->free_list) */ -struct request_tracker { +struct scsiio_tracker { u16 smid; struct scsi_cmnd *scmd; u8 cb_idx; + u8 direct_io; + struct list_head chain_list; + struct list_head tracker_list; +}; + +/** + * struct request_tracker - firmware request tracker + * @smid: system message id + * @cb_idx: callback index + * @tracker_list: list of free request (ioc->free_list) + */ +struct request_tracker { + u16 smid; + u8 cb_idx; struct list_head tracker_list; }; @@ -454,17 +566,96 @@ struct _tr_list { typedef void (*MPT_ADD_SGE)(void *paddr, u32 flags_length, dma_addr_t dma_addr); /** + * struct adapter_reply_queue - the reply queue struct + * @ioc: per adapter object + * @msix_index: msix index into vector table + * @vector: irq vector + * @reply_post_host_index: head index in the pool where FW completes IO + * @reply_post_free: reply post base virt address + * @name: the name registered to request_irq() + * @busy: isr is actively processing replies on another cpu + * @list: this list +*/ +struct adapter_reply_queue { + struct MPT2SAS_ADAPTER *ioc; + u8 msix_index; + unsigned int vector; + u32 reply_post_host_index; + Mpi2ReplyDescriptorsUnion_t *reply_post_free; + char name[MPT_NAME_LENGTH]; + atomic_t busy; + struct list_head list; +}; + +/* IOC Facts and Port Facts converted from little endian to cpu */ +union mpi2_version_union { + MPI2_VERSION_STRUCT Struct; + u32 Word; +}; + +struct mpt2sas_facts { + u16 MsgVersion; + u16 HeaderVersion; + u8 IOCNumber; + u8 VP_ID; + u8 VF_ID; + u16 IOCExceptions; + u16 IOCStatus; + u32 IOCLogInfo; + u8 MaxChainDepth; + u8 WhoInit; + u8 NumberOfPorts; + u8 MaxMSIxVectors; + u16 RequestCredit; + u16 ProductID; + u32 IOCCapabilities; + union mpi2_version_union FWVersion; + u16 IOCRequestFrameSize; + u16 Reserved3; + u16 MaxInitiators; + u16 MaxTargets; + u16 MaxSasExpanders; + u16 MaxEnclosures; + u16 ProtocolFlags; + u16 HighPriorityCredit; + u16 MaxReplyDescriptorPostQueueDepth; + u8 ReplyFrameSize; + u8 MaxVolumes; + u16 MaxDevHandle; + u16 MaxPersistentEntries; + u16 MinDevHandle; +}; + +struct mpt2sas_port_facts { + u8 PortNumber; + u8 VP_ID; + u8 VF_ID; + u8 PortType; + u16 MaxPostedCmdBuffers; +}; + +/** + * enum mutex_type - task management mutex type + * @TM_MUTEX_OFF: mutex is not required becuase calling function is acquiring it + * @TM_MUTEX_ON: mutex is required + */ +enum mutex_type { + TM_MUTEX_OFF = 0, + TM_MUTEX_ON = 1, +}; + +typedef void (*MPT2SAS_FLUSH_RUNNING_CMDS)(struct MPT2SAS_ADAPTER *ioc); +/** * struct MPT2SAS_ADAPTER - per adapter struct * @list: ioc_list * @shost: shost object * @id: unique adapter id - * @pci_irq: irq number + * @cpu_count: number online cpus * @name: generic ioc string * @tmp_string: tmp string used for logging * @pdev: pci pdev object * @chip: memory mapped register space * @chip_phys: physical addrss prior to mapping - * @pio_chip: I/O mapped register space * @logging_level: see mpt2sas_debug.h * @fwfault_debug: debuging FW timeouts * @ir_firmware: IR firmware present @@ -483,13 +674,20 @@ typedef void (*MPT_ADD_SGE)(void *paddr, u32 flags_length, dma_addr_t dma_addr); * @shost_recovery: host reset in progress * @ioc_reset_in_progress_lock: * @ioc_link_reset_in_progress: phy/hard reset in progress - * @ignore_loginfos: ignore loginfos during task managment + * @ignore_loginfos: ignore loginfos during task management * @remove_host: flag for when driver unloads, to avoid sending dev resets - * @wait_for_port_enable_to_complete: + * @pci_error_recovery: flag to prevent ioc access until slot reset completes + * @wait_for_discovery_to_complete: flag set at driver load time when + * waiting on reporting devices + * @is_driver_loading: flag set at driver load time + * @port_enable_failed: flag set when port enable has failed + * @start_scan: flag set from scan_start callback, cleared from _mpt2sas_fw_work + * @start_scan_failed: means port enable failed, return's the ioc_status * @msix_enable: flag indicating msix is enabled * @msix_vector_count: number msix vectors - * @msix_table: virt address to the msix table - * @msix_table_backup: backup msix table + * @cpu_msix_table: table for mapping cpus to msix index + * @cpu_msix_table_sz: table size + * @schedule_dead_ioc_flush_running_cmds: callback to flush pending commands * @scsi_io_cb_idx: shost generated commands * @tm_cb_idx: task management commands * @scsih_cb_idx: scsih internal commands @@ -497,6 +695,8 @@ typedef void (*MPT_ADD_SGE)(void *paddr, u32 flags_length, dma_addr_t dma_addr); * @ctl_cb_idx: clt internal commands * @base_cb_idx: base internal commands * @config_cb_idx: base internal commands + * @tm_tr_cb_idx : device removal target reset handshake + * @tm_tr_volume_cb_idx : volume removal target reset * @base_cmds: * @transport_cmds: * @scsih_cmds: @@ -525,6 +725,10 @@ typedef void (*MPT_ADD_SGE)(void *paddr, u32 flags_length, dma_addr_t dma_addr); * @sas_device_lock: * @io_missing_delay: time for IO completed by fw when PDR enabled * @device_missing_delay: time for device missing by fw when PDR enabled + * @sas_id : used for setting volume target IDs + * @blocking_handles: bitmask used to identify which devices need blocking + * @pd_handles : bitmask for PD handles + * @pd_handles_sz : size of pd_handle bitmask * @config_page_sz: config page size * @config_page: reserve memory for config page payload * @config_page_dma: @@ -575,20 +779,22 @@ typedef void (*MPT_ADD_SGE)(void *paddr, u32 flags_length, dma_addr_t dma_addr); * @reply_post_queue_depth: reply post queue depth * @reply_post_free: pool for reply post (64bit descriptor) * @reply_post_free_dma: - * @reply_post_free_dma_pool: + * @reply_queue_count: number of reply queue's + * @reply_queue_list: link list contaning the reply queue info * @reply_post_host_index: head index in the pool where FW completes IO + * @delayed_tr_list: target reset link list + * @delayed_tr_volume_list: volume target reset link list */ struct MPT2SAS_ADAPTER { struct list_head list; struct Scsi_Host *shost; u8 id; - u32 pci_irq; + int cpu_count; char name[MPT_NAME_LENGTH]; char tmp_string[MPT_STRING_LENGTH]; struct pci_dev *pdev; Mpi2SystemInterfaceRegs_t __iomem *chip; - unsigned long chip_phys; - unsigned long pio_chip; + resource_size_t chip_phys; int logging_level; int fwfault_debug; u8 ir_firmware; @@ -603,24 +809,39 @@ struct MPT2SAS_ADAPTER { /* fw event handler */ char firmware_event_name[20]; struct workqueue_struct *firmware_event_thread; - u8 fw_events_off; spinlock_t fw_event_lock; struct list_head fw_event_list; /* misc flags */ int aen_event_read_flag; u8 broadcast_aen_busy; + u16 broadcast_aen_pending; u8 shost_recovery; + + struct mutex reset_in_progress_mutex; spinlock_t ioc_reset_in_progress_lock; u8 ioc_link_reset_in_progress; + u8 ioc_reset_in_progress_status; + u8 ignore_loginfos; u8 remove_host; - u8 wait_for_port_enable_to_complete; + u8 pci_error_recovery; + u8 wait_for_discovery_to_complete; + struct completion port_enable_done; + u8 is_driver_loading; + u8 port_enable_failed; + + u8 start_scan; + u16 start_scan_failed; u8 msix_enable; u16 msix_vector_count; - u32 *msix_table; - u32 *msix_table_backup; + u8 *cpu_msix_table; + resource_size_t **reply_post_host_index; + u16 cpu_msix_table_sz; + u32 ioc_reset_count; + MPT2SAS_FLUSH_RUNNING_CMDS schedule_dead_ioc_flush_running_cmds; + u32 non_operational_loop; /* internal commands, callback index */ u8 scsi_io_cb_idx; @@ -629,10 +850,13 @@ struct MPT2SAS_ADAPTER { u8 scsih_cb_idx; u8 ctl_cb_idx; u8 base_cb_idx; + u8 port_enable_cb_idx; u8 config_cb_idx; u8 tm_tr_cb_idx; + u8 tm_tr_volume_cb_idx; u8 tm_sas_control_cb_idx; struct _internal_cmd base_cmds; + struct _internal_cmd port_enable_cmds; struct _internal_cmd transport_cmds; struct _internal_cmd scsih_cmds; struct _internal_cmd tm_cmds; @@ -648,8 +872,8 @@ struct MPT2SAS_ADAPTER { u32 event_masks[MPI2_EVENT_NOTIFY_EVENTMASK_WORDS]; /* static config pages */ - Mpi2IOCFactsReply_t facts; - Mpi2PortFactsReply_t *pfacts; + struct mpt2sas_facts facts; + struct mpt2sas_port_facts *pfacts; Mpi2ManufacturingPage0_t manu_pg0; Mpi2BiosPage2_t bios_pg2; Mpi2BiosPage3_t bios_pg3; @@ -673,6 +897,9 @@ struct MPT2SAS_ADAPTER { u8 io_missing_delay; u16 device_missing_delay; int sas_id; + void *blocking_handles; + void *pd_handles; + u16 pd_handles_sz; /* config page */ u16 config_page_sz; @@ -687,20 +914,23 @@ struct MPT2SAS_ADAPTER { u8 *request; dma_addr_t request_dma; u32 request_dma_sz; - struct request_tracker *scsi_lookup; - spinlock_t scsi_lookup_lock; + struct scsiio_tracker *scsi_lookup; + ulong scsi_lookup_pages; + spinlock_t scsi_lookup_lock; struct list_head free_list; int pending_io_count; wait_queue_head_t reset_wq; /* chain */ - u8 *chain; - dma_addr_t chain_dma; + struct chain_tracker *chain_lookup; + struct list_head free_chain_list; + struct dma_pool *chain_dma_pool; + ulong chain_pages; u16 max_sges_in_main_message; u16 max_sges_in_chain_message; u16 chains_needed_per_io; u16 chain_offset_value_for_main_message; - u16 chain_depth; + u32 chain_depth; /* hi-priority queue */ u16 hi_priority_smid; @@ -727,11 +957,13 @@ struct MPT2SAS_ADAPTER { u16 reply_sz; u8 *reply; dma_addr_t reply_dma; + u32 reply_dma_max_address; + u32 reply_dma_min_address; struct dma_pool *reply_dma_pool; /* reply free queue */ u16 reply_free_queue_depth; - u32 *reply_free; + __le32 *reply_free; dma_addr_t reply_free_dma; struct dma_pool *reply_free_dma_pool; u32 reply_free_host_index; @@ -741,9 +973,11 @@ struct MPT2SAS_ADAPTER { Mpi2ReplyDescriptorsUnion_t *reply_post_free; dma_addr_t reply_post_free_dma; struct dma_pool *reply_post_free_dma_pool; - u32 reply_post_host_index; + u8 reply_queue_count; + struct list_head reply_queue_list; struct list_head delayed_tr_list; + struct list_head delayed_tr_volume_list; /* diag buffer support */ u8 *diag_buffer[MPI2_DIAG_BUF_TYPE_COUNT]; @@ -754,6 +988,13 @@ struct MPT2SAS_ADAPTER { Mpi2ManufacturingPage10_t manu_pg10; u32 product_specific[MPI2_DIAG_BUF_TYPE_COUNT][23]; u32 diagnostic_flags[MPI2_DIAG_BUF_TYPE_COUNT]; + u32 ring_buffer_offset; + u32 ring_buffer_sz; + u8 is_warpdrive; + u8 hide_ir_msg; + u8 mfg_pg10_hide_flag; + u8 hide_drives; + }; typedef u8 (*MPT_CALLBACK)(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, @@ -777,6 +1018,7 @@ void *mpt2sas_base_get_sense_buffer(struct MPT2SAS_ADAPTER *ioc, u16 smid); void mpt2sas_base_build_zero_len_sge(struct MPT2SAS_ADAPTER *ioc, void *paddr); __le32 mpt2sas_base_get_sense_buffer_dma(struct MPT2SAS_ADAPTER *ioc, u16 smid); +void mpt2sas_base_flush_reply_queues(struct MPT2SAS_ADAPTER *ioc); /* hi-priority queue */ u16 mpt2sas_base_get_smid_hpr(struct MPT2SAS_ADAPTER *ioc, u8 cb_idx); @@ -797,6 +1039,8 @@ void mpt2sas_base_release_callback_handler(u8 cb_idx); u8 mpt2sas_base_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply); +u8 mpt2sas_port_enable_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, + u8 msix_index, u32 reply); void *mpt2sas_base_get_reply_virt_addr(struct MPT2SAS_ADAPTER *ioc, u32 phys_addr); u32 mpt2sas_base_get_iocstate(struct MPT2SAS_ADAPTER *ioc, int cooked); @@ -811,13 +1055,22 @@ void mpt2sas_base_validate_event_type(struct MPT2SAS_ADAPTER *ioc, u32 *event_ty void mpt2sas_halt_firmware(struct MPT2SAS_ADAPTER *ioc); +void mpt2sas_base_update_missing_delay(struct MPT2SAS_ADAPTER *ioc, + u16 device_missing_delay, u8 io_missing_delay); + +int mpt2sas_port_enable(struct MPT2SAS_ADAPTER *ioc); + /* scsih shared API */ -u8 mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, +void mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, u32 reply); -void mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint lun, - u8 type, u16 smid_task, ulong timeout); +int mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, + uint channel, uint id, uint lun, u8 type, u16 smid_task, + ulong timeout, enum mutex_type m_type); void mpt2sas_scsih_set_tm_flag(struct MPT2SAS_ADAPTER *ioc, u16 handle); void mpt2sas_scsih_clear_tm_flag(struct MPT2SAS_ADAPTER *ioc, u16 handle); +void mpt2sas_expander_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address); +void mpt2sas_device_remove_by_sas_address(struct MPT2SAS_ADAPTER *ioc, + u64 sas_address); struct _sas_node *mpt2sas_scsih_expander_find_by_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle); struct _sas_node *mpt2sas_scsih_expander_find_by_sas_address(struct MPT2SAS_ADAPTER @@ -825,6 +1078,8 @@ struct _sas_node *mpt2sas_scsih_expander_find_by_sas_address(struct MPT2SAS_ADAP struct _sas_device *mpt2sas_scsih_sas_device_find_by_sas_address( struct MPT2SAS_ADAPTER *ioc, u64 sas_address); +void mpt2sas_port_enable_complete(struct MPT2SAS_ADAPTER *ioc); + void mpt2sas_scsih_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase); /* config shared API */ @@ -851,8 +1106,12 @@ int mpt2sas_config_get_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage1_t *config_page); int mpt2sas_config_set_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage1_t *config_page); +int mpt2sas_config_get_iounit_pg3(struct MPT2SAS_ADAPTER *ioc, + Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage3_t *config_page, u16 sz); int mpt2sas_config_get_sas_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t *mpi_reply, Mpi2SasIOUnitPage1_t *config_page, u16 sz); +int mpt2sas_config_set_sas_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, + Mpi2ConfigReply_t *mpi_reply, Mpi2SasIOUnitPage1_t *config_page, u16 sz); int mpt2sas_config_get_ioc_pg8(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t *mpi_reply, Mpi2IOCPage8_t *config_page); int mpt2sas_config_get_expander_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t @@ -885,7 +1144,7 @@ void mpt2sas_ctl_exit(void); u8 mpt2sas_ctl_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply); void mpt2sas_ctl_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase); -u8 mpt2sas_ctl_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, +void mpt2sas_ctl_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, u32 reply); void mpt2sas_ctl_add_to_event_log(struct MPT2SAS_ADAPTER *ioc, Mpi2EventNotificationReply_t *mpi_reply); @@ -911,6 +1170,7 @@ extern struct scsi_transport_template *mpt2sas_transport_template; extern int scsi_internal_device_block(struct scsi_device *sdev); extern u8 mpt2sas_stm_zero_smid_handler(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, u32 reply); -extern int scsi_internal_device_unblock(struct scsi_device *sdev); +extern int scsi_internal_device_unblock(struct scsi_device *sdev, + enum scsi_device_state new_state); #endif /* MPT2SAS_BASE_H_INCLUDED */ diff --git a/drivers/scsi/mpt2sas/mpt2sas_config.c b/drivers/scsi/mpt2sas/mpt2sas_config.c index 594a389c652..0c47425c73f 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_config.c +++ b/drivers/scsi/mpt2sas/mpt2sas_config.c @@ -2,7 +2,7 @@ * This module provides common API for accessing firmware configuration pages * * This code is based on drivers/scsi/mpt2sas/mpt2_base.c - * Copyright (C) 2007-2009 LSI Corporation + * Copyright (C) 2007-2013 LSI Corporation * (mailto:DL-MPTFusionLinux@lsi.com) * * This program is free software; you can redistribute it and/or @@ -41,7 +41,6 @@ * USA. */ -#include <linux/version.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> @@ -51,6 +50,7 @@ #include <linux/workqueue.h> #include <linux/delay.h> #include <linux/pci.h> +#include <linux/slab.h> #include "mpt2sas_base.h" @@ -92,7 +92,7 @@ struct config_request{ * @mpi_reply: reply message frame * Context: none. * - * Function for displaying debug info helpfull when debugging issues + * Function for displaying debug info helpful when debugging issues * in this module. */ static void @@ -149,7 +149,7 @@ _config_display_some_debug(struct MPT2SAS_ADAPTER *ioc, u16 smid, desc = "raid_config"; break; case MPI2_CONFIG_EXTPAGETYPE_DRIVER_MAPPING: - desc = "driver_mappping"; + desc = "driver_mapping"; break; } break; @@ -158,7 +158,7 @@ _config_display_some_debug(struct MPT2SAS_ADAPTER *ioc, u16 smid, if (!desc) return; - printk(MPT2SAS_DEBUG_FMT "%s: %s(%d), action(%d), form(0x%08x), " + printk(MPT2SAS_INFO_FMT "%s: %s(%d), action(%d), form(0x%08x), " "smid(%d)\n", ioc->name, calling_function_name, desc, mpi_request->Header.PageNumber, mpi_request->Action, le32_to_cpu(mpi_request->PageAddress), smid); @@ -167,7 +167,7 @@ _config_display_some_debug(struct MPT2SAS_ADAPTER *ioc, u16 smid, return; if (mpi_reply->IOCStatus || mpi_reply->IOCLogInfo) - printk(MPT2SAS_DEBUG_FMT + printk(MPT2SAS_INFO_FMT "\tiocstatus(0x%04x), loginfo(0x%08x)\n", ioc->name, le16_to_cpu(mpi_reply->IOCStatus), le32_to_cpu(mpi_reply->IOCLogInfo)); @@ -257,7 +257,7 @@ mpt2sas_config_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, #ifdef CONFIG_SCSI_MPT2SAS_LOGGING _config_display_some_debug(ioc, smid, "config_done", mpi_reply); #endif - ioc->config_cmds.smid = USHORT_MAX; + ioc->config_cmds.smid = USHRT_MAX; complete(&ioc->config_cmds.done); return 1; } @@ -324,7 +324,9 @@ _config_request(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigRequest_t if (r != 0) goto out; if (mpi_request->Action == - MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT) { + MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT || + mpi_request->Action == + MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM) { ioc->base_add_sg_single(&mpi_request->PageBufferSGE, MPT2_CONFIG_COMMON_WRITE_SGLFLAGS | mem.sz, mem.page_dma); @@ -398,7 +400,7 @@ _config_request(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigRequest_t if (ioc->config_cmds.smid == smid) mpt2sas_base_free_smid(ioc, smid); if ((ioc->shost_recovery) || (ioc->config_cmds.status & - MPT2_CMD_RESET)) + MPT2_CMD_RESET) || ioc->pci_error_recovery) goto retry_config; issue_host_reset = 1; r = -EFAULT; @@ -681,6 +683,42 @@ mpt2sas_config_set_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, } /** + * mpt2sas_config_get_iounit_pg3 - obtain iounit page 3 + * @ioc: per adapter object + * @mpi_reply: reply mf payload returned from firmware + * @config_page: contents of the config page + * @sz: size of buffer passed in config_page + * Context: sleep. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt2sas_config_get_iounit_pg3(struct MPT2SAS_ADAPTER *ioc, + Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage3_t *config_page, u16 sz) +{ + Mpi2ConfigRequest_t mpi_request; + int r; + + memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); + mpi_request.Function = MPI2_FUNCTION_CONFIG; + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; + mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IO_UNIT; + mpi_request.Header.PageNumber = 3; + mpi_request.Header.PageVersion = MPI2_IOUNITPAGE3_PAGEVERSION; + mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); + if (r) + goto out; + + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz); + out: + return r; +} + +/** * mpt2sas_config_get_ioc_pg8 - obtain ioc page 8 * @ioc: per adapter object * @mpi_reply: reply mf payload returned from firmware @@ -882,7 +920,7 @@ mpt2sas_config_get_sas_iounit_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t } /** - * mpt2sas_config_get_sas_iounit_pg1 - obtain sas iounit page 0 + * mpt2sas_config_get_sas_iounit_pg1 - obtain sas iounit page 1 * @ioc: per adapter object * @mpi_reply: reply mf payload returned from firmware * @config_page: contents of the config page @@ -907,7 +945,7 @@ mpt2sas_config_get_sas_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT; mpi_request.Header.PageNumber = 1; - mpi_request.Header.PageVersion = MPI2_SASIOUNITPAGE0_PAGEVERSION; + mpi_request.Header.PageVersion = MPI2_SASIOUNITPAGE1_PAGEVERSION; mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); r = _config_request(ioc, &mpi_request, mpi_reply, MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); @@ -922,6 +960,49 @@ mpt2sas_config_get_sas_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t } /** + * mpt2sas_config_set_sas_iounit_pg1 - send sas iounit page 1 + * @ioc: per adapter object + * @mpi_reply: reply mf payload returned from firmware + * @config_page: contents of the config page + * @sz: size of buffer passed in config_page + * Context: sleep. + * + * Calling function should call config_get_number_hba_phys prior to + * this function, so enough memory is allocated for config_page. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt2sas_config_set_sas_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t + *mpi_reply, Mpi2SasIOUnitPage1_t *config_page, u16 sz) +{ + Mpi2ConfigRequest_t mpi_request; + int r; + + memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); + mpi_request.Function = MPI2_FUNCTION_CONFIG; + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; + mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; + mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT; + mpi_request.Header.PageNumber = 1; + mpi_request.Header.PageVersion = MPI2_SASIOUNITPAGE1_PAGEVERSION; + mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); + if (r) + goto out; + + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT; + _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz); + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM; + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz); + out: + return r; +} + +/** * mpt2sas_config_get_expander_pg0 - obtain expander page 0 * @ioc: per adapter object * @mpi_reply: reply mf payload returned from firmware @@ -1311,6 +1392,9 @@ mpt2sas_config_get_volume_handle(struct MPT2SAS_ADAPTER *ioc, u16 pd_handle, Mpi2ConfigReply_t mpi_reply; int r, i, config_page_sz; u16 ioc_status; + int config_num; + u16 element_type; + u16 phys_disk_dev_handle; *volume_handle = 0; memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); @@ -1326,35 +1410,53 @@ mpt2sas_config_get_volume_handle(struct MPT2SAS_ADAPTER *ioc, u16 pd_handle, if (r) goto out; - mpi_request.PageAddress = - cpu_to_le32(MPI2_RAID_PGAD_FORM_ACTIVE_CONFIG); mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; config_page_sz = (le16_to_cpu(mpi_reply.ExtPageLength) * 4); config_page = kmalloc(config_page_sz, GFP_KERNEL); - if (!config_page) + if (!config_page) { + r = -1; goto out; - r = _config_request(ioc, &mpi_request, &mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, - config_page_sz); - if (r) - goto out; - - r = -1; - ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; - if (ioc_status != MPI2_IOCSTATUS_SUCCESS) - goto out; - for (i = 0; i < config_page->NumElements; i++) { - if ((config_page->ConfigElement[i].ElementFlags & - MPI2_RAIDCONFIG0_EFLAGS_MASK_ELEMENT_TYPE) != - MPI2_RAIDCONFIG0_EFLAGS_VOL_PHYS_DISK_ELEMENT) - continue; - if (config_page->ConfigElement[i].PhysDiskDevHandle == - pd_handle) { - *volume_handle = le16_to_cpu(config_page-> - ConfigElement[i].VolDevHandle); - r = 0; + } + config_num = 0xff; + while (1) { + mpi_request.PageAddress = cpu_to_le32(config_num + + MPI2_RAID_PGAD_FORM_GET_NEXT_CONFIGNUM); + r = _config_request(ioc, &mpi_request, &mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, + config_page_sz); + if (r) + goto out; + r = -1; + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) goto out; + for (i = 0; i < config_page->NumElements; i++) { + element_type = le16_to_cpu(config_page-> + ConfigElement[i].ElementFlags) & + MPI2_RAIDCONFIG0_EFLAGS_MASK_ELEMENT_TYPE; + if (element_type == + MPI2_RAIDCONFIG0_EFLAGS_VOL_PHYS_DISK_ELEMENT || + element_type == + MPI2_RAIDCONFIG0_EFLAGS_OCE_ELEMENT) { + phys_disk_dev_handle = + le16_to_cpu(config_page->ConfigElement[i]. + PhysDiskDevHandle); + if (phys_disk_dev_handle == pd_handle) { + *volume_handle = + le16_to_cpu(config_page-> + ConfigElement[i].VolDevHandle); + r = 0; + goto out; + } + } else if (element_type == + MPI2_RAIDCONFIG0_EFLAGS_HOT_SPARE_ELEMENT) { + *volume_handle = 0; + r = 0; + goto out; + } } + config_num = config_page->ConfigNum; } out: kfree(config_page); diff --git a/drivers/scsi/mpt2sas/mpt2sas_ctl.c b/drivers/scsi/mpt2sas/mpt2sas_ctl.c index 84a124f8e21..62df8f9d427 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_ctl.c +++ b/drivers/scsi/mpt2sas/mpt2sas_ctl.c @@ -3,7 +3,7 @@ * controllers * * This code is based on drivers/scsi/mpt2sas/mpt2_ctl.c - * Copyright (C) 2007-2009 LSI Corporation + * Copyright (C) 2007-2013 LSI Corporation * (mailto:DL-MPTFusionLinux@lsi.com) * * This program is free software; you can redistribute it and/or @@ -42,7 +42,6 @@ * USA. */ -#include <linux/version.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/errno.h> @@ -51,7 +50,7 @@ #include <linux/types.h> #include <linux/pci.h> #include <linux/delay.h> -#include <linux/smp_lock.h> +#include <linux/mutex.h> #include <linux/compat.h> #include <linux/poll.h> @@ -61,6 +60,7 @@ #include "mpt2sas_base.h" #include "mpt2sas_ctl.h" +static DEFINE_MUTEX(_ctl_mutex); static struct fasync_struct *async_queue; static DECLARE_WAIT_QUEUE_HEAD(ctl_poll_wait); @@ -82,6 +82,32 @@ enum block_state { #ifdef CONFIG_SCSI_MPT2SAS_LOGGING /** + * _ctl_sas_device_find_by_handle - sas device search + * @ioc: per adapter object + * @handle: sas device handle (assigned by firmware) + * Context: Calling function should acquire ioc->sas_device_lock + * + * This searches for sas_device based on sas_address, then return sas_device + * object. + */ +static struct _sas_device * +_ctl_sas_device_find_by_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle) +{ + struct _sas_device *sas_device, *r; + + r = NULL; + list_for_each_entry(sas_device, &ioc->sas_device_list, list) { + if (sas_device->handle != handle) + continue; + r = sas_device; + goto out; + } + + out: + return r; +} + +/** * _ctl_display_some_debug - debug routine * @ioc: per adapter object * @smid: system request message index @@ -89,7 +115,7 @@ enum block_state { * @mpi_reply: reply message frame * Context: none. * - * Function for displaying debug info helpfull when debugging issues + * Function for displaying debug info helpful when debugging issues * in this module. */ static void @@ -188,14 +214,14 @@ _ctl_display_some_debug(struct MPT2SAS_ADAPTER *ioc, u16 smid, if (!desc) return; - printk(MPT2SAS_DEBUG_FMT "%s: %s, smid(%d)\n", + printk(MPT2SAS_INFO_FMT "%s: %s, smid(%d)\n", ioc->name, calling_function_name, desc, smid); if (!mpi_reply) return; if (mpi_reply->IOCStatus || mpi_reply->IOCLogInfo) - printk(MPT2SAS_DEBUG_FMT + printk(MPT2SAS_INFO_FMT "\tiocstatus(0x%04x), loginfo(0x%08x)\n", ioc->name, le16_to_cpu(mpi_reply->IOCStatus), le32_to_cpu(mpi_reply->IOCLogInfo)); @@ -205,8 +231,24 @@ _ctl_display_some_debug(struct MPT2SAS_ADAPTER *ioc, u16 smid, MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH) { Mpi2SCSIIOReply_t *scsi_reply = (Mpi2SCSIIOReply_t *)mpi_reply; + struct _sas_device *sas_device = NULL; + unsigned long flags; + + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_device = _ctl_sas_device_find_by_handle(ioc, + le16_to_cpu(scsi_reply->DevHandle)); + if (sas_device) { + printk(MPT2SAS_WARN_FMT "\tsas_address(0x%016llx), " + "phy(%d)\n", ioc->name, (unsigned long long) + sas_device->sas_address, sas_device->phy); + printk(MPT2SAS_WARN_FMT + "\tenclosure_logical_id(0x%016llx), slot(%d)\n", + ioc->name, sas_device->enclosure_logical_id, + sas_device->slot); + } + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); if (scsi_reply->SCSIState || scsi_reply->SCSIStatus) - printk(MPT2SAS_DEBUG_FMT + printk(MPT2SAS_INFO_FMT "\tscsi_state(0x%02x), scsi_status" "(0x%02x)\n", ioc->name, scsi_reply->SCSIState, @@ -233,6 +275,9 @@ mpt2sas_ctl_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) { MPI2DefaultReply_t *mpi_reply; + Mpi2SCSIIOReply_t *scsiio_reply; + const void *sense_data; + u32 sz; if (ioc->ctl_cmds.status == MPT2_CMD_NOT_USED) return 1; @@ -243,6 +288,20 @@ mpt2sas_ctl_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, if (mpi_reply) { memcpy(ioc->ctl_cmds.reply, mpi_reply, mpi_reply->MsgLength*4); ioc->ctl_cmds.status |= MPT2_CMD_REPLY_VALID; + /* get sense data */ + if (mpi_reply->Function == MPI2_FUNCTION_SCSI_IO_REQUEST || + mpi_reply->Function == + MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH) { + scsiio_reply = (Mpi2SCSIIOReply_t *)mpi_reply; + if (scsiio_reply->SCSIState & + MPI2_SCSI_STATE_AUTOSENSE_VALID) { + sz = min_t(u32, SCSI_SENSE_BUFFERSIZE, + le32_to_cpu(scsiio_reply->SenseCount)); + sense_data = mpt2sas_base_get_sense_buffer(ioc, + smid); + memcpy(ioc->ctl_cmds.sense, sense_data, sz); + } + } } #ifdef CONFIG_SCSI_MPT2SAS_LOGGING _ctl_display_some_debug(ioc, smid, "ctl_done", mpi_reply); @@ -338,18 +397,22 @@ mpt2sas_ctl_add_to_event_log(struct MPT2SAS_ADAPTER *ioc, * This function merely adds a new work task into ioc->firmware_event_thread. * The tasks are worked from _firmware_event_work in user context. * - * Return 1 meaning mf should be freed from _base_interrupt - * 0 means the mf is freed from this function. + * Returns void. */ -u8 +void mpt2sas_ctl_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, u32 reply) { Mpi2EventNotificationReply_t *mpi_reply; mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); + if (unlikely(!mpi_reply)) { + printk(MPT2SAS_ERR_FMT "mpi_reply not valid at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return; + } mpt2sas_ctl_add_to_event_log(ioc, mpi_reply); - return 1; + return; } /** @@ -392,7 +455,7 @@ mpt2sas_ctl_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase) switch (reset_phase) { case MPT2_IOC_PRE_RESET: - dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " + dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: " "MPT2_IOC_PRE_RESET\n", ioc->name, __func__)); for (i = 0; i < MPI2_DIAG_BUF_TYPE_COUNT; i++) { if (!(ioc->diag_buffer_status[i] & @@ -405,7 +468,7 @@ mpt2sas_ctl_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase) } break; case MPT2_IOC_AFTER_RESET: - dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " + dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: " "MPT2_IOC_AFTER_RESET\n", ioc->name, __func__)); if (ioc->ctl_cmds.status & MPT2_CMD_PENDING) { ioc->ctl_cmds.status |= MPT2_CMD_RESET; @@ -414,7 +477,7 @@ mpt2sas_ctl_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase) } break; case MPT2_IOC_DONE_RESET: - dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " + dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: " "MPT2_IOC_DONE_RESET\n", ioc->name, __func__)); for (i = 0; i < MPI2_DIAG_BUF_TYPE_COUNT; i++) { @@ -446,19 +509,6 @@ _ctl_fasync(int fd, struct file *filep, int mode) } /** - * _ctl_release - - * @inode - - * @filep - - * - * Called when application releases the fasyn callback handler. - */ -static int -_ctl_release(struct inode *inode, struct file *filep) -{ - return fasync_helper(-1, filep, 0, &async_queue); -} - -/** * _ctl_poll - * @file - * @wait - @@ -531,9 +581,9 @@ _ctl_set_task_mid(struct MPT2SAS_ADAPTER *ioc, struct mpt2_ioctl_command *karg, spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); if (!found) { - dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " + dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: " "handle(0x%04x), lun(%d), no active mid!!\n", ioc->name, - desc, tm_request->DevHandle, lun)); + desc, le16_to_cpu(tm_request->DevHandle), lun)); tm_reply = ioc->ctl_cmds.reply; tm_reply->DevHandle = tm_request->DevHandle; tm_reply->Function = MPI2_FUNCTION_SCSI_TASK_MGMT; @@ -549,9 +599,10 @@ _ctl_set_task_mid(struct MPT2SAS_ADAPTER *ioc, struct mpt2_ioctl_command *karg, return 1; } - dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " + dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: " "handle(0x%04x), lun(%d), task_mid(%d)\n", ioc->name, - desc, tm_request->DevHandle, lun, tm_request->TaskMID)); + desc, le16_to_cpu(tm_request->DevHandle), lun, + le16_to_cpu(tm_request->TaskMID))); return 0; } @@ -560,13 +611,12 @@ _ctl_set_task_mid(struct MPT2SAS_ADAPTER *ioc, struct mpt2_ioctl_command *karg, * @ioc: per adapter object * @karg - (struct mpt2_ioctl_command) * @mf - pointer to mf in user space - * @state - NON_BLOCKING or BLOCKING */ static long -_ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, - struct mpt2_ioctl_command karg, void __user *mf, enum block_state state) +_ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, struct mpt2_ioctl_command karg, + void __user *mf) { - MPI2RequestHeader_t *mpi_request; + MPI2RequestHeader_t *mpi_request = NULL, *request; MPI2DefaultReply_t *mpi_reply; u32 ioc_state; u16 ioc_status; @@ -575,7 +625,6 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, u8 issue_reset; u32 sz; void *psge; - void *priv_sense = NULL; void *data_out = NULL; dma_addr_t data_out_dma; size_t data_out_sz = 0; @@ -588,11 +637,6 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, issue_reset = 0; - if (state == NON_BLOCKING && !mutex_trylock(&ioc->ctl_cmds.mutex)) - return -EAGAIN; - else if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex)) - return -ERESTARTSYS; - if (ioc->ctl_cmds.status != MPT2_CMD_NOT_USED) { printk(MPT2SAS_ERR_FMT "%s: ctl_cmd in use\n", ioc->name, __func__); @@ -620,36 +664,62 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, printk(MPT2SAS_INFO_FMT "%s: ioc is operational\n", ioc->name, __func__); - smid = mpt2sas_base_get_smid_scsiio(ioc, ioc->ctl_cb_idx, NULL); - if (!smid) { - printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", - ioc->name, __func__); - ret = -EAGAIN; + mpi_request = kzalloc(ioc->request_sz, GFP_KERNEL); + if (!mpi_request) { + printk(MPT2SAS_ERR_FMT "%s: failed obtaining a memory for " + "mpi_request\n", ioc->name, __func__); + ret = -ENOMEM; goto out; } - ret = 0; - ioc->ctl_cmds.status = MPT2_CMD_PENDING; - memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz); - mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); - ioc->ctl_cmds.smid = smid; - data_out_sz = karg.data_out_size; - data_in_sz = karg.data_in_size; + /* Check for overflow and wraparound */ + if (karg.data_sge_offset * 4 > ioc->request_sz || + karg.data_sge_offset > (UINT_MAX / 4)) { + ret = -EINVAL; + goto out; + } /* copy in request message frame from user */ if (copy_from_user(mpi_request, mf, karg.data_sge_offset*4)) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); ret = -EFAULT; - mpt2sas_base_free_smid(ioc, smid); goto out; } + if (mpi_request->Function == MPI2_FUNCTION_SCSI_TASK_MGMT) { + smid = mpt2sas_base_get_smid_hpr(ioc, ioc->ctl_cb_idx); + if (!smid) { + printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", + ioc->name, __func__); + ret = -EAGAIN; + goto out; + } + } else { + + smid = mpt2sas_base_get_smid_scsiio(ioc, ioc->ctl_cb_idx, NULL); + if (!smid) { + printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", + ioc->name, __func__); + ret = -EAGAIN; + goto out; + } + } + + ret = 0; + ioc->ctl_cmds.status = MPT2_CMD_PENDING; + memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz); + request = mpt2sas_base_get_msg_frame(ioc, smid); + memcpy(request, mpi_request, karg.data_sge_offset*4); + ioc->ctl_cmds.smid = smid; + data_out_sz = karg.data_out_size; + data_in_sz = karg.data_in_size; + if (mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST || mpi_request->Function == MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH) { - if (!mpi_request->FunctionDependent1 || - mpi_request->FunctionDependent1 > - cpu_to_le16(ioc->facts.MaxDevHandle)) { + if (!le16_to_cpu(mpi_request->FunctionDependent1) || + le16_to_cpu(mpi_request->FunctionDependent1) > + ioc->facts.MaxDevHandle) { ret = -EINVAL; mpt2sas_base_free_smid(ioc, smid); goto out; @@ -690,7 +760,7 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, } /* add scatter gather elements */ - psge = (void *)mpi_request + (karg.data_sge_offset*4); + psge = (void *)request + (karg.data_sge_offset*4); if (!data_out_sz && !data_in_sz) { mpt2sas_base_build_zero_len_sge(ioc, psge); @@ -733,24 +803,32 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, _ctl_display_some_debug(ioc, smid, "ctl_request", NULL); #endif + init_completion(&ioc->ctl_cmds.done); switch (mpi_request->Function) { case MPI2_FUNCTION_SCSI_IO_REQUEST: case MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH: { Mpi2SCSIIORequest_t *scsiio_request = - (Mpi2SCSIIORequest_t *)mpi_request; + (Mpi2SCSIIORequest_t *)request; + scsiio_request->SenseBufferLength = SCSI_SENSE_BUFFERSIZE; scsiio_request->SenseBufferLowAddress = mpt2sas_base_get_sense_buffer_dma(ioc, smid); - priv_sense = mpt2sas_base_get_sense_buffer(ioc, smid); - memset(priv_sense, 0, SCSI_SENSE_BUFFERSIZE); - mpt2sas_base_put_smid_scsi_io(ioc, smid, - le16_to_cpu(mpi_request->FunctionDependent1)); + memset(ioc->ctl_cmds.sense, 0, SCSI_SENSE_BUFFERSIZE); + if (mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST) + mpt2sas_base_put_smid_scsi_io(ioc, smid, + le16_to_cpu(mpi_request->FunctionDependent1)); + else + mpt2sas_base_put_smid_default(ioc, smid); break; } case MPI2_FUNCTION_SCSI_TASK_MGMT: { Mpi2SCSITaskManagementRequest_t *tm_request = - (Mpi2SCSITaskManagementRequest_t *)mpi_request; + (Mpi2SCSITaskManagementRequest_t *)request; + + dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "TASK_MGMT: " + "handle(0x%04x), task_type(0x%02x)\n", ioc->name, + le16_to_cpu(tm_request->DevHandle), tm_request->TaskType)); if (tm_request->TaskType == MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK || @@ -762,7 +840,6 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, } } - mutex_lock(&ioc->tm_cmds.mutex); mpt2sas_scsih_set_tm_flag(ioc, le16_to_cpu( tm_request->DevHandle)); mpt2sas_base_put_smid_hi_priority(ioc, smid); @@ -779,8 +856,16 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, if (smp_request->PassthroughFlags & MPI2_SMP_PT_REQ_PT_FLAGS_IMMEDIATE) data = (u8 *)&smp_request->SGL; - else + else { + if (unlikely(data_out == NULL)) { + printk(KERN_ERR "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + mpt2sas_base_free_smid(ioc, smid); + ret = -EINVAL; + goto out; + } data = data_out; + } if (data[1] == 0x91 && (data[10] == 1 || data[10] == 2)) { ioc->ioc_link_reset_in_progress = 1; @@ -812,13 +897,11 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, timeout = MPT2_IOCTL_DEFAULT_TIMEOUT; else timeout = karg.timeout; - init_completion(&ioc->ctl_cmds.done); timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, timeout*HZ); if (mpi_request->Function == MPI2_FUNCTION_SCSI_TASK_MGMT) { Mpi2SCSITaskManagementRequest_t *tm_request = (Mpi2SCSITaskManagementRequest_t *)mpi_request; - mutex_unlock(&ioc->tm_cmds.mutex); mpt2sas_scsih_clear_tm_flag(ioc, le16_to_cpu( tm_request->DevHandle)); } else if ((mpi_request->Function == MPI2_FUNCTION_SMP_PASSTHROUGH || @@ -845,7 +928,7 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, Mpi2SCSITaskManagementReply_t *tm_reply = (Mpi2SCSITaskManagementReply_t *)mpi_reply; - printk(MPT2SAS_DEBUG_FMT "TASK_MGMT: " + printk(MPT2SAS_INFO_FMT "TASK_MGMT: " "IOCStatus(0x%04x), IOCLogInfo(0x%08x), " "TerminationCount(0x%08x)\n", ioc->name, le16_to_cpu(tm_reply->IOCStatus), @@ -881,7 +964,8 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, MPI2_FUNCTION_SCSI_IO_REQUEST || mpi_request->Function == MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH)) { sz = min_t(u32, karg.max_sense_bytes, SCSI_SENSE_BUFFERSIZE); - if (copy_to_user(karg.sense_data_ptr, priv_sense, sz)) { + if (copy_to_user(karg.sense_data_ptr, + ioc->ctl_cmds.sense, sz)) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); ret = -ENODATA; @@ -891,19 +975,20 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, issue_host_reset: if (issue_reset) { + ret = -ENODATA; if ((mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST || mpi_request->Function == - MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH)) { + MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH || + mpi_request->Function == MPI2_FUNCTION_SATA_PASSTHROUGH)) { printk(MPT2SAS_INFO_FMT "issue target reset: handle " "= (0x%04x)\n", ioc->name, - mpi_request->FunctionDependent1); + le16_to_cpu(mpi_request->FunctionDependent1)); mpt2sas_halt_firmware(ioc); - mutex_lock(&ioc->tm_cmds.mutex); mpt2sas_scsih_issue_tm(ioc, - mpi_request->FunctionDependent1, 0, - MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, 10); + le16_to_cpu(mpi_request->FunctionDependent1), 0, 0, + 0, MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, 10, + TM_MUTEX_ON); ioc->tm_cmds.status = MPT2_CMD_NOT_USED; - mutex_unlock(&ioc->tm_cmds.mutex); } else mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, FORCE_BIG_HAMMER); @@ -920,39 +1005,38 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, pci_free_consistent(ioc->pdev, data_out_sz, data_out, data_out_dma); + kfree(mpi_request); ioc->ctl_cmds.status = MPT2_CMD_NOT_USED; - mutex_unlock(&ioc->ctl_cmds.mutex); return ret; } /** * _ctl_getiocinfo - main handler for MPT2IOCINFO opcode + * @ioc: per adapter object * @arg - user space buffer containing ioctl content */ static long -_ctl_getiocinfo(void __user *arg) +_ctl_getiocinfo(struct MPT2SAS_ADAPTER *ioc, void __user *arg) { struct mpt2_ioctl_iocinfo karg; - struct MPT2SAS_ADAPTER *ioc; - u8 revision; if (copy_from_user(&karg, arg, sizeof(karg))) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); return -EFAULT; } - if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc) - return -ENODEV; - dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: enter\n", ioc->name, + dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter\n", ioc->name, __func__)); memset(&karg, 0 , sizeof(karg)); - karg.adapter_type = MPT2_IOCTL_INTERFACE_SAS2; + if (ioc->is_warpdrive) + karg.adapter_type = MPT2_IOCTL_INTERFACE_SAS2_SSS6200; + else + karg.adapter_type = MPT2_IOCTL_INTERFACE_SAS2; if (ioc->pfacts) karg.port_number = ioc->pfacts[0].PortNumber; - pci_read_config_byte(ioc->pdev, PCI_CLASS_REVISION, &revision); - karg.hw_rev = revision; + karg.hw_rev = ioc->pdev->revision; karg.pci_id = ioc->pdev->device; karg.subsystem_device = ioc->pdev->subsystem_device; karg.subsystem_vendor = ioc->pdev->subsystem_vendor; @@ -976,23 +1060,21 @@ _ctl_getiocinfo(void __user *arg) /** * _ctl_eventquery - main handler for MPT2EVENTQUERY opcode + * @ioc: per adapter object * @arg - user space buffer containing ioctl content */ static long -_ctl_eventquery(void __user *arg) +_ctl_eventquery(struct MPT2SAS_ADAPTER *ioc, void __user *arg) { struct mpt2_ioctl_eventquery karg; - struct MPT2SAS_ADAPTER *ioc; if (copy_from_user(&karg, arg, sizeof(karg))) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); return -EFAULT; } - if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc) - return -ENODEV; - dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: enter\n", ioc->name, + dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter\n", ioc->name, __func__)); karg.event_entries = MPT2SAS_CTL_EVENT_LOG_SIZE; @@ -1009,23 +1091,21 @@ _ctl_eventquery(void __user *arg) /** * _ctl_eventenable - main handler for MPT2EVENTENABLE opcode + * @ioc: per adapter object * @arg - user space buffer containing ioctl content */ static long -_ctl_eventenable(void __user *arg) +_ctl_eventenable(struct MPT2SAS_ADAPTER *ioc, void __user *arg) { struct mpt2_ioctl_eventenable karg; - struct MPT2SAS_ADAPTER *ioc; if (copy_from_user(&karg, arg, sizeof(karg))) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); return -EFAULT; } - if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc) - return -ENODEV; - dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: enter\n", ioc->name, + dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter\n", ioc->name, __func__)); if (ioc->event_log) @@ -1049,13 +1129,13 @@ _ctl_eventenable(void __user *arg) /** * _ctl_eventreport - main handler for MPT2EVENTREPORT opcode + * @ioc: per adapter object * @arg - user space buffer containing ioctl content */ static long -_ctl_eventreport(void __user *arg) +_ctl_eventreport(struct MPT2SAS_ADAPTER *ioc, void __user *arg) { struct mpt2_ioctl_eventreport karg; - struct MPT2SAS_ADAPTER *ioc; u32 number_bytes, max_events, max; struct mpt2_ioctl_eventreport __user *uarg = arg; @@ -1064,10 +1144,8 @@ _ctl_eventreport(void __user *arg) __FILE__, __LINE__, __func__); return -EFAULT; } - if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc) - return -ENODEV; - dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: enter\n", ioc->name, + dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter\n", ioc->name, __func__)); number_bytes = karg.hdr.max_data_size - @@ -1095,13 +1173,13 @@ _ctl_eventreport(void __user *arg) /** * _ctl_do_reset - main handler for MPT2HARDRESET opcode + * @ioc: per adapter object * @arg - user space buffer containing ioctl content */ static long -_ctl_do_reset(void __user *arg) +_ctl_do_reset(struct MPT2SAS_ADAPTER *ioc, void __user *arg) { struct mpt2_ioctl_diag_reset karg; - struct MPT2SAS_ADAPTER *ioc; int retval; if (copy_from_user(&karg, arg, sizeof(karg))) { @@ -1109,10 +1187,11 @@ _ctl_do_reset(void __user *arg) __FILE__, __LINE__, __func__); return -EFAULT; } - if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc) - return -ENODEV; - dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: enter\n", ioc->name, + if (ioc->shost_recovery || ioc->pci_error_recovery || + ioc->is_driver_loading) + return -EAGAIN; + dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter\n", ioc->name, __func__)); retval = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, @@ -1196,13 +1275,13 @@ _ctl_btdh_search_raid_device(struct MPT2SAS_ADAPTER *ioc, /** * _ctl_btdh_mapping - main handler for MPT2BTDHMAPPING opcode + * @ioc: per adapter object * @arg - user space buffer containing ioctl content */ static long -_ctl_btdh_mapping(void __user *arg) +_ctl_btdh_mapping(struct MPT2SAS_ADAPTER *ioc, void __user *arg) { struct mpt2_ioctl_btdh_mapping karg; - struct MPT2SAS_ADAPTER *ioc; int rc; if (copy_from_user(&karg, arg, sizeof(karg))) { @@ -1210,10 +1289,8 @@ _ctl_btdh_mapping(void __user *arg) __FILE__, __LINE__, __func__); return -EFAULT; } - if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc) - return -ENODEV; - dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, + dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__)); rc = _ctl_btdh_search_sas_device(ioc, &karg); @@ -1282,7 +1359,7 @@ _ctl_diag_register_2(struct MPT2SAS_ADAPTER *ioc, u16 ioc_status; u8 issue_reset = 0; - dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, + dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__)); if (ioc->ctl_cmds.status != MPT2_CMD_NOT_USED) { @@ -1370,16 +1447,17 @@ _ctl_diag_register_2(struct MPT2SAS_ADAPTER *ioc, mpi_request->VF_ID = 0; /* TODO */ mpi_request->VP_ID = 0; - dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: diag_buffer(0x%p), " + dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: diag_buffer(0x%p), " "dma(0x%llx), sz(%d)\n", ioc->name, __func__, request_data, - (unsigned long long)request_data_dma, mpi_request->BufferLength)); + (unsigned long long)request_data_dma, + le32_to_cpu(mpi_request->BufferLength))); for (i = 0; i < MPT2_PRODUCT_SPECIFIC_DWORDS; i++) mpi_request->ProductSpecific[i] = cpu_to_le32(ioc->product_specific[buffer_type][i]); - mpt2sas_base_put_smid_default(ioc, smid); init_completion(&ioc->ctl_cmds.done); + mpt2sas_base_put_smid_default(ioc, smid); timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, MPT2_IOCTL_DEFAULT_TIMEOUT*HZ); @@ -1407,10 +1485,10 @@ _ctl_diag_register_2(struct MPT2SAS_ADAPTER *ioc, if (ioc_status == MPI2_IOCSTATUS_SUCCESS) { ioc->diag_buffer_status[buffer_type] |= MPT2_DIAG_BUFFER_IS_REGISTERED; - dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: success\n", + dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: success\n", ioc->name, __func__)); } else { - printk(MPT2SAS_DEBUG_FMT "%s: ioc_status(0x%04x) " + printk(MPT2SAS_INFO_FMT "%s: ioc_status(0x%04x) " "log_info(0x%08x)\n", ioc->name, __func__, ioc_status, le32_to_cpu(mpi_reply->IOCLogInfo)); rc = -EFAULT; @@ -1479,17 +1557,16 @@ mpt2sas_enable_diag_buffer(struct MPT2SAS_ADAPTER *ioc, u8 bits_to_register) /** * _ctl_diag_register - application register with driver + * @ioc: per adapter object * @arg - user space buffer containing ioctl content - * @state - NON_BLOCKING or BLOCKING * * This will allow the driver to setup any required buffers that will be * needed by firmware to communicate with the driver. */ static long -_ctl_diag_register(void __user *arg, enum block_state state) +_ctl_diag_register(struct MPT2SAS_ADAPTER *ioc, void __user *arg) { struct mpt2_diag_register karg; - struct MPT2SAS_ADAPTER *ioc; long rc; if (copy_from_user(&karg, arg, sizeof(karg))) { @@ -1497,30 +1574,23 @@ _ctl_diag_register(void __user *arg, enum block_state state) __FILE__, __LINE__, __func__); return -EFAULT; } - if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc) - return -ENODEV; - if (state == NON_BLOCKING && !mutex_trylock(&ioc->ctl_cmds.mutex)) - return -EAGAIN; - else if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex)) - return -ERESTARTSYS; rc = _ctl_diag_register_2(ioc, &karg); - mutex_unlock(&ioc->ctl_cmds.mutex); return rc; } /** * _ctl_diag_unregister - application unregister with driver + * @ioc: per adapter object * @arg - user space buffer containing ioctl content * * This will allow the driver to cleanup any memory allocated for diag * messages and to free up any resources. */ static long -_ctl_diag_unregister(void __user *arg) +_ctl_diag_unregister(struct MPT2SAS_ADAPTER *ioc, void __user *arg) { struct mpt2_diag_unregister karg; - struct MPT2SAS_ADAPTER *ioc; void *request_data; dma_addr_t request_data_dma; u32 request_data_sz; @@ -1531,10 +1601,8 @@ _ctl_diag_unregister(void __user *arg) __FILE__, __LINE__, __func__); return -EFAULT; } - if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc) - return -ENODEV; - dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, + dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__)); buffer_type = karg.unique_id & 0x000000ff; @@ -1581,6 +1649,7 @@ _ctl_diag_unregister(void __user *arg) /** * _ctl_diag_query - query relevant info associated with diag buffers + * @ioc: per adapter object * @arg - user space buffer containing ioctl content * * The application will send only buffer_type and unique_id. Driver will @@ -1588,10 +1657,9 @@ _ctl_diag_unregister(void __user *arg) * 0x00, the driver will return info specified by Buffer Type. */ static long -_ctl_diag_query(void __user *arg) +_ctl_diag_query(struct MPT2SAS_ADAPTER *ioc, void __user *arg) { struct mpt2_diag_query karg; - struct MPT2SAS_ADAPTER *ioc; void *request_data; int i; u8 buffer_type; @@ -1601,10 +1669,8 @@ _ctl_diag_query(void __user *arg) __FILE__, __LINE__, __func__); return -EFAULT; } - if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc) - return -ENODEV; - dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, + dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__)); karg.application_flags = 0; @@ -1682,7 +1748,7 @@ _ctl_send_release(struct MPT2SAS_ADAPTER *ioc, u8 buffer_type, u8 *issue_reset) int rc; unsigned long timeleft; - dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, + dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__)); rc = 0; @@ -1690,7 +1756,7 @@ _ctl_send_release(struct MPT2SAS_ADAPTER *ioc, u8 buffer_type, u8 *issue_reset) ioc_state = mpt2sas_base_get_iocstate(ioc, 1); if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { - dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " + dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: " "skipping due to FAULT state\n", ioc->name, __func__)); rc = -EAGAIN; @@ -1722,8 +1788,8 @@ _ctl_send_release(struct MPT2SAS_ADAPTER *ioc, u8 buffer_type, u8 *issue_reset) mpi_request->VF_ID = 0; /* TODO */ mpi_request->VP_ID = 0; - mpt2sas_base_put_smid_default(ioc, smid); init_completion(&ioc->ctl_cmds.done); + mpt2sas_base_put_smid_default(ioc, smid); timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, MPT2_IOCTL_DEFAULT_TIMEOUT*HZ); @@ -1752,10 +1818,10 @@ _ctl_send_release(struct MPT2SAS_ADAPTER *ioc, u8 buffer_type, u8 *issue_reset) if (ioc_status == MPI2_IOCSTATUS_SUCCESS) { ioc->diag_buffer_status[buffer_type] |= MPT2_DIAG_BUFFER_IS_RELEASED; - dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: success\n", + dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: success\n", ioc->name, __func__)); } else { - printk(MPT2SAS_DEBUG_FMT "%s: ioc_status(0x%04x) " + printk(MPT2SAS_INFO_FMT "%s: ioc_status(0x%04x) " "log_info(0x%08x)\n", ioc->name, __func__, ioc_status, le32_to_cpu(mpi_reply->IOCLogInfo)); rc = -EFAULT; @@ -1769,17 +1835,15 @@ _ctl_send_release(struct MPT2SAS_ADAPTER *ioc, u8 buffer_type, u8 *issue_reset) /** * _ctl_diag_release - request to send Diag Release Message to firmware * @arg - user space buffer containing ioctl content - * @state - NON_BLOCKING or BLOCKING * * This allows ownership of the specified buffer to returned to the driver, * allowing an application to read the buffer without fear that firmware is * overwritting information in the buffer. */ static long -_ctl_diag_release(void __user *arg, enum block_state state) +_ctl_diag_release(struct MPT2SAS_ADAPTER *ioc, void __user *arg) { struct mpt2_diag_release karg; - struct MPT2SAS_ADAPTER *ioc; void *request_data; int rc; u8 buffer_type; @@ -1790,10 +1854,8 @@ _ctl_diag_release(void __user *arg, enum block_state state) __FILE__, __LINE__, __func__); return -EFAULT; } - if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc) - return -ENODEV; - dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, + dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__)); buffer_type = karg.unique_id & 0x000000ff; @@ -1845,38 +1907,31 @@ _ctl_diag_release(void __user *arg, enum block_state state) return 0; } - if (state == NON_BLOCKING && !mutex_trylock(&ioc->ctl_cmds.mutex)) - return -EAGAIN; - else if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex)) - return -ERESTARTSYS; - rc = _ctl_send_release(ioc, buffer_type, &issue_reset); if (issue_reset) mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, FORCE_BIG_HAMMER); - mutex_unlock(&ioc->ctl_cmds.mutex); return rc; } /** * _ctl_diag_read_buffer - request for copy of the diag buffer + * @ioc: per adapter object * @arg - user space buffer containing ioctl content - * @state - NON_BLOCKING or BLOCKING */ static long -_ctl_diag_read_buffer(void __user *arg, enum block_state state) +_ctl_diag_read_buffer(struct MPT2SAS_ADAPTER *ioc, void __user *arg) { struct mpt2_diag_read_buffer karg; struct mpt2_diag_read_buffer __user *uarg = arg; - struct MPT2SAS_ADAPTER *ioc; void *request_data, *diag_data; Mpi2DiagBufferPostRequest_t *mpi_request; Mpi2DiagBufferPostReply_t *mpi_reply; int rc, i; u8 buffer_type; - unsigned long timeleft; + unsigned long timeleft, request_size, copy_size; u16 smid; u16 ioc_status; u8 issue_reset = 0; @@ -1886,10 +1941,8 @@ _ctl_diag_read_buffer(void __user *arg, enum block_state state) __FILE__, __LINE__, __func__); return -EFAULT; } - if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc) - return -ENODEV; - dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, + dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__)); buffer_type = karg.unique_id & 0x000000ff; @@ -1912,6 +1965,8 @@ _ctl_diag_read_buffer(void __user *arg, enum block_state state) return -ENOMEM; } + request_size = ioc->diag_buffer_sz[buffer_type]; + if ((karg.starting_offset % 4) || (karg.bytes_to_read % 4)) { printk(MPT2SAS_ERR_FMT "%s: either the starting_offset " "or bytes_to_read are not 4 byte aligned\n", ioc->name, @@ -1919,13 +1974,23 @@ _ctl_diag_read_buffer(void __user *arg, enum block_state state) return -EINVAL; } + if (karg.starting_offset > request_size) + return -EINVAL; + diag_data = (void *)(request_data + karg.starting_offset); - dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: diag_buffer(%p), " + dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: diag_buffer(%p), " "offset(%d), sz(%d)\n", ioc->name, __func__, diag_data, karg.starting_offset, karg.bytes_to_read)); + /* Truncate data on requests that are too large */ + if ((diag_data + karg.bytes_to_read < diag_data) || + (diag_data + karg.bytes_to_read > request_data + request_size)) + copy_size = request_size - karg.starting_offset; + else + copy_size = karg.bytes_to_read; + if (copy_to_user((void __user *)uarg->diagnostic_data, - diag_data, karg.bytes_to_read)) { + diag_data, copy_size)) { printk(MPT2SAS_ERR_FMT "%s: Unable to write " "mpt_diag_read_buffer_t data @ %p\n", ioc->name, __func__, diag_data); @@ -1935,21 +2000,17 @@ _ctl_diag_read_buffer(void __user *arg, enum block_state state) if ((karg.flags & MPT2_FLAGS_REREGISTER) == 0) return 0; - dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: Reregister " + dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: Reregister " "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type)); if ((ioc->diag_buffer_status[buffer_type] & MPT2_DIAG_BUFFER_IS_RELEASED) == 0) { - dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " + dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: " "buffer_type(0x%02x) is still registered\n", ioc->name, __func__, buffer_type)); return 0; } /* Get a free request frame and save the message context. */ - if (state == NON_BLOCKING && !mutex_trylock(&ioc->ctl_cmds.mutex)) - return -EAGAIN; - else if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex)) - return -ERESTARTSYS; if (ioc->ctl_cmds.status != MPT2_CMD_NOT_USED) { printk(MPT2SAS_ERR_FMT "%s: ctl_cmd in use\n", @@ -1984,8 +2045,8 @@ _ctl_diag_read_buffer(void __user *arg, enum block_state state) mpi_request->VF_ID = 0; /* TODO */ mpi_request->VP_ID = 0; - mpt2sas_base_put_smid_default(ioc, smid); init_completion(&ioc->ctl_cmds.done); + mpt2sas_base_put_smid_default(ioc, smid); timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, MPT2_IOCTL_DEFAULT_TIMEOUT*HZ); @@ -2013,10 +2074,10 @@ _ctl_diag_read_buffer(void __user *arg, enum block_state state) if (ioc_status == MPI2_IOCSTATUS_SUCCESS) { ioc->diag_buffer_status[buffer_type] |= MPT2_DIAG_BUFFER_IS_REGISTERED; - dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: success\n", + dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: success\n", ioc->name, __func__)); } else { - printk(MPT2SAS_DEBUG_FMT "%s: ioc_status(0x%04x) " + printk(MPT2SAS_INFO_FMT "%s: ioc_status(0x%04x) " "log_info(0x%08x)\n", ioc->name, __func__, ioc_status, le32_to_cpu(mpi_reply->IOCLogInfo)); rc = -EFAULT; @@ -2030,114 +2091,172 @@ _ctl_diag_read_buffer(void __user *arg, enum block_state state) out: ioc->ctl_cmds.status = MPT2_CMD_NOT_USED; - mutex_unlock(&ioc->ctl_cmds.mutex); return rc; } + +#ifdef CONFIG_COMPAT +/** + * _ctl_compat_mpt_command - convert 32bit pointers to 64bit. + * @ioc: per adapter object + * @cmd - ioctl opcode + * @arg - (struct mpt2_ioctl_command32) + * + * MPT2COMMAND32 - Handle 32bit applications running on 64bit os. + */ +static long +_ctl_compat_mpt_command(struct MPT2SAS_ADAPTER *ioc, unsigned cmd, + void __user *arg) +{ + struct mpt2_ioctl_command32 karg32; + struct mpt2_ioctl_command32 __user *uarg; + struct mpt2_ioctl_command karg; + + if (_IOC_SIZE(cmd) != sizeof(struct mpt2_ioctl_command32)) + return -EINVAL; + + uarg = (struct mpt2_ioctl_command32 __user *) arg; + + if (copy_from_user(&karg32, (char __user *)arg, sizeof(karg32))) { + printk(KERN_ERR "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + return -EFAULT; + } + + memset(&karg, 0, sizeof(struct mpt2_ioctl_command)); + karg.hdr.ioc_number = karg32.hdr.ioc_number; + karg.hdr.port_number = karg32.hdr.port_number; + karg.hdr.max_data_size = karg32.hdr.max_data_size; + karg.timeout = karg32.timeout; + karg.max_reply_bytes = karg32.max_reply_bytes; + karg.data_in_size = karg32.data_in_size; + karg.data_out_size = karg32.data_out_size; + karg.max_sense_bytes = karg32.max_sense_bytes; + karg.data_sge_offset = karg32.data_sge_offset; + karg.reply_frame_buf_ptr = compat_ptr(karg32.reply_frame_buf_ptr); + karg.data_in_buf_ptr = compat_ptr(karg32.data_in_buf_ptr); + karg.data_out_buf_ptr = compat_ptr(karg32.data_out_buf_ptr); + karg.sense_data_ptr = compat_ptr(karg32.sense_data_ptr); + return _ctl_do_mpt_command(ioc, karg, &uarg->mf); +} +#endif + /** * _ctl_ioctl_main - main ioctl entry point * @file - (struct file) * @cmd - ioctl opcode * @arg - + * compat - handles 32 bit applications in 64bit os */ static long -_ctl_ioctl_main(struct file *file, unsigned int cmd, void __user *arg) +_ctl_ioctl_main(struct file *file, unsigned int cmd, void __user *arg, + u8 compat) { + struct MPT2SAS_ADAPTER *ioc; + struct mpt2_ioctl_header ioctl_header; enum block_state state; long ret = -EINVAL; - state = (file->f_flags & O_NONBLOCK) ? NON_BLOCKING : - BLOCKING; + /* get IOCTL header */ + if (copy_from_user(&ioctl_header, (char __user *)arg, + sizeof(struct mpt2_ioctl_header))) { + printk(KERN_ERR "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + return -EFAULT; + } + + if (_ctl_verify_adapter(ioctl_header.ioc_number, &ioc) == -1 || !ioc) + return -ENODEV; + if (ioc->shost_recovery || ioc->pci_error_recovery || + ioc->is_driver_loading) + return -EAGAIN; + + state = (file->f_flags & O_NONBLOCK) ? NON_BLOCKING : BLOCKING; + if (state == NON_BLOCKING) { + if (!mutex_trylock(&ioc->ctl_cmds.mutex)) + return -EAGAIN; + } else if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex)) { + return -ERESTARTSYS; + } switch (cmd) { case MPT2IOCINFO: if (_IOC_SIZE(cmd) == sizeof(struct mpt2_ioctl_iocinfo)) - ret = _ctl_getiocinfo(arg); + ret = _ctl_getiocinfo(ioc, arg); break; +#ifdef CONFIG_COMPAT + case MPT2COMMAND32: +#endif case MPT2COMMAND: { - struct mpt2_ioctl_command karg; struct mpt2_ioctl_command __user *uarg; - struct MPT2SAS_ADAPTER *ioc; - + struct mpt2_ioctl_command karg; +#ifdef CONFIG_COMPAT + if (compat) { + ret = _ctl_compat_mpt_command(ioc, cmd, arg); + break; + } +#endif if (copy_from_user(&karg, arg, sizeof(karg))) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); - return -EFAULT; + ret = -EFAULT; + break; } - if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || - !ioc) - return -ENODEV; - - if (ioc->shost_recovery) - return -EAGAIN; - if (_IOC_SIZE(cmd) == sizeof(struct mpt2_ioctl_command)) { uarg = arg; - ret = _ctl_do_mpt_command(ioc, karg, &uarg->mf, state); + ret = _ctl_do_mpt_command(ioc, karg, &uarg->mf); } break; } case MPT2EVENTQUERY: if (_IOC_SIZE(cmd) == sizeof(struct mpt2_ioctl_eventquery)) - ret = _ctl_eventquery(arg); + ret = _ctl_eventquery(ioc, arg); break; case MPT2EVENTENABLE: if (_IOC_SIZE(cmd) == sizeof(struct mpt2_ioctl_eventenable)) - ret = _ctl_eventenable(arg); + ret = _ctl_eventenable(ioc, arg); break; case MPT2EVENTREPORT: - ret = _ctl_eventreport(arg); + ret = _ctl_eventreport(ioc, arg); break; case MPT2HARDRESET: if (_IOC_SIZE(cmd) == sizeof(struct mpt2_ioctl_diag_reset)) - ret = _ctl_do_reset(arg); + ret = _ctl_do_reset(ioc, arg); break; case MPT2BTDHMAPPING: if (_IOC_SIZE(cmd) == sizeof(struct mpt2_ioctl_btdh_mapping)) - ret = _ctl_btdh_mapping(arg); + ret = _ctl_btdh_mapping(ioc, arg); break; case MPT2DIAGREGISTER: if (_IOC_SIZE(cmd) == sizeof(struct mpt2_diag_register)) - ret = _ctl_diag_register(arg, state); + ret = _ctl_diag_register(ioc, arg); break; case MPT2DIAGUNREGISTER: if (_IOC_SIZE(cmd) == sizeof(struct mpt2_diag_unregister)) - ret = _ctl_diag_unregister(arg); + ret = _ctl_diag_unregister(ioc, arg); break; case MPT2DIAGQUERY: if (_IOC_SIZE(cmd) == sizeof(struct mpt2_diag_query)) - ret = _ctl_diag_query(arg); + ret = _ctl_diag_query(ioc, arg); break; case MPT2DIAGRELEASE: if (_IOC_SIZE(cmd) == sizeof(struct mpt2_diag_release)) - ret = _ctl_diag_release(arg, state); + ret = _ctl_diag_release(ioc, arg); break; case MPT2DIAGREADBUFFER: if (_IOC_SIZE(cmd) == sizeof(struct mpt2_diag_read_buffer)) - ret = _ctl_diag_read_buffer(arg, state); + ret = _ctl_diag_read_buffer(ioc, arg); break; default: - { - struct mpt2_ioctl_command karg; - struct MPT2SAS_ADAPTER *ioc; - if (copy_from_user(&karg, arg, sizeof(karg))) { - printk(KERN_ERR "failure at %s:%d/%s()!\n", - __FILE__, __LINE__, __func__); - return -EFAULT; - } - - if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || - !ioc) - return -ENODEV; - - dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT + dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "unsupported ioctl opcode(0x%08x)\n", ioc->name, cmd)); break; } - } + + mutex_unlock(&ioc->ctl_cmds.mutex); return ret; } @@ -2152,69 +2271,11 @@ _ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { long ret; - lock_kernel(); - ret = _ctl_ioctl_main(file, cmd, (void __user *)arg); - unlock_kernel(); + ret = _ctl_ioctl_main(file, cmd, (void __user *)arg, 0); return ret; } - #ifdef CONFIG_COMPAT /** - * _ctl_compat_mpt_command - convert 32bit pointers to 64bit. - * @file - (struct file) - * @cmd - ioctl opcode - * @arg - (struct mpt2_ioctl_command32) - * - * MPT2COMMAND32 - Handle 32bit applications running on 64bit os. - */ -static long -_ctl_compat_mpt_command(struct file *file, unsigned cmd, unsigned long arg) -{ - struct mpt2_ioctl_command32 karg32; - struct mpt2_ioctl_command32 __user *uarg; - struct mpt2_ioctl_command karg; - struct MPT2SAS_ADAPTER *ioc; - enum block_state state; - - if (_IOC_SIZE(cmd) != sizeof(struct mpt2_ioctl_command32)) - return -EINVAL; - - uarg = (struct mpt2_ioctl_command32 __user *) arg; - - if (copy_from_user(&karg32, (char __user *)arg, sizeof(karg32))) { - printk(KERN_ERR "failure at %s:%d/%s()!\n", - __FILE__, __LINE__, __func__); - return -EFAULT; - } - if (_ctl_verify_adapter(karg32.hdr.ioc_number, &ioc) == -1 || !ioc) - return -ENODEV; - - if (ioc->shost_recovery) - return -EAGAIN; - - memset(&karg, 0, sizeof(struct mpt2_ioctl_command)); - karg.hdr.ioc_number = karg32.hdr.ioc_number; - karg.hdr.port_number = karg32.hdr.port_number; - karg.hdr.max_data_size = karg32.hdr.max_data_size; - karg.timeout = karg32.timeout; - karg.max_reply_bytes = karg32.max_reply_bytes; - karg.data_in_size = karg32.data_in_size; - karg.data_out_size = karg32.data_out_size; - karg.max_sense_bytes = karg32.max_sense_bytes; - karg.data_sge_offset = karg32.data_sge_offset; - memcpy(&karg.reply_frame_buf_ptr, &karg32.reply_frame_buf_ptr, - sizeof(uint32_t)); - memcpy(&karg.data_in_buf_ptr, &karg32.data_in_buf_ptr, - sizeof(uint32_t)); - memcpy(&karg.data_out_buf_ptr, &karg32.data_out_buf_ptr, - sizeof(uint32_t)); - memcpy(&karg.sense_data_ptr, &karg32.sense_data_ptr, - sizeof(uint32_t)); - state = (file->f_flags & O_NONBLOCK) ? NON_BLOCKING : BLOCKING; - return _ctl_do_mpt_command(ioc, karg, &uarg->mf, state); -} - -/** * _ctl_ioctl_compat - main ioctl entry point (compat) * @file - * @cmd - @@ -2227,12 +2288,7 @@ _ctl_ioctl_compat(struct file *file, unsigned cmd, unsigned long arg) { long ret; - lock_kernel(); - if (cmd == MPT2COMMAND32) - ret = _ctl_compat_mpt_command(file, cmd, arg); - else - ret = _ctl_ioctl_main(file, cmd, (void __user *)arg); - unlock_kernel(); + ret = _ctl_ioctl_main(file, cmd, (void __user *)arg, 1); return ret; } #endif @@ -2337,8 +2393,8 @@ _ctl_version_nvdata_persistent_show(struct device *cdev, struct Scsi_Host *shost = class_to_shost(cdev); struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - return snprintf(buf, PAGE_SIZE, "%02xh\n", - le16_to_cpu(ioc->iounit_pg0.NvdataVersionPersistent.Word)); + return snprintf(buf, PAGE_SIZE, "%08xh\n", + le32_to_cpu(ioc->iounit_pg0.NvdataVersionPersistent.Word)); } static DEVICE_ATTR(version_nvdata_persistent, S_IRUGO, _ctl_version_nvdata_persistent_show, NULL); @@ -2357,8 +2413,8 @@ _ctl_version_nvdata_default_show(struct device *cdev, struct Scsi_Host *shost = class_to_shost(cdev); struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - return snprintf(buf, PAGE_SIZE, "%02xh\n", - le16_to_cpu(ioc->iounit_pg0.NvdataVersionDefault.Word)); + return snprintf(buf, PAGE_SIZE, "%08xh\n", + le32_to_cpu(ioc->iounit_pg0.NvdataVersionDefault.Word)); } static DEVICE_ATTR(version_nvdata_default, S_IRUGO, _ctl_version_nvdata_default_show, NULL); @@ -2578,6 +2634,314 @@ _ctl_fwfault_debug_store(struct device *cdev, static DEVICE_ATTR(fwfault_debug, S_IRUGO | S_IWUSR, _ctl_fwfault_debug_show, _ctl_fwfault_debug_store); + +/** + * _ctl_ioc_reset_count_show - ioc reset count + * @cdev - pointer to embedded class device + * @buf - the buffer returned + * + * This is firmware queue depth limit + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t +_ctl_ioc_reset_count_show(struct device *cdev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + + return snprintf(buf, PAGE_SIZE, "%08d\n", ioc->ioc_reset_count); +} +static DEVICE_ATTR(ioc_reset_count, S_IRUGO, + _ctl_ioc_reset_count_show, NULL); + +/** + * _ctl_ioc_reply_queue_count_show - number of reply queues + * @cdev - pointer to embedded class device + * @buf - the buffer returned + * + * This is number of reply queues + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t +_ctl_ioc_reply_queue_count_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + u8 reply_queue_count; + struct Scsi_Host *shost = class_to_shost(cdev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + + if ((ioc->facts.IOCCapabilities & + MPI2_IOCFACTS_CAPABILITY_MSI_X_INDEX) && ioc->msix_enable) + reply_queue_count = ioc->reply_queue_count; + else + reply_queue_count = 1; + return snprintf(buf, PAGE_SIZE, "%d\n", reply_queue_count); +} +static DEVICE_ATTR(reply_queue_count, S_IRUGO, + _ctl_ioc_reply_queue_count_show, NULL); + +/** + * _ctl_BRM_status_show - Backup Rail Monitor Status + * @cdev - pointer to embedded class device + * @buf - the buffer returned + * + * This is number of reply queues + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t +_ctl_BRM_status_show(struct device *cdev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + Mpi2IOUnitPage3_t *io_unit_pg3 = NULL; + Mpi2ConfigReply_t mpi_reply; + u16 backup_rail_monitor_status = 0; + u16 ioc_status; + int sz; + ssize_t rc = 0; + + if (!ioc->is_warpdrive) { + printk(MPT2SAS_ERR_FMT "%s: BRM attribute is only for"\ + "warpdrive\n", ioc->name, __func__); + goto out; + } + + /* allocate upto GPIOVal 36 entries */ + sz = offsetof(Mpi2IOUnitPage3_t, GPIOVal) + (sizeof(u16) * 36); + io_unit_pg3 = kzalloc(sz, GFP_KERNEL); + if (!io_unit_pg3) { + printk(MPT2SAS_ERR_FMT "%s: failed allocating memory"\ + "for iounit_pg3: (%d) bytes\n", ioc->name, __func__, sz); + goto out; + } + + if (mpt2sas_config_get_iounit_pg3(ioc, &mpi_reply, io_unit_pg3, sz) != + 0) { + printk(MPT2SAS_ERR_FMT + "%s: failed reading iounit_pg3\n", ioc->name, + __func__); + goto out; + } + + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + printk(MPT2SAS_ERR_FMT "%s: iounit_pg3 failed with"\ + "ioc_status(0x%04x)\n", ioc->name, __func__, ioc_status); + goto out; + } + + if (io_unit_pg3->GPIOCount < 25) { + printk(MPT2SAS_ERR_FMT "%s: iounit_pg3->GPIOCount less than"\ + "25 entries, detected (%d) entries\n", ioc->name, __func__, + io_unit_pg3->GPIOCount); + goto out; + } + + /* BRM status is in bit zero of GPIOVal[24] */ + backup_rail_monitor_status = le16_to_cpu(io_unit_pg3->GPIOVal[24]); + rc = snprintf(buf, PAGE_SIZE, "%d\n", (backup_rail_monitor_status & 1)); + + out: + kfree(io_unit_pg3); + return rc; +} +static DEVICE_ATTR(BRM_status, S_IRUGO, _ctl_BRM_status_show, NULL); + +struct DIAG_BUFFER_START { + __le32 Size; + __le32 DiagVersion; + u8 BufferType; + u8 Reserved[3]; + __le32 Reserved1; + __le32 Reserved2; + __le32 Reserved3; +}; +/** + * _ctl_host_trace_buffer_size_show - host buffer size (trace only) + * @cdev - pointer to embedded class device + * @buf - the buffer returned + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t +_ctl_host_trace_buffer_size_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + u32 size = 0; + struct DIAG_BUFFER_START *request_data; + + if (!ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]) { + printk(MPT2SAS_ERR_FMT "%s: host_trace_buffer is not " + "registered\n", ioc->name, __func__); + return 0; + } + + if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & + MPT2_DIAG_BUFFER_IS_REGISTERED) == 0) { + printk(MPT2SAS_ERR_FMT "%s: host_trace_buffer is not " + "registered\n", ioc->name, __func__); + return 0; + } + + request_data = (struct DIAG_BUFFER_START *) + ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]; + if ((le32_to_cpu(request_data->DiagVersion) == 0x00000000 || + le32_to_cpu(request_data->DiagVersion) == 0x01000000) && + le32_to_cpu(request_data->Reserved3) == 0x4742444c) + size = le32_to_cpu(request_data->Size); + + ioc->ring_buffer_sz = size; + return snprintf(buf, PAGE_SIZE, "%d\n", size); +} +static DEVICE_ATTR(host_trace_buffer_size, S_IRUGO, + _ctl_host_trace_buffer_size_show, NULL); + +/** + * _ctl_host_trace_buffer_show - firmware ring buffer (trace only) + * @cdev - pointer to embedded class device + * @buf - the buffer returned + * + * A sysfs 'read/write' shost attribute. + * + * You will only be able to read 4k bytes of ring buffer at a time. + * In order to read beyond 4k bytes, you will have to write out the + * offset to the same attribute, it will move the pointer. + */ +static ssize_t +_ctl_host_trace_buffer_show(struct device *cdev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + void *request_data; + u32 size; + + if (!ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]) { + printk(MPT2SAS_ERR_FMT "%s: host_trace_buffer is not " + "registered\n", ioc->name, __func__); + return 0; + } + + if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & + MPT2_DIAG_BUFFER_IS_REGISTERED) == 0) { + printk(MPT2SAS_ERR_FMT "%s: host_trace_buffer is not " + "registered\n", ioc->name, __func__); + return 0; + } + + if (ioc->ring_buffer_offset > ioc->ring_buffer_sz) + return 0; + + size = ioc->ring_buffer_sz - ioc->ring_buffer_offset; + size = (size > PAGE_SIZE) ? PAGE_SIZE : size; + request_data = ioc->diag_buffer[0] + ioc->ring_buffer_offset; + memcpy(buf, request_data, size); + return size; +} + +static ssize_t +_ctl_host_trace_buffer_store(struct device *cdev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + int val = 0; + + if (sscanf(buf, "%d", &val) != 1) + return -EINVAL; + + ioc->ring_buffer_offset = val; + return strlen(buf); +} +static DEVICE_ATTR(host_trace_buffer, S_IRUGO | S_IWUSR, + _ctl_host_trace_buffer_show, _ctl_host_trace_buffer_store); + +/*****************************************/ + +/** + * _ctl_host_trace_buffer_enable_show - firmware ring buffer (trace only) + * @cdev - pointer to embedded class device + * @buf - the buffer returned + * + * A sysfs 'read/write' shost attribute. + * + * This is a mechnism to post/release host_trace_buffers + */ +static ssize_t +_ctl_host_trace_buffer_enable_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + + if ((!ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]) || + ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & + MPT2_DIAG_BUFFER_IS_REGISTERED) == 0)) + return snprintf(buf, PAGE_SIZE, "off\n"); + else if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & + MPT2_DIAG_BUFFER_IS_RELEASED)) + return snprintf(buf, PAGE_SIZE, "release\n"); + else + return snprintf(buf, PAGE_SIZE, "post\n"); +} + +static ssize_t +_ctl_host_trace_buffer_enable_store(struct device *cdev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + char str[10] = ""; + struct mpt2_diag_register diag_register; + u8 issue_reset = 0; + + if (sscanf(buf, "%9s", str) != 1) + return -EINVAL; + + if (!strcmp(str, "post")) { + /* exit out if host buffers are already posted */ + if ((ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]) && + (ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & + MPT2_DIAG_BUFFER_IS_REGISTERED) && + ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & + MPT2_DIAG_BUFFER_IS_RELEASED) == 0)) + goto out; + memset(&diag_register, 0, sizeof(struct mpt2_diag_register)); + printk(MPT2SAS_INFO_FMT "posting host trace buffers\n", + ioc->name); + diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_TRACE; + diag_register.requested_buffer_size = (1024 * 1024); + diag_register.unique_id = 0x7075900; + ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] = 0; + _ctl_diag_register_2(ioc, &diag_register); + } else if (!strcmp(str, "release")) { + /* exit out if host buffers are already released */ + if (!ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]) + goto out; + if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & + MPT2_DIAG_BUFFER_IS_REGISTERED) == 0) + goto out; + if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & + MPT2_DIAG_BUFFER_IS_RELEASED)) + goto out; + printk(MPT2SAS_INFO_FMT "releasing host trace buffer\n", + ioc->name); + _ctl_send_release(ioc, MPI2_DIAG_BUF_TYPE_TRACE, &issue_reset); + } + + out: + return strlen(buf); +} +static DEVICE_ATTR(host_trace_buffer_enable, S_IRUGO | S_IWUSR, + _ctl_host_trace_buffer_enable_show, _ctl_host_trace_buffer_enable_store); + struct device_attribute *mpt2sas_host_attrs[] = { &dev_attr_version_fw, &dev_attr_version_bios, @@ -2594,6 +2958,12 @@ struct device_attribute *mpt2sas_host_attrs[] = { &dev_attr_fwfault_debug, &dev_attr_fw_queue_depth, &dev_attr_host_sas_address, + &dev_attr_ioc_reset_count, + &dev_attr_host_trace_buffer_size, + &dev_attr_host_trace_buffer, + &dev_attr_host_trace_buffer_enable, + &dev_attr_reply_queue_count, + &dev_attr_BRM_status, NULL, }; @@ -2648,12 +3018,12 @@ struct device_attribute *mpt2sas_dev_attrs[] = { static const struct file_operations ctl_fops = { .owner = THIS_MODULE, .unlocked_ioctl = _ctl_ioctl, - .release = _ctl_release, .poll = _ctl_poll, .fasync = _ctl_fasync, #ifdef CONFIG_COMPAT .compat_ioctl = _ctl_ioctl_compat, #endif + .llseek = noop_llseek, }; static struct miscdevice ctl_dev = { diff --git a/drivers/scsi/mpt2sas/mpt2sas_ctl.h b/drivers/scsi/mpt2sas/mpt2sas_ctl.h index 8a5eeb1a5c8..8b2ac1869dc 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_ctl.h +++ b/drivers/scsi/mpt2sas/mpt2sas_ctl.h @@ -3,7 +3,7 @@ * controllers * * This code is based on drivers/scsi/mpt2sas/mpt2_ctl.h - * Copyright (C) 2007-2009 LSI Corporation + * Copyright (C) 2007-2013 LSI Corporation * (mailto:DL-MPTFusionLinux@lsi.com) * * This program is free software; you can redistribute it and/or @@ -133,6 +133,7 @@ struct mpt2_ioctl_pci_info { #define MPT2_IOCTL_INTERFACE_FC_IP (0x02) #define MPT2_IOCTL_INTERFACE_SAS (0x03) #define MPT2_IOCTL_INTERFACE_SAS2 (0x04) +#define MPT2_IOCTL_INTERFACE_SAS2_SSS6200 (0x05) #define MPT2_IOCTL_VERSION_LENGTH (32) /** diff --git a/drivers/scsi/mpt2sas/mpt2sas_debug.h b/drivers/scsi/mpt2sas/mpt2sas_debug.h index 5308a25cb30..a9021cbd662 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_debug.h +++ b/drivers/scsi/mpt2sas/mpt2sas_debug.h @@ -2,7 +2,7 @@ * Logging Support for MPT (Message Passing Technology) based controllers * * This code is based on drivers/scsi/mpt2sas/mpt2_debug.c - * Copyright (C) 2007-2009 LSI Corporation + * Copyright (C) 2007-2013 LSI Corporation * (mailto:DL-MPTFusionLinux@lsi.com) * * This program is free software; you can redistribute it and/or @@ -164,7 +164,7 @@ static inline void _debug_dump_mf(void *mpi_request, int sz) { int i; - u32 *mfp = (u32 *)mpi_request; + __le32 *mfp = (__le32 *)mpi_request; printk(KERN_INFO "mf:\n\t"); for (i = 0; i < sz; i++) { diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c index efabea1a3ce..5055f925d2c 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c +++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c @@ -2,7 +2,7 @@ * Scsi Host Layer for MPT (Message Passing Technology) based controllers * * This code is based on drivers/scsi/mpt2sas/mpt2_scsih.c - * Copyright (C) 2007-2009 LSI Corporation + * Copyright (C) 2007-2013 LSI Corporation * (mailto:DL-MPTFusionLinux@lsi.com) * * This program is free software; you can redistribute it and/or @@ -41,7 +41,6 @@ * USA. */ -#include <linux/version.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> @@ -52,6 +51,9 @@ #include <linux/delay.h> #include <linux/pci.h> #include <linux/interrupt.h> +#include <linux/aer.h> +#include <linux/raid_class.h> +#include <linux/slab.h> #include "mpt2sas_base.h" @@ -67,6 +69,11 @@ static void _scsih_expander_node_remove(struct MPT2SAS_ADAPTER *ioc, struct _sas_node *sas_expander); static void _firmware_event_work(struct work_struct *work); +static u8 _scsih_check_for_pending_tm(struct MPT2SAS_ADAPTER *ioc, u16 smid); + +static void _scsih_scan_start(struct Scsi_Host *shost); +static int _scsih_scan_finished(struct Scsi_Host *shost, unsigned long time); + /* global parameters */ LIST_HEAD(mpt2sas_ioc_list); @@ -75,12 +82,14 @@ static u8 scsi_io_cb_idx = -1; static u8 tm_cb_idx = -1; static u8 ctl_cb_idx = -1; static u8 base_cb_idx = -1; +static u8 port_enable_cb_idx = -1; static u8 transport_cb_idx = -1; static u8 scsih_cb_idx = -1; static u8 config_cb_idx = -1; static int mpt_ids; static u8 tm_tr_cb_idx = -1 ; +static u8 tm_tr_volume_cb_idx = -1 ; static u8 tm_sas_control_cb_idx = -1; /* command line options */ @@ -88,12 +97,41 @@ static u32 logging_level; MODULE_PARM_DESC(logging_level, " bits for enabling additional logging info " "(default=0)"); +static ushort max_sectors = 0xFFFF; +module_param(max_sectors, ushort, 0); +MODULE_PARM_DESC(max_sectors, "max sectors, range 64 to 32767 default=32767"); + +static int missing_delay[2] = {-1, -1}; +module_param_array(missing_delay, int, NULL, 0); +MODULE_PARM_DESC(missing_delay, " device missing delay , io missing delay"); + /* scsi-mid layer global parmeter is max_report_luns, which is 511 */ #define MPT2SAS_MAX_LUN (16895) static int max_lun = MPT2SAS_MAX_LUN; module_param(max_lun, int, 0); MODULE_PARM_DESC(max_lun, " max lun, default=16895 "); +/* diag_buffer_enable is bitwise + * bit 0 set = TRACE + * bit 1 set = SNAPSHOT + * bit 2 set = EXTENDED + * + * Either bit can be set, or both + */ +static int diag_buffer_enable = -1; +module_param(diag_buffer_enable, int, 0); +MODULE_PARM_DESC(diag_buffer_enable, " post diag buffers " + "(TRACE=1/SNAPSHOT=2/EXTENDED=4/default=0)"); + +static int disable_discovery = -1; +module_param(disable_discovery, int, 0); +MODULE_PARM_DESC(disable_discovery, " disable discovery "); + +/* permit overriding the host protection capabilities mask (EEDP/T10 PI) */ +static int prot_mask = 0; +module_param(prot_mask, int, 0); +MODULE_PARM_DESC(prot_mask, " host protection capabilities mask, def=7 "); + /** * struct sense_info - common structure for obtaining sense keys * @skey: sense key @@ -107,14 +145,18 @@ struct sense_info { }; +#define MPT2SAS_TURN_ON_FAULT_LED (0xFFFC) +#define MPT2SAS_PORT_ENABLE_COMPLETE (0xFFFD) +#define MPT2SAS_REMOVE_UNRESPONDING_DEVICES (0xFFFF) /** * struct fw_event_work - firmware event struct * @list: link list framework * @work: work object (ioc->fault_reset_work_q) + * @cancel_pending_work: flag set during reset handling * @ioc: per adapter object + * @device_handle: device handle * @VF_ID: virtual function id * @VP_ID: virtual port id - * @host_reset_handling: handling events during host reset * @ignore: flag meaning this event has been marked to ignore * @event: firmware event MPI2_EVENT_XXX defined in mpt2_ioc.h * @event_data: reply event data payload follows @@ -123,16 +165,20 @@ struct sense_info { */ struct fw_event_work { struct list_head list; - struct work_struct work; + u8 cancel_pending_work; + struct delayed_work delayed_work; struct MPT2SAS_ADAPTER *ioc; + u16 device_handle; u8 VF_ID; u8 VP_ID; - u8 host_reset_handling; u8 ignore; u16 event; void *event_data; }; +/* raid transport support */ +static struct raid_template *mpt2sas_raid_template; + /** * struct _scsi_io_transfer - scsi io transfer * @handle: sas device handle (assigned by firmware) @@ -215,9 +261,15 @@ static struct pci_device_id scsih_pci_table[] = { PCI_ANY_ID, PCI_ANY_ID }, { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_6, PCI_ANY_ID, PCI_ANY_ID }, - { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_7, + /* Mustang ~ 2308 */ + { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_1, + PCI_ANY_ID, PCI_ANY_ID }, + { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_2, PCI_ANY_ID, PCI_ANY_ID }, - { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_8, + { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_3, + PCI_ANY_ID, PCI_ANY_ID }, + /* SSS6200 */ + { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SSS6200, PCI_ANY_ID, PCI_ANY_ID }, {0} /* Terminating entry */ }; @@ -349,31 +401,34 @@ _scsih_get_sas_address(struct MPT2SAS_ADAPTER *ioc, u16 handle, Mpi2SasDevicePage0_t sas_device_pg0; Mpi2ConfigReply_t mpi_reply; u32 ioc_status; + *sas_address = 0; if (handle <= ioc->sas_hba.num_phys) { *sas_address = ioc->sas_hba.sas_address; return 0; - } else - *sas_address = 0; + } if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->name, + __FILE__, __LINE__, __func__); return -ENXIO; } - ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & - MPI2_IOCSTATUS_MASK; - if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { - printk(MPT2SAS_ERR_FMT "handle(0x%04x), ioc_status(0x%04x)" - "\nfailure at %s:%d/%s()!\n", ioc->name, handle, ioc_status, - __FILE__, __LINE__, __func__); - return -EIO; + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; + if (ioc_status == MPI2_IOCSTATUS_SUCCESS) { + *sas_address = le64_to_cpu(sas_device_pg0.SASAddress); + return 0; } - *sas_address = le64_to_cpu(sas_device_pg0.SASAddress); - return 0; + /* we hit this becuase the given parent handle doesn't exist */ + if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) + return -ENXIO; + /* else error case */ + printk(MPT2SAS_ERR_FMT "handle(0x%04x), ioc_status(0x%04x), " + "failure at %s:%d/%s()!\n", ioc->name, handle, ioc_status, + __FILE__, __LINE__, __func__); + return -EIO; } /** @@ -383,7 +438,7 @@ _scsih_get_sas_address(struct MPT2SAS_ADAPTER *ioc, u16 handle, * @is_raid: [flag] 1 = raid object, 0 = sas object * * Determines whether this device should be first reported device to - * to scsi-ml or sas transport, this purpose is for persistant boot device. + * to scsi-ml or sas transport, this purpose is for persistent boot device. * There are primary, alternate, and current entries in bios page 2. The order * priority is primary, alternate, then current. This routine saves * the corresponding device object and is_raid flag in the ioc object. @@ -401,7 +456,11 @@ _scsih_determine_boot_device(struct MPT2SAS_ADAPTER *ioc, u16 slot; /* only process this function when driver loads */ - if (!ioc->wait_for_port_enable_to_complete) + if (!ioc->is_driver_loading) + return; + + /* no Bios, return immediately */ + if (!ioc->bios_pg3.BiosVersion) return; if (!is_raid) { @@ -424,7 +483,7 @@ _scsih_determine_boot_device(struct MPT2SAS_ADAPTER *ioc, (ioc->bios_pg2.ReqBootDeviceForm & MPI2_BIOSPAGE2_FORM_MASK), &ioc->bios_pg2.RequestedBootDevice)) { - dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: req_boot_device(0x%016llx)\n", ioc->name, __func__, (unsigned long long)sas_address)); @@ -439,7 +498,7 @@ _scsih_determine_boot_device(struct MPT2SAS_ADAPTER *ioc, (ioc->bios_pg2.ReqAltBootDeviceForm & MPI2_BIOSPAGE2_FORM_MASK), &ioc->bios_pg2.RequestedAltBootDevice)) { - dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: req_alt_boot_device(0x%016llx)\n", ioc->name, __func__, (unsigned long long)sas_address)); @@ -454,7 +513,7 @@ _scsih_determine_boot_device(struct MPT2SAS_ADAPTER *ioc, (ioc->bios_pg2.CurrentBootDeviceForm & MPI2_BIOSPAGE2_FORM_MASK), &ioc->bios_pg2.CurrentBootDevice)) { - dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: current_boot_device(0x%016llx)\n", ioc->name, __func__, (unsigned long long)sas_address)); @@ -477,27 +536,17 @@ struct _sas_device * mpt2sas_scsih_sas_device_find_by_sas_address(struct MPT2SAS_ADAPTER *ioc, u64 sas_address) { - struct _sas_device *sas_device, *r; + struct _sas_device *sas_device; - r = NULL; - /* check the sas_device_init_list */ - list_for_each_entry(sas_device, &ioc->sas_device_init_list, - list) { - if (sas_device->sas_address != sas_address) - continue; - r = sas_device; - goto out; - } + list_for_each_entry(sas_device, &ioc->sas_device_list, list) + if (sas_device->sas_address == sas_address) + return sas_device; - /* then check the sas_device_list */ - list_for_each_entry(sas_device, &ioc->sas_device_list, list) { - if (sas_device->sas_address != sas_address) - continue; - r = sas_device; - goto out; - } - out: - return r; + list_for_each_entry(sas_device, &ioc->sas_device_init_list, list) + if (sas_device->sas_address == sas_address) + return sas_device; + + return NULL; } /** @@ -512,28 +561,17 @@ mpt2sas_scsih_sas_device_find_by_sas_address(struct MPT2SAS_ADAPTER *ioc, static struct _sas_device * _scsih_sas_device_find_by_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle) { - struct _sas_device *sas_device, *r; + struct _sas_device *sas_device; - r = NULL; - if (ioc->wait_for_port_enable_to_complete) { - list_for_each_entry(sas_device, &ioc->sas_device_init_list, - list) { - if (sas_device->handle != handle) - continue; - r = sas_device; - goto out; - } - } else { - list_for_each_entry(sas_device, &ioc->sas_device_list, list) { - if (sas_device->handle != handle) - continue; - r = sas_device; - goto out; - } - } + list_for_each_entry(sas_device, &ioc->sas_device_list, list) + if (sas_device->handle == handle) + return sas_device; - out: - return r; + list_for_each_entry(sas_device, &ioc->sas_device_init_list, list) + if (sas_device->handle == handle) + return sas_device; + + return NULL; } /** @@ -550,13 +588,16 @@ _scsih_sas_device_remove(struct MPT2SAS_ADAPTER *ioc, { unsigned long flags; + if (!sas_device) + return; + spin_lock_irqsave(&ioc->sas_device_lock, flags); list_del(&sas_device->list); - memset(sas_device, 0, sizeof(struct _sas_device)); kfree(sas_device); spin_unlock_irqrestore(&ioc->sas_device_lock, flags); } + /** * _scsih_sas_device_add - insert sas_device to the list. * @ioc: per adapter object @@ -571,7 +612,7 @@ _scsih_sas_device_add(struct MPT2SAS_ADAPTER *ioc, { unsigned long flags; - dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: handle" + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: handle" "(0x%04x), sas_addr(0x%016llx)\n", ioc->name, __func__, sas_device->handle, (unsigned long long)sas_device->sas_address)); @@ -580,8 +621,20 @@ _scsih_sas_device_add(struct MPT2SAS_ADAPTER *ioc, spin_unlock_irqrestore(&ioc->sas_device_lock, flags); if (!mpt2sas_transport_port_add(ioc, sas_device->handle, - sas_device->sas_address_parent)) + sas_device->sas_address_parent)) { _scsih_sas_device_remove(ioc, sas_device); + } else if (!sas_device->starget) { + /* When asyn scanning is enabled, its not possible to remove + * devices while scanning is turned on due to an oops in + * scsi_sysfs_add_sdev()->add_device()->sysfs_addrm_start() + */ + if (!ioc->is_driver_loading) { + mpt2sas_transport_port_remove(ioc, + sas_device->sas_address, + sas_device->sas_address_parent); + _scsih_sas_device_remove(ioc, sas_device); + } + } } /** @@ -598,14 +651,14 @@ _scsih_sas_device_init_add(struct MPT2SAS_ADAPTER *ioc, { unsigned long flags; - dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: handle" + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: handle" "(0x%04x), sas_addr(0x%016llx)\n", ioc->name, __func__, sas_device->handle, (unsigned long long)sas_device->sas_address)); spin_lock_irqsave(&ioc->sas_device_lock, flags); list_add_tail(&sas_device->list, &ioc->sas_device_init_list); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); _scsih_determine_boot_device(ioc, sas_device, 0); + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); } /** @@ -700,7 +753,7 @@ _scsih_raid_device_add(struct MPT2SAS_ADAPTER *ioc, { unsigned long flags; - dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: handle" + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: handle" "(0x%04x), wwid(0x%016llx)\n", ioc->name, __func__, raid_device->handle, (unsigned long long)raid_device->wwid)); @@ -714,7 +767,6 @@ _scsih_raid_device_add(struct MPT2SAS_ADAPTER *ioc, * @ioc: per adapter object * @raid_device: raid_device object * - * This is removed from the raid_device_list link list. */ static void _scsih_raid_device_remove(struct MPT2SAS_ADAPTER *ioc, @@ -724,7 +776,6 @@ _scsih_raid_device_remove(struct MPT2SAS_ADAPTER *ioc, spin_lock_irqsave(&ioc->raid_device_lock, flags); list_del(&raid_device->list); - memset(raid_device, 0, sizeof(struct _raid_device)); kfree(raid_device); spin_unlock_irqrestore(&ioc->raid_device_lock, flags); } @@ -821,7 +872,7 @@ _scsih_is_end_device(u32 device_info) } /** - * mptscsih_get_scsi_lookup - returns scmd entry + * _scsih_scsi_lookup_get - returns scmd entry * @ioc: per adapter object * @smid: system request message index * @@ -834,6 +885,28 @@ _scsih_scsi_lookup_get(struct MPT2SAS_ADAPTER *ioc, u16 smid) } /** + * _scsih_scsi_lookup_get_clear - returns scmd entry + * @ioc: per adapter object + * @smid: system request message index + * + * Returns the smid stored scmd pointer. + * Then will derefrence the stored scmd pointer. + */ +static inline struct scsi_cmnd * +_scsih_scsi_lookup_get_clear(struct MPT2SAS_ADAPTER *ioc, u16 smid) +{ + unsigned long flags; + struct scsi_cmnd *scmd; + + spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); + scmd = ioc->scsi_lookup[smid - 1].scmd; + ioc->scsi_lookup[smid - 1].scmd = NULL; + spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); + + return scmd; +} + +/** * _scsih_scsi_lookup_find_by_scmd - scmd lookup * @ioc: per adapter object * @smid: system request message index @@ -933,31 +1006,32 @@ _scsih_scsi_lookup_find_by_lun(struct MPT2SAS_ADAPTER *ioc, int id, } /** - * _scsih_get_chain_buffer_dma - obtain block of chains (dma address) + * _scsih_get_chain_buffer_tracker - obtain chain tracker * @ioc: per adapter object - * @smid: system request message index + * @smid: smid associated to an IO request * - * Returns phys pointer to chain buffer. + * Returns chain tracker(from ioc->free_chain_list) */ -static dma_addr_t -_scsih_get_chain_buffer_dma(struct MPT2SAS_ADAPTER *ioc, u16 smid) +static struct chain_tracker * +_scsih_get_chain_buffer_tracker(struct MPT2SAS_ADAPTER *ioc, u16 smid) { - return ioc->chain_dma + ((smid - 1) * (ioc->request_sz * - ioc->chains_needed_per_io)); -} + struct chain_tracker *chain_req; + unsigned long flags; -/** - * _scsih_get_chain_buffer - obtain block of chains assigned to a mf request - * @ioc: per adapter object - * @smid: system request message index - * - * Returns virt pointer to chain buffer. - */ -static void * -_scsih_get_chain_buffer(struct MPT2SAS_ADAPTER *ioc, u16 smid) -{ - return (void *)(ioc->chain + ((smid - 1) * (ioc->request_sz * - ioc->chains_needed_per_io))); + spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); + if (list_empty(&ioc->free_chain_list)) { + spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); + dfailprintk(ioc, printk(MPT2SAS_WARN_FMT "chain buffers not " + "available\n", ioc->name)); + return NULL; + } + chain_req = list_entry(ioc->free_chain_list.next, + struct chain_tracker, tracker_list); + list_del_init(&chain_req->tracker_list); + list_add_tail(&chain_req->tracker_list, + &ioc->scsi_lookup[smid - 1].chain_list); + spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); + return chain_req; } /** @@ -983,11 +1057,12 @@ _scsih_build_scatter_gather(struct MPT2SAS_ADAPTER *ioc, u32 chain_offset; u32 chain_length; u32 chain_flags; - u32 sges_left; + int sges_left; u32 sges_in_segment; u32 sgl_flags; u32 sgl_flags_last_element; u32 sgl_flags_end_buffer; + struct chain_tracker *chain_req; mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); @@ -1004,7 +1079,7 @@ _scsih_build_scatter_gather(struct MPT2SAS_ADAPTER *ioc, sg_scmd = scsi_sglist(scmd); sges_left = scsi_dma_map(scmd); - if (!sges_left) { + if (sges_left < 0) { sdev_printk(KERN_ERR, scmd->device, "pci_map_sg" " failed: request for %d bytes!\n", scsi_bufflen(scmd)); return -ENOMEM; @@ -1035,8 +1110,11 @@ _scsih_build_scatter_gather(struct MPT2SAS_ADAPTER *ioc, /* initializing the chain flags and pointers */ chain_flags = MPI2_SGE_FLAGS_CHAIN_ELEMENT << MPI2_SGE_FLAGS_SHIFT; - chain = _scsih_get_chain_buffer(ioc, smid); - chain_dma = _scsih_get_chain_buffer_dma(ioc, smid); + chain_req = _scsih_get_chain_buffer_tracker(ioc, smid); + if (!chain_req) + return -1; + chain = chain_req->chain_buffer; + chain_dma = chain_req->chain_buffer_dma; do { sges_in_segment = (sges_left <= ioc->max_sges_in_chain_message) ? sges_left : @@ -1072,8 +1150,11 @@ _scsih_build_scatter_gather(struct MPT2SAS_ADAPTER *ioc, sges_in_segment--; } - chain_dma += ioc->request_sz; - chain += ioc->request_sz; + chain_req = _scsih_get_chain_buffer_tracker(ioc, smid); + if (!chain_req) + return -1; + chain = chain_req->chain_buffer; + chain_dma = chain_req->chain_buffer_dma; } while (1); @@ -1096,28 +1177,24 @@ _scsih_build_scatter_gather(struct MPT2SAS_ADAPTER *ioc, } /** - * _scsih_change_queue_depth - setting device queue depth + * _scsih_adjust_queue_depth - setting device queue depth * @sdev: scsi device struct * @qdepth: requested queue depth - * @reason: calling context * - * Returns queue depth. + * + * Returns nothing */ -static int -_scsih_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason) +static void +_scsih_adjust_queue_depth(struct scsi_device *sdev, int qdepth) { struct Scsi_Host *shost = sdev->host; int max_depth; - int tag_type; struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); struct MPT2SAS_DEVICE *sas_device_priv_data; struct MPT2SAS_TARGET *sas_target_priv_data; struct _sas_device *sas_device; unsigned long flags; - if (reason != SCSI_QDEPTH_DEFAULT) - return -EOPNOTSUPP; - max_depth = shost->can_queue; /* limit max device queue for SATA to 32 */ @@ -1132,10 +1209,10 @@ _scsih_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason) spin_lock_irqsave(&ioc->sas_device_lock, flags); sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, sas_device_priv_data->sas_target->sas_address); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); if (sas_device && sas_device->device_info & MPI2_SAS_DEVICE_INFO_SATA_DEVICE) max_depth = MPT2SAS_SATA_QUEUE_DEPTH; + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); not_sata: @@ -1143,8 +1220,27 @@ _scsih_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason) max_depth = 1; if (qdepth > max_depth) qdepth = max_depth; - tag_type = (qdepth == 1) ? 0 : MSG_SIMPLE_TAG; - scsi_adjust_queue_depth(sdev, tag_type, qdepth); + scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth); +} + +/** + * _scsih_change_queue_depth - setting device queue depth + * @sdev: scsi device struct + * @qdepth: requested queue depth + * @reason: SCSI_QDEPTH_DEFAULT/SCSI_QDEPTH_QFULL/SCSI_QDEPTH_RAMP_UP + * (see include/scsi/scsi_host.h for definition) + * + * Returns queue depth. + */ +static int +_scsih_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason) +{ + if (reason == SCSI_QDEPTH_DEFAULT || reason == SCSI_QDEPTH_RAMP_UP) + _scsih_adjust_queue_depth(sdev, qdepth); + else if (reason == SCSI_QDEPTH_QFULL) + scsi_track_queue_full(sdev, qdepth); + else + return -EOPNOTSUPP; if (sdev->inquiry_len > 7) sdev_printk(KERN_INFO, sdev, "qdepth(%d), tagged(%d), " @@ -1213,6 +1309,8 @@ _scsih_target_alloc(struct scsi_target *starget) sas_target_priv_data->handle = raid_device->handle; sas_target_priv_data->sas_address = raid_device->wwid; sas_target_priv_data->flags |= MPT_TARGET_FLAGS_VOLUME; + if (ioc->is_warpdrive) + sas_target_priv_data->raid_device = raid_device; raid_device->starget = starget; } spin_unlock_irqrestore(&ioc->raid_device_lock, flags); @@ -1231,7 +1329,7 @@ _scsih_target_alloc(struct scsi_target *starget) sas_device->starget = starget; sas_device->id = starget->id; sas_device->channel = starget->channel; - if (sas_device->hidden_raid_component) + if (test_bit(sas_device->handle, ioc->pd_handles)) sas_target_priv_data->flags |= MPT_TARGET_FLAGS_RAID_COMPONENT; } @@ -1332,21 +1430,21 @@ _scsih_slave_alloc(struct scsi_device *sdev) if (raid_device) raid_device->sdev = sdev; /* raid is single lun */ spin_unlock_irqrestore(&ioc->raid_device_lock, flags); - } else { - /* set TLR bit for SSP devices */ - if (!(ioc->facts.IOCCapabilities & - MPI2_IOCFACTS_CAPABILITY_TLR)) - goto out; + } + + if (!(sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME)) { spin_lock_irqsave(&ioc->sas_device_lock, flags); sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, - sas_device_priv_data->sas_target->sas_address); + sas_target_priv_data->sas_address); + if (sas_device && (sas_device->starget == NULL)) { + sdev_printk(KERN_INFO, sdev, + "%s : sas_device->starget set to starget @ %d\n", + __func__, __LINE__); + sas_device->starget = starget; + } spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - if (sas_device && sas_device->device_info & - MPI2_SAS_DEVICE_INFO_SSP_TARGET) - sas_device_priv_data->flags |= MPT_DEVICE_TLR_ON; } - out: return 0; } @@ -1361,6 +1459,10 @@ _scsih_slave_destroy(struct scsi_device *sdev) { struct MPT2SAS_TARGET *sas_target_priv_data; struct scsi_target *starget; + struct Scsi_Host *shost; + struct MPT2SAS_ADAPTER *ioc; + struct _sas_device *sas_device; + unsigned long flags; if (!sdev->hostdata) return; @@ -1368,6 +1470,19 @@ _scsih_slave_destroy(struct scsi_device *sdev) starget = scsi_target(sdev); sas_target_priv_data = starget->hostdata; sas_target_priv_data->num_luns--; + + shost = dev_to_shost(&starget->dev); + ioc = shost_priv(shost); + + if (!(sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME)) { + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, + sas_target_priv_data->sas_address); + if (sas_device && !sas_target_priv_data->num_luns) + sas_device->starget = NULL; + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + } + kfree(sdev->hostdata); sdev->hostdata = NULL; } @@ -1375,12 +1490,12 @@ _scsih_slave_destroy(struct scsi_device *sdev) /** * _scsih_display_sata_capabilities - sata capabilities * @ioc: per adapter object - * @sas_device: the sas_device object + * @handle: device handle * @sdev: scsi device struct */ static void _scsih_display_sata_capabilities(struct MPT2SAS_ADAPTER *ioc, - struct _sas_device *sas_device, struct scsi_device *sdev) + u16 handle, struct scsi_device *sdev) { Mpi2ConfigReply_t mpi_reply; Mpi2SasDevicePage0_t sas_device_pg0; @@ -1389,7 +1504,7 @@ _scsih_display_sata_capabilities(struct MPT2SAS_ADAPTER *ioc, u32 device_info; if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, - MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, sas_device->handle))) { + MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) { printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, __func__); return; @@ -1404,7 +1519,7 @@ _scsih_display_sata_capabilities(struct MPT2SAS_ADAPTER *ioc, } flags = le16_to_cpu(sas_device_pg0.Flags); - device_info = le16_to_cpu(sas_device_pg0.DeviceInfo); + device_info = le32_to_cpu(sas_device_pg0.DeviceInfo); sdev_printk(KERN_INFO, sdev, "atapi(%s), ncq(%s), asyn_notify(%s), smart(%s), fua(%s), " @@ -1419,11 +1534,166 @@ _scsih_display_sata_capabilities(struct MPT2SAS_ADAPTER *ioc, } /** + * _scsih_is_raid - return boolean indicating device is raid volume + * @dev the device struct object + */ +static int +_scsih_is_raid(struct device *dev) +{ + struct scsi_device *sdev = to_scsi_device(dev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(sdev->host); + + if (ioc->is_warpdrive) + return 0; + return (sdev->channel == RAID_CHANNEL) ? 1 : 0; +} + +/** + * _scsih_get_resync - get raid volume resync percent complete + * @dev the device struct object + */ +static void +_scsih_get_resync(struct device *dev) +{ + struct scsi_device *sdev = to_scsi_device(dev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(sdev->host); + static struct _raid_device *raid_device; + unsigned long flags; + Mpi2RaidVolPage0_t vol_pg0; + Mpi2ConfigReply_t mpi_reply; + u32 volume_status_flags; + u8 percent_complete; + u16 handle; + + percent_complete = 0; + handle = 0; + if (ioc->is_warpdrive) + goto out; + + spin_lock_irqsave(&ioc->raid_device_lock, flags); + raid_device = _scsih_raid_device_find_by_id(ioc, sdev->id, + sdev->channel); + if (raid_device) { + handle = raid_device->handle; + percent_complete = raid_device->percent_complete; + } + spin_unlock_irqrestore(&ioc->raid_device_lock, flags); + + if (!handle) + goto out; + + if (mpt2sas_config_get_raid_volume_pg0(ioc, &mpi_reply, &vol_pg0, + MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, handle, + sizeof(Mpi2RaidVolPage0_t))) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + percent_complete = 0; + goto out; + } + + volume_status_flags = le32_to_cpu(vol_pg0.VolumeStatusFlags); + if (!(volume_status_flags & + MPI2_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS)) + percent_complete = 0; + + out: + raid_set_resync(mpt2sas_raid_template, dev, percent_complete); +} + +/** + * _scsih_get_state - get raid volume level + * @dev the device struct object + */ +static void +_scsih_get_state(struct device *dev) +{ + struct scsi_device *sdev = to_scsi_device(dev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(sdev->host); + static struct _raid_device *raid_device; + unsigned long flags; + Mpi2RaidVolPage0_t vol_pg0; + Mpi2ConfigReply_t mpi_reply; + u32 volstate; + enum raid_state state = RAID_STATE_UNKNOWN; + u16 handle = 0; + + spin_lock_irqsave(&ioc->raid_device_lock, flags); + raid_device = _scsih_raid_device_find_by_id(ioc, sdev->id, + sdev->channel); + if (raid_device) + handle = raid_device->handle; + spin_unlock_irqrestore(&ioc->raid_device_lock, flags); + + if (!raid_device) + goto out; + + if (mpt2sas_config_get_raid_volume_pg0(ioc, &mpi_reply, &vol_pg0, + MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, handle, + sizeof(Mpi2RaidVolPage0_t))) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + goto out; + } + + volstate = le32_to_cpu(vol_pg0.VolumeStatusFlags); + if (volstate & MPI2_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS) { + state = RAID_STATE_RESYNCING; + goto out; + } + + switch (vol_pg0.VolumeState) { + case MPI2_RAID_VOL_STATE_OPTIMAL: + case MPI2_RAID_VOL_STATE_ONLINE: + state = RAID_STATE_ACTIVE; + break; + case MPI2_RAID_VOL_STATE_DEGRADED: + state = RAID_STATE_DEGRADED; + break; + case MPI2_RAID_VOL_STATE_FAILED: + case MPI2_RAID_VOL_STATE_MISSING: + state = RAID_STATE_OFFLINE; + break; + } + out: + raid_set_state(mpt2sas_raid_template, dev, state); +} + +/** + * _scsih_set_level - set raid level + * @sdev: scsi device struct + * @volume_type: volume type + */ +static void +_scsih_set_level(struct scsi_device *sdev, u8 volume_type) +{ + enum raid_level level = RAID_LEVEL_UNKNOWN; + + switch (volume_type) { + case MPI2_RAID_VOL_TYPE_RAID0: + level = RAID_LEVEL_0; + break; + case MPI2_RAID_VOL_TYPE_RAID10: + level = RAID_LEVEL_10; + break; + case MPI2_RAID_VOL_TYPE_RAID1E: + level = RAID_LEVEL_1E; + break; + case MPI2_RAID_VOL_TYPE_RAID1: + level = RAID_LEVEL_1; + break; + } + + raid_set_level(mpt2sas_raid_template, &sdev->sdev_gendev, level); +} + +/** * _scsih_get_volume_capabilities - volume capabilities * @ioc: per adapter object * @sas_device: the raid_device object + * + * Returns 0 for success, else 1 */ -static void +static int _scsih_get_volume_capabilities(struct MPT2SAS_ADAPTER *ioc, struct _raid_device *raid_device) { @@ -1436,9 +1706,10 @@ _scsih_get_volume_capabilities(struct MPT2SAS_ADAPTER *ioc, if ((mpt2sas_config_get_number_pds(ioc, raid_device->handle, &num_pds)) || !num_pds) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - return; + dfailprintk(ioc, printk(MPT2SAS_WARN_FMT + "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, + __func__)); + return 1; } raid_device->num_pds = num_pds; @@ -1446,17 +1717,19 @@ _scsih_get_volume_capabilities(struct MPT2SAS_ADAPTER *ioc, sizeof(Mpi2RaidVol0PhysDisk_t)); vol_pg0 = kzalloc(sz, GFP_KERNEL); if (!vol_pg0) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - return; + dfailprintk(ioc, printk(MPT2SAS_WARN_FMT + "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, + __func__)); + return 1; } if ((mpt2sas_config_get_raid_volume_pg0(ioc, &mpi_reply, vol_pg0, MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, raid_device->handle, sz))) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); + dfailprintk(ioc, printk(MPT2SAS_WARN_FMT + "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, + __func__)); kfree(vol_pg0); - return; + return 1; } raid_device->volume_type = vol_pg0->VolumeType; @@ -1476,6 +1749,247 @@ _scsih_get_volume_capabilities(struct MPT2SAS_ADAPTER *ioc, } kfree(vol_pg0); + return 0; +} +/** + * _scsih_disable_ddio - Disable direct I/O for all the volumes + * @ioc: per adapter object + */ +static void +_scsih_disable_ddio(struct MPT2SAS_ADAPTER *ioc) +{ + Mpi2RaidVolPage1_t vol_pg1; + Mpi2ConfigReply_t mpi_reply; + struct _raid_device *raid_device; + u16 handle; + u16 ioc_status; + unsigned long flags; + + handle = 0xFFFF; + while (!(mpt2sas_config_get_raid_volume_pg1(ioc, &mpi_reply, + &vol_pg1, MPI2_RAID_VOLUME_PGAD_FORM_GET_NEXT_HANDLE, handle))) { + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) + break; + handle = le16_to_cpu(vol_pg1.DevHandle); + spin_lock_irqsave(&ioc->raid_device_lock, flags); + raid_device = _scsih_raid_device_find_by_handle(ioc, handle); + if (raid_device) + raid_device->direct_io_enabled = 0; + spin_unlock_irqrestore(&ioc->raid_device_lock, flags); + } + return; +} + + +/** + * _scsih_get_num_volumes - Get number of volumes in the ioc + * @ioc: per adapter object + */ +static u8 +_scsih_get_num_volumes(struct MPT2SAS_ADAPTER *ioc) +{ + Mpi2RaidVolPage1_t vol_pg1; + Mpi2ConfigReply_t mpi_reply; + u16 handle; + u8 vol_cnt = 0; + u16 ioc_status; + + handle = 0xFFFF; + while (!(mpt2sas_config_get_raid_volume_pg1(ioc, &mpi_reply, + &vol_pg1, MPI2_RAID_VOLUME_PGAD_FORM_GET_NEXT_HANDLE, handle))) { + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) + break; + vol_cnt++; + handle = le16_to_cpu(vol_pg1.DevHandle); + } + return vol_cnt; +} + + +/** + * _scsih_init_warpdrive_properties - Set properties for warpdrive direct I/O. + * @ioc: per adapter object + * @raid_device: the raid_device object + */ +static void +_scsih_init_warpdrive_properties(struct MPT2SAS_ADAPTER *ioc, + struct _raid_device *raid_device) +{ + Mpi2RaidVolPage0_t *vol_pg0; + Mpi2RaidPhysDiskPage0_t pd_pg0; + Mpi2ConfigReply_t mpi_reply; + u16 sz; + u8 num_pds, count; + unsigned long stripe_sz, block_sz; + u8 stripe_exp, block_exp; + u64 dev_max_lba; + + if (!ioc->is_warpdrive) + return; + + if (ioc->mfg_pg10_hide_flag == MFG_PAGE10_EXPOSE_ALL_DISKS) { + printk(MPT2SAS_INFO_FMT "WarpDrive : Direct IO is disabled " + "globally as drives are exposed\n", ioc->name); + return; + } + if (_scsih_get_num_volumes(ioc) > 1) { + _scsih_disable_ddio(ioc); + printk(MPT2SAS_INFO_FMT "WarpDrive : Direct IO is disabled " + "globally as number of drives > 1\n", ioc->name); + return; + } + if ((mpt2sas_config_get_number_pds(ioc, raid_device->handle, + &num_pds)) || !num_pds) { + printk(MPT2SAS_INFO_FMT "WarpDrive : Direct IO is disabled " + "Failure in computing number of drives\n", ioc->name); + return; + } + + sz = offsetof(Mpi2RaidVolPage0_t, PhysDisk) + (num_pds * + sizeof(Mpi2RaidVol0PhysDisk_t)); + vol_pg0 = kzalloc(sz, GFP_KERNEL); + if (!vol_pg0) { + printk(MPT2SAS_INFO_FMT "WarpDrive : Direct IO is disabled " + "Memory allocation failure for RVPG0\n", ioc->name); + return; + } + + if ((mpt2sas_config_get_raid_volume_pg0(ioc, &mpi_reply, vol_pg0, + MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, raid_device->handle, sz))) { + printk(MPT2SAS_INFO_FMT "WarpDrive : Direct IO is disabled " + "Failure in retrieving RVPG0\n", ioc->name); + kfree(vol_pg0); + return; + } + + /* + * WARPDRIVE:If number of physical disks in a volume exceeds the max pds + * assumed for WARPDRIVE, disable direct I/O + */ + if (num_pds > MPT_MAX_WARPDRIVE_PDS) { + printk(MPT2SAS_WARN_FMT "WarpDrive : Direct IO is disabled " + "for the drive with handle(0x%04x): num_mem=%d, " + "max_mem_allowed=%d\n", ioc->name, raid_device->handle, + num_pds, MPT_MAX_WARPDRIVE_PDS); + kfree(vol_pg0); + return; + } + for (count = 0; count < num_pds; count++) { + if (mpt2sas_config_get_phys_disk_pg0(ioc, &mpi_reply, + &pd_pg0, MPI2_PHYSDISK_PGAD_FORM_PHYSDISKNUM, + vol_pg0->PhysDisk[count].PhysDiskNum) || + le16_to_cpu(pd_pg0.DevHandle) == + MPT2SAS_INVALID_DEVICE_HANDLE) { + printk(MPT2SAS_INFO_FMT "WarpDrive : Direct IO is " + "disabled for the drive with handle(0x%04x) member" + "handle retrieval failed for member number=%d\n", + ioc->name, raid_device->handle, + vol_pg0->PhysDisk[count].PhysDiskNum); + goto out_error; + } + /* Disable direct I/O if member drive lba exceeds 4 bytes */ + dev_max_lba = le64_to_cpu(pd_pg0.DeviceMaxLBA); + if (dev_max_lba >> 32) { + printk(MPT2SAS_INFO_FMT "WarpDrive : Direct IO is " + "disabled for the drive with handle(0x%04x) member" + "handle (0x%04x) unsupported max lba 0x%016llx\n", + ioc->name, raid_device->handle, + le16_to_cpu(pd_pg0.DevHandle), + (unsigned long long)dev_max_lba); + goto out_error; + } + + raid_device->pd_handle[count] = le16_to_cpu(pd_pg0.DevHandle); + } + + /* + * Assumption for WD: Direct I/O is not supported if the volume is + * not RAID0 + */ + if (raid_device->volume_type != MPI2_RAID_VOL_TYPE_RAID0) { + printk(MPT2SAS_INFO_FMT "WarpDrive : Direct IO is disabled " + "for the drive with handle(0x%04x): type=%d, " + "s_sz=%uK, blk_size=%u\n", ioc->name, + raid_device->handle, raid_device->volume_type, + (le32_to_cpu(vol_pg0->StripeSize) * + le16_to_cpu(vol_pg0->BlockSize)) / 1024, + le16_to_cpu(vol_pg0->BlockSize)); + goto out_error; + } + + stripe_sz = le32_to_cpu(vol_pg0->StripeSize); + stripe_exp = find_first_bit(&stripe_sz, 32); + if (stripe_exp == 32) { + printk(MPT2SAS_INFO_FMT "WarpDrive : Direct IO is disabled " + "for the drive with handle(0x%04x) invalid stripe sz %uK\n", + ioc->name, raid_device->handle, + (le32_to_cpu(vol_pg0->StripeSize) * + le16_to_cpu(vol_pg0->BlockSize)) / 1024); + goto out_error; + } + raid_device->stripe_exponent = stripe_exp; + block_sz = le16_to_cpu(vol_pg0->BlockSize); + block_exp = find_first_bit(&block_sz, 16); + if (block_exp == 16) { + printk(MPT2SAS_INFO_FMT "WarpDrive : Direct IO is disabled " + "for the drive with handle(0x%04x) invalid block sz %u\n", + ioc->name, raid_device->handle, + le16_to_cpu(vol_pg0->BlockSize)); + goto out_error; + } + raid_device->block_exponent = block_exp; + raid_device->direct_io_enabled = 1; + + printk(MPT2SAS_INFO_FMT "WarpDrive : Direct IO is Enabled for the drive" + " with handle(0x%04x)\n", ioc->name, raid_device->handle); + /* + * WARPDRIVE: Though the following fields are not used for direct IO, + * stored for future purpose: + */ + raid_device->max_lba = le64_to_cpu(vol_pg0->MaxLBA); + raid_device->stripe_sz = le32_to_cpu(vol_pg0->StripeSize); + raid_device->block_sz = le16_to_cpu(vol_pg0->BlockSize); + + + kfree(vol_pg0); + return; + +out_error: + raid_device->direct_io_enabled = 0; + for (count = 0; count < num_pds; count++) + raid_device->pd_handle[count] = 0; + kfree(vol_pg0); + return; +} + +/** + * _scsih_enable_tlr - setting TLR flags + * @ioc: per adapter object + * @sdev: scsi device struct + * + * Enabling Transaction Layer Retries for tape devices when + * vpd page 0x90 is present + * + */ +static void +_scsih_enable_tlr(struct MPT2SAS_ADAPTER *ioc, struct scsi_device *sdev) +{ + /* only for TAPE */ + if (sdev->type != TYPE_TAPE) + return; + + if (!(ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_TLR)) + return; + + sas_enable_tlr(sdev); + sdev_printk(KERN_INFO, sdev, "TLR %s\n", + sas_is_tlr_enabled(sdev) ? "Enabled" : "Disabled"); + return; + } /** @@ -1499,27 +2013,39 @@ _scsih_slave_configure(struct scsi_device *sdev) u8 ssp_target = 0; char *ds = ""; char *r_level = ""; + u16 handle, volume_handle = 0; + u64 volume_wwid = 0; qdepth = 1; sas_device_priv_data = sdev->hostdata; sas_device_priv_data->configured_lun = 1; sas_device_priv_data->flags &= ~MPT_DEVICE_FLAGS_INIT; sas_target_priv_data = sas_device_priv_data->sas_target; + handle = sas_target_priv_data->handle; /* raid volume handling */ if (sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME) { spin_lock_irqsave(&ioc->raid_device_lock, flags); - raid_device = _scsih_raid_device_find_by_handle(ioc, - sas_target_priv_data->handle); + raid_device = _scsih_raid_device_find_by_handle(ioc, handle); spin_unlock_irqrestore(&ioc->raid_device_lock, flags); if (!raid_device) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - return 0; + dfailprintk(ioc, printk(MPT2SAS_WARN_FMT + "failure at %s:%d/%s()!\n", ioc->name, __FILE__, + __LINE__, __func__)); + return 1; } - _scsih_get_volume_capabilities(ioc, raid_device); + if (_scsih_get_volume_capabilities(ioc, raid_device)) { + dfailprintk(ioc, printk(MPT2SAS_WARN_FMT + "failure at %s:%d/%s()!\n", ioc->name, __FILE__, + __LINE__, __func__)); + return 1; + } + /* + * WARPDRIVE: Initialize the required data for Direct IO + */ + _scsih_init_warpdrive_properties(ioc, raid_device); /* RAID Queue Depth Support * IS volume = underlying qdepth of drive type, either @@ -1546,7 +2072,7 @@ _scsih_slave_configure(struct scsi_device *sdev) case MPI2_RAID_VOL_TYPE_RAID1E: qdepth = MPT2SAS_RAID_QUEUE_DEPTH; if (ioc->manu_pg10.OEMIdentifier && - (ioc->manu_pg10.GenericFlags0 & + (le32_to_cpu(ioc->manu_pg10.GenericFlags0) & MFG10_GF0_R10_DISPLAY) && !(raid_device->num_pds % 2)) r_level = "RAID10"; @@ -1568,61 +2094,83 @@ _scsih_slave_configure(struct scsi_device *sdev) break; } - sdev_printk(KERN_INFO, sdev, "%s: " - "handle(0x%04x), wwid(0x%016llx), pd_count(%d), type(%s)\n", - r_level, raid_device->handle, - (unsigned long long)raid_device->wwid, - raid_device->num_pds, ds); + if (!ioc->hide_ir_msg) + sdev_printk(KERN_INFO, sdev, "%s: handle(0x%04x), " + "wwid(0x%016llx), pd_count(%d), type(%s)\n", + r_level, raid_device->handle, + (unsigned long long)raid_device->wwid, + raid_device->num_pds, ds); _scsih_change_queue_depth(sdev, qdepth, SCSI_QDEPTH_DEFAULT); + /* raid transport support */ + if (!ioc->is_warpdrive) + _scsih_set_level(sdev, raid_device->volume_type); return 0; } /* non-raid handling */ + if (sas_target_priv_data->flags & MPT_TARGET_FLAGS_RAID_COMPONENT) { + if (mpt2sas_config_get_volume_handle(ioc, handle, + &volume_handle)) { + dfailprintk(ioc, printk(MPT2SAS_WARN_FMT + "failure at %s:%d/%s()!\n", ioc->name, + __FILE__, __LINE__, __func__)); + return 1; + } + if (volume_handle && mpt2sas_config_get_volume_wwid(ioc, + volume_handle, &volume_wwid)) { + dfailprintk(ioc, printk(MPT2SAS_WARN_FMT + "failure at %s:%d/%s()!\n", ioc->name, + __FILE__, __LINE__, __func__)); + return 1; + } + } + spin_lock_irqsave(&ioc->sas_device_lock, flags); sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, sas_device_priv_data->sas_target->sas_address); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - if (sas_device) { - if (sas_target_priv_data->flags & - MPT_TARGET_FLAGS_RAID_COMPONENT) { - mpt2sas_config_get_volume_handle(ioc, - sas_device->handle, &sas_device->volume_handle); - mpt2sas_config_get_volume_wwid(ioc, - sas_device->volume_handle, - &sas_device->volume_wwid); - } - if (sas_device->device_info & MPI2_SAS_DEVICE_INFO_SSP_TARGET) { - qdepth = MPT2SAS_SAS_QUEUE_DEPTH; - ssp_target = 1; - ds = "SSP"; - } else { - qdepth = MPT2SAS_SATA_QUEUE_DEPTH; - if (sas_device->device_info & - MPI2_SAS_DEVICE_INFO_STP_TARGET) - ds = "STP"; - else if (sas_device->device_info & - MPI2_SAS_DEVICE_INFO_SATA_DEVICE) - ds = "SATA"; - } + if (!sas_device) { + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + dfailprintk(ioc, printk(MPT2SAS_WARN_FMT + "failure at %s:%d/%s()!\n", ioc->name, __FILE__, + __LINE__, __func__)); + return 1; + } + sas_device->volume_handle = volume_handle; + sas_device->volume_wwid = volume_wwid; + if (sas_device->device_info & MPI2_SAS_DEVICE_INFO_SSP_TARGET) { + qdepth = MPT2SAS_SAS_QUEUE_DEPTH; + ssp_target = 1; + ds = "SSP"; + } else { + qdepth = MPT2SAS_SATA_QUEUE_DEPTH; + if (sas_device->device_info & MPI2_SAS_DEVICE_INFO_STP_TARGET) + ds = "STP"; + else if (sas_device->device_info & + MPI2_SAS_DEVICE_INFO_SATA_DEVICE) + ds = "SATA"; + } + sdev_printk(KERN_INFO, sdev, "%s: handle(0x%04x), " + "sas_addr(0x%016llx), phy(%d), device_name(0x%016llx)\n", + ds, sas_device->handle, + (unsigned long long)sas_device->sas_address, + sas_device->phy, + (unsigned long long)sas_device->device_name); + sdev_printk(KERN_INFO, sdev, "%s: " + "enclosure_logical_id(0x%016llx), slot(%d)\n", ds, + (unsigned long long) sas_device->enclosure_logical_id, + sas_device->slot); - sdev_printk(KERN_INFO, sdev, "%s: handle(0x%04x), " - "sas_addr(0x%016llx), device_name(0x%016llx)\n", - ds, sas_device->handle, - (unsigned long long)sas_device->sas_address, - (unsigned long long)sas_device->device_name); - sdev_printk(KERN_INFO, sdev, "%s: " - "enclosure_logical_id(0x%016llx), slot(%d)\n", ds, - (unsigned long long) sas_device->enclosure_logical_id, - sas_device->slot); + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + if (!ssp_target) + _scsih_display_sata_capabilities(ioc, handle, sdev); - if (!ssp_target) - _scsih_display_sata_capabilities(ioc, sas_device, sdev); - } _scsih_change_queue_depth(sdev, qdepth, SCSI_QDEPTH_DEFAULT); - if (ssp_target) + if (ssp_target) { sas_read_port_mode_page(sdev); + _scsih_enable_tlr(ioc, sdev); + } return 0; } @@ -1741,6 +2289,7 @@ _scsih_tm_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) return 1; if (ioc->tm_cmds.smid != smid) return 1; + mpt2sas_base_flush_reply_queues(ioc); ioc->tm_cmds.status |= MPT2_CMD_COMPLETE; mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); if (mpi_reply) { @@ -1808,67 +2357,86 @@ mpt2sas_scsih_clear_tm_flag(struct MPT2SAS_ADAPTER *ioc, u16 handle) } } + /** * mpt2sas_scsih_issue_tm - main routine for sending tm requests * @ioc: per adapter struct * @device_handle: device handle + * @channel: the channel assigned by the OS + * @id: the id assigned by the OS * @lun: lun number * @type: MPI2_SCSITASKMGMT_TASKTYPE__XXX (defined in mpi2_init.h) * @smid_task: smid assigned to the task * @timeout: timeout in seconds - * Context: The calling function needs to acquire the tm_cmds.mutex + * @m_type: TM_MUTEX_ON or TM_MUTEX_OFF + * Context: user * * A generic API for sending task management requests to firmware. * - * The ioc->tm_cmds.status flag should be MPT2_CMD_NOT_USED before calling - * this API. - * * The callback index is set inside `ioc->tm_cb_idx`. * - * Return nothing. + * Return SUCCESS or FAILED. */ -void -mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint lun, - u8 type, u16 smid_task, ulong timeout) +int +mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint channel, + uint id, uint lun, u8 type, u16 smid_task, ulong timeout, + enum mutex_type m_type) { Mpi2SCSITaskManagementRequest_t *mpi_request; Mpi2SCSITaskManagementReply_t *mpi_reply; u16 smid = 0; u32 ioc_state; unsigned long timeleft; + struct scsiio_tracker *scsi_lookup = NULL; + int rc; + if (m_type == TM_MUTEX_ON) + mutex_lock(&ioc->tm_cmds.mutex); if (ioc->tm_cmds.status != MPT2_CMD_NOT_USED) { printk(MPT2SAS_INFO_FMT "%s: tm_cmd busy!!!\n", __func__, ioc->name); - return; + rc = FAILED; + goto err_out; } - if (ioc->shost_recovery) { + if (ioc->shost_recovery || ioc->remove_host || + ioc->pci_error_recovery) { printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n", __func__, ioc->name); - return; + rc = FAILED; + goto err_out; } ioc_state = mpt2sas_base_get_iocstate(ioc, 0); if (ioc_state & MPI2_DOORBELL_USED) { - dhsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "unexpected doorbell " + dhsprintk(ioc, printk(MPT2SAS_INFO_FMT "unexpected doorbell " "active!\n", ioc->name)); - goto issue_host_reset; + rc = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, + FORCE_BIG_HAMMER); + rc = (!rc) ? SUCCESS : FAILED; + goto err_out; } if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) { mpt2sas_base_fault_info(ioc, ioc_state & MPI2_DOORBELL_DATA_MASK); - goto issue_host_reset; + rc = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, + FORCE_BIG_HAMMER); + rc = (!rc) ? SUCCESS : FAILED; + goto err_out; } smid = mpt2sas_base_get_smid_hpr(ioc, ioc->tm_cb_idx); if (!smid) { printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", ioc->name, __func__); - return; + rc = FAILED; + goto err_out; } + if (type == MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK) + scsi_lookup = &ioc->scsi_lookup[smid_task - 1]; + dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "sending tm: handle(0x%04x)," " task_type(0x%02x), smid(%d)\n", ioc->name, handle, type, smid_task)); @@ -1876,25 +2444,29 @@ mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint lun, mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ioc->tm_cmds.smid = smid; memset(mpi_request, 0, sizeof(Mpi2SCSITaskManagementRequest_t)); + memset(ioc->tm_cmds.reply, 0, sizeof(Mpi2SCSITaskManagementReply_t)); mpi_request->Function = MPI2_FUNCTION_SCSI_TASK_MGMT; mpi_request->DevHandle = cpu_to_le16(handle); mpi_request->TaskType = type; mpi_request->TaskMID = cpu_to_le16(smid_task); - mpi_request->VP_ID = 0; /* TODO */ - mpi_request->VF_ID = 0; int_to_scsilun(lun, (struct scsi_lun *)mpi_request->LUN); mpt2sas_scsih_set_tm_flag(ioc, handle); init_completion(&ioc->tm_cmds.done); mpt2sas_base_put_smid_hi_priority(ioc, smid); timeleft = wait_for_completion_timeout(&ioc->tm_cmds.done, timeout*HZ); - mpt2sas_scsih_clear_tm_flag(ioc, handle); if (!(ioc->tm_cmds.status & MPT2_CMD_COMPLETE)) { printk(MPT2SAS_ERR_FMT "%s: timeout\n", ioc->name, __func__); _debug_dump_mf(mpi_request, sizeof(Mpi2SCSITaskManagementRequest_t)/4); - if (!(ioc->tm_cmds.status & MPT2_CMD_RESET)) - goto issue_host_reset; + if (!(ioc->tm_cmds.status & MPT2_CMD_RESET)) { + rc = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, + FORCE_BIG_HAMMER); + rc = (!rc) ? SUCCESS : FAILED; + ioc->tm_cmds.status = MPT2_CMD_NOT_USED; + mpt2sas_scsih_clear_tm_flag(ioc, handle); + goto err_out; + } } if (ioc->tm_cmds.status & MPT2_CMD_REPLY_VALID) { @@ -1904,17 +2476,115 @@ mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint lun, ioc->name, le16_to_cpu(mpi_reply->IOCStatus), le32_to_cpu(mpi_reply->IOCLogInfo), le32_to_cpu(mpi_reply->TerminationCount))); - if (ioc->logging_level & MPT_DEBUG_TM) + if (ioc->logging_level & MPT_DEBUG_TM) { _scsih_response_code(ioc, mpi_reply->ResponseCode); + if (mpi_reply->IOCStatus) + _debug_dump_mf(mpi_request, + sizeof(Mpi2SCSITaskManagementRequest_t)/4); + } + } + + switch (type) { + case MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK: + rc = SUCCESS; + if (scsi_lookup->scmd == NULL) + break; + rc = FAILED; + break; + + case MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET: + if (_scsih_scsi_lookup_find_by_target(ioc, id, channel)) + rc = FAILED; + else + rc = SUCCESS; + break; + + case MPI2_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET: + case MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET: + if (_scsih_scsi_lookup_find_by_lun(ioc, id, lun, channel)) + rc = FAILED; + else + rc = SUCCESS; + break; + case MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK: + rc = SUCCESS; + break; + default: + rc = FAILED; + break; + } + + mpt2sas_scsih_clear_tm_flag(ioc, handle); + ioc->tm_cmds.status = MPT2_CMD_NOT_USED; + if (m_type == TM_MUTEX_ON) + mutex_unlock(&ioc->tm_cmds.mutex); + + return rc; + + err_out: + if (m_type == TM_MUTEX_ON) + mutex_unlock(&ioc->tm_cmds.mutex); + return rc; +} + +/** + * _scsih_tm_display_info - displays info about the device + * @ioc: per adapter struct + * @scmd: pointer to scsi command object + * + * Called by task management callback handlers. + */ +static void +_scsih_tm_display_info(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd) +{ + struct scsi_target *starget = scmd->device->sdev_target; + struct MPT2SAS_TARGET *priv_target = starget->hostdata; + struct _sas_device *sas_device = NULL; + unsigned long flags; + char *device_str = NULL; + + if (!priv_target) + return; + if (ioc->hide_ir_msg) + device_str = "WarpDrive"; + else + device_str = "volume"; + + scsi_print_command(scmd); + if (priv_target->flags & MPT_TARGET_FLAGS_VOLUME) { + starget_printk(KERN_INFO, starget, "%s handle(0x%04x), " + "%s wwid(0x%016llx)\n", device_str, priv_target->handle, + device_str, (unsigned long long)priv_target->sas_address); + } else { + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, + priv_target->sas_address); + if (sas_device) { + if (priv_target->flags & + MPT_TARGET_FLAGS_RAID_COMPONENT) { + starget_printk(KERN_INFO, starget, + "volume handle(0x%04x), " + "volume wwid(0x%016llx)\n", + sas_device->volume_handle, + (unsigned long long)sas_device->volume_wwid); + } + starget_printk(KERN_INFO, starget, + "handle(0x%04x), sas_address(0x%016llx), phy(%d)\n", + sas_device->handle, + (unsigned long long)sas_device->sas_address, + sas_device->phy); + starget_printk(KERN_INFO, starget, + "enclosure_logical_id(0x%016llx), slot(%d)\n", + (unsigned long long)sas_device->enclosure_logical_id, + sas_device->slot); + } + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); } - return; - issue_host_reset: - mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, FORCE_BIG_HAMMER); } /** * _scsih_abort - eh threads main abort routine - * @sdev: scsi device struct + * @scmd: pointer to scsi command object * * Returns SUCCESS if command aborted else FAILED */ @@ -1926,16 +2596,15 @@ _scsih_abort(struct scsi_cmnd *scmd) u16 smid; u16 handle; int r; - struct scsi_cmnd *scmd_lookup; - printk(MPT2SAS_INFO_FMT "attempting task abort! scmd(%p)\n", - ioc->name, scmd); - scsi_print_command(scmd); + sdev_printk(KERN_INFO, scmd->device, "attempting task abort! " + "scmd(%p)\n", scmd); + _scsih_tm_display_info(ioc, scmd); sas_device_priv_data = scmd->device->hostdata; if (!sas_device_priv_data || !sas_device_priv_data->sas_target) { - printk(MPT2SAS_INFO_FMT "device been deleted! scmd(%p)\n", - ioc->name, scmd); + sdev_printk(KERN_INFO, scmd->device, "device been deleted! " + "scmd(%p)\n", scmd); scmd->result = DID_NO_CONNECT << 16; scmd->scsi_done(scmd); r = SUCCESS; @@ -1961,29 +2630,20 @@ _scsih_abort(struct scsi_cmnd *scmd) mpt2sas_halt_firmware(ioc); - mutex_lock(&ioc->tm_cmds.mutex); handle = sas_device_priv_data->sas_target->handle; - mpt2sas_scsih_issue_tm(ioc, handle, sas_device_priv_data->lun, - MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK, smid, 30); - - /* sanity check - see whether command actually completed */ - scmd_lookup = _scsih_scsi_lookup_get(ioc, smid); - if (scmd_lookup && (scmd_lookup->serial_number == scmd->serial_number)) - r = FAILED; - else - r = SUCCESS; - ioc->tm_cmds.status = MPT2_CMD_NOT_USED; - mutex_unlock(&ioc->tm_cmds.mutex); + r = mpt2sas_scsih_issue_tm(ioc, handle, scmd->device->channel, + scmd->device->id, scmd->device->lun, + MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK, smid, 30, TM_MUTEX_ON); out: - printk(MPT2SAS_INFO_FMT "task abort: %s scmd(%p)\n", - ioc->name, ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd); + sdev_printk(KERN_INFO, scmd->device, "task abort: %s scmd(%p)\n", + ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd); return r; } /** * _scsih_dev_reset - eh threads main device reset routine - * @sdev: scsi device struct + * @scmd: pointer to scsi command object * * Returns SUCCESS if command aborted else FAILED */ @@ -1997,14 +2657,16 @@ _scsih_dev_reset(struct scsi_cmnd *scmd) u16 handle; int r; - printk(MPT2SAS_INFO_FMT "attempting device reset! scmd(%p)\n", - ioc->name, scmd); - scsi_print_command(scmd); + struct scsi_target *starget = scmd->device->sdev_target; + + starget_printk(KERN_INFO, starget, "attempting device reset! " + "scmd(%p)\n", scmd); + _scsih_tm_display_info(ioc, scmd); sas_device_priv_data = scmd->device->hostdata; if (!sas_device_priv_data || !sas_device_priv_data->sas_target) { - printk(MPT2SAS_INFO_FMT "device been deleted! scmd(%p)\n", - ioc->name, scmd); + starget_printk(KERN_INFO, starget, "device been deleted! " + "scmd(%p)\n", scmd); scmd->result = DID_NO_CONNECT << 16; scmd->scsi_done(scmd); r = SUCCESS; @@ -2030,32 +2692,19 @@ _scsih_dev_reset(struct scsi_cmnd *scmd) goto out; } - mutex_lock(&ioc->tm_cmds.mutex); - mpt2sas_scsih_issue_tm(ioc, handle, 0, - MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET, scmd->device->lun, - 30); - - /* - * sanity check see whether all commands to this device been - * completed - */ - if (_scsih_scsi_lookup_find_by_lun(ioc, scmd->device->id, - scmd->device->lun, scmd->device->channel)) - r = FAILED; - else - r = SUCCESS; - ioc->tm_cmds.status = MPT2_CMD_NOT_USED; - mutex_unlock(&ioc->tm_cmds.mutex); + r = mpt2sas_scsih_issue_tm(ioc, handle, scmd->device->channel, + scmd->device->id, scmd->device->lun, + MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET, 0, 30, TM_MUTEX_ON); out: - printk(MPT2SAS_INFO_FMT "device reset: %s scmd(%p)\n", - ioc->name, ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd); + sdev_printk(KERN_INFO, scmd->device, "device reset: %s scmd(%p)\n", + ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd); return r; } /** * _scsih_target_reset - eh threads main target reset routine - * @sdev: scsi device struct + * @scmd: pointer to scsi command object * * Returns SUCCESS if command aborted else FAILED */ @@ -2068,15 +2717,16 @@ _scsih_target_reset(struct scsi_cmnd *scmd) unsigned long flags; u16 handle; int r; + struct scsi_target *starget = scmd->device->sdev_target; - printk(MPT2SAS_INFO_FMT "attempting target reset! scmd(%p)\n", - ioc->name, scmd); - scsi_print_command(scmd); + starget_printk(KERN_INFO, starget, "attempting target reset! " + "scmd(%p)\n", scmd); + _scsih_tm_display_info(ioc, scmd); sas_device_priv_data = scmd->device->hostdata; if (!sas_device_priv_data || !sas_device_priv_data->sas_target) { - printk(MPT2SAS_INFO_FMT "target been deleted! scmd(%p)\n", - ioc->name, scmd); + starget_printk(KERN_INFO, starget, "target been deleted! " + "scmd(%p)\n", scmd); scmd->result = DID_NO_CONNECT << 16; scmd->scsi_done(scmd); r = SUCCESS; @@ -2102,31 +2752,19 @@ _scsih_target_reset(struct scsi_cmnd *scmd) goto out; } - mutex_lock(&ioc->tm_cmds.mutex); - mpt2sas_scsih_issue_tm(ioc, handle, 0, - MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, 30); - - /* - * sanity check see whether all commands to this target been - * completed - */ - if (_scsih_scsi_lookup_find_by_target(ioc, scmd->device->id, - scmd->device->channel)) - r = FAILED; - else - r = SUCCESS; - ioc->tm_cmds.status = MPT2_CMD_NOT_USED; - mutex_unlock(&ioc->tm_cmds.mutex); + r = mpt2sas_scsih_issue_tm(ioc, handle, scmd->device->channel, + scmd->device->id, 0, MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, + 30, TM_MUTEX_ON); out: - printk(MPT2SAS_INFO_FMT "target reset: %s scmd(%p)\n", - ioc->name, ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd); + starget_printk(KERN_INFO, starget, "target reset: %s scmd(%p)\n", + ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd); return r; } /** * _scsih_host_reset - eh threads main host reset routine - * @sdev: scsi device struct + * @scmd: pointer to scsi command object * * Returns SUCCESS if command aborted else FAILED */ @@ -2170,8 +2808,9 @@ _scsih_fw_event_add(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work *fw_event) spin_lock_irqsave(&ioc->fw_event_lock, flags); list_add_tail(&fw_event->list, &ioc->fw_event_list); - INIT_WORK(&fw_event->work, _firmware_event_work); - queue_work(ioc->firmware_event_thread, &fw_event->work); + INIT_DELAYED_WORK(&fw_event->delayed_work, _firmware_event_work); + queue_delayed_work(ioc->firmware_event_thread, + &fw_event->delayed_work, 0); spin_unlock_irqrestore(&ioc->fw_event_lock, flags); } @@ -2198,63 +2837,101 @@ _scsih_fw_event_free(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work spin_unlock_irqrestore(&ioc->fw_event_lock, flags); } + /** - * _scsih_fw_event_add - requeue an event + * _scsih_error_recovery_delete_devices - remove devices not responding * @ioc: per adapter object - * @fw_event: object describing the event - * Context: This function will acquire ioc->fw_event_lock. * * Return nothing. */ static void -_scsih_fw_event_requeue(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work - *fw_event, unsigned long delay) +_scsih_error_recovery_delete_devices(struct MPT2SAS_ADAPTER *ioc) { - unsigned long flags; - if (ioc->firmware_event_thread == NULL) + struct fw_event_work *fw_event; + + if (ioc->is_driver_loading) return; - spin_lock_irqsave(&ioc->fw_event_lock, flags); - queue_work(ioc->firmware_event_thread, &fw_event->work); - spin_unlock_irqrestore(&ioc->fw_event_lock, flags); + fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC); + if (!fw_event) + return; + + fw_event->event = MPT2SAS_REMOVE_UNRESPONDING_DEVICES; + fw_event->ioc = ioc; + _scsih_fw_event_add(ioc, fw_event); +} + +/** + * mpt2sas_port_enable_complete - port enable completed (fake event) + * @ioc: per adapter object + * + * Return nothing. + */ +void +mpt2sas_port_enable_complete(struct MPT2SAS_ADAPTER *ioc) +{ + struct fw_event_work *fw_event; + + fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC); + if (!fw_event) + return; + fw_event->event = MPT2SAS_PORT_ENABLE_COMPLETE; + fw_event->ioc = ioc; + _scsih_fw_event_add(ioc, fw_event); } /** - * _scsih_fw_event_off - turn flag off preventing event handling + * _scsih_fw_event_cleanup_queue - cleanup event queue * @ioc: per adapter object * - * Used to prevent handling of firmware events during adapter reset - * driver unload. + * Walk the firmware event queue, either killing timers, or waiting + * for outstanding events to complete * * Return nothing. */ static void -_scsih_fw_event_off(struct MPT2SAS_ADAPTER *ioc) +_scsih_fw_event_cleanup_queue(struct MPT2SAS_ADAPTER *ioc) { - unsigned long flags; + struct fw_event_work *fw_event, *next; - spin_lock_irqsave(&ioc->fw_event_lock, flags); - ioc->fw_events_off = 1; - spin_unlock_irqrestore(&ioc->fw_event_lock, flags); + if (list_empty(&ioc->fw_event_list) || + !ioc->firmware_event_thread || in_interrupt()) + return; + list_for_each_entry_safe(fw_event, next, &ioc->fw_event_list, list) { + if (cancel_delayed_work(&fw_event->delayed_work)) { + _scsih_fw_event_free(ioc, fw_event); + continue; + } + fw_event->cancel_pending_work = 1; + } } /** - * _scsih_fw_event_on - turn flag on allowing firmware event handling + * _scsih_ublock_io_all_device - unblock every device * @ioc: per adapter object * - * Returns nothing. + * change the device state from block to running */ static void -_scsih_fw_event_on(struct MPT2SAS_ADAPTER *ioc) +_scsih_ublock_io_all_device(struct MPT2SAS_ADAPTER *ioc) { - unsigned long flags; + struct MPT2SAS_DEVICE *sas_device_priv_data; + struct scsi_device *sdev; - spin_lock_irqsave(&ioc->fw_event_lock, flags); - ioc->fw_events_off = 0; - spin_unlock_irqrestore(&ioc->fw_event_lock, flags); + shost_for_each_device(sdev, ioc->shost) { + sas_device_priv_data = sdev->hostdata; + if (!sas_device_priv_data) + continue; + if (!sas_device_priv_data->block) + continue; + sas_device_priv_data->block = 0; + dewtprintk(ioc, sdev_printk(KERN_INFO, sdev, "device_running, " + "handle(0x%04x)\n", + sas_device_priv_data->sas_target->handle)); + scsi_internal_device_unblock(sdev, SDEV_RUNNING); + } } - /** * _scsih_ublock_io_device - set the device state to SDEV_RUNNING * @ioc: per adapter object @@ -2263,7 +2940,7 @@ _scsih_fw_event_on(struct MPT2SAS_ADAPTER *ioc) * During device pull we need to appropiately set the sdev state. */ static void -_scsih_ublock_io_device(struct MPT2SAS_ADAPTER *ioc, u16 handle) +_scsih_ublock_io_device(struct MPT2SAS_ADAPTER *ioc, u64 sas_address) { struct MPT2SAS_DEVICE *sas_device_priv_data; struct scsi_device *sdev; @@ -2274,17 +2951,47 @@ _scsih_ublock_io_device(struct MPT2SAS_ADAPTER *ioc, u16 handle) continue; if (!sas_device_priv_data->block) continue; - if (sas_device_priv_data->sas_target->handle == handle) { + if (sas_device_priv_data->sas_target->sas_address == + sas_address) { dewtprintk(ioc, sdev_printk(KERN_INFO, sdev, MPT2SAS_INFO_FMT "SDEV_RUNNING: " - "handle(0x%04x)\n", ioc->name, handle)); + "sas address(0x%016llx)\n", ioc->name, + (unsigned long long)sas_address)); sas_device_priv_data->block = 0; - scsi_internal_device_unblock(sdev); + scsi_internal_device_unblock(sdev, SDEV_RUNNING); } } } /** + * _scsih_block_io_all_device - set the device state to SDEV_BLOCK + * @ioc: per adapter object + * @handle: device handle + * + * During device pull we need to appropiately set the sdev state. + */ +static void +_scsih_block_io_all_device(struct MPT2SAS_ADAPTER *ioc) +{ + struct MPT2SAS_DEVICE *sas_device_priv_data; + struct scsi_device *sdev; + + shost_for_each_device(sdev, ioc->shost) { + sas_device_priv_data = sdev->hostdata; + if (!sas_device_priv_data) + continue; + if (sas_device_priv_data->block) + continue; + sas_device_priv_data->block = 1; + dewtprintk(ioc, sdev_printk(KERN_INFO, sdev, "device_blocked, " + "handle(0x%04x)\n", + sas_device_priv_data->sas_target->handle)); + scsi_internal_device_block(sdev); + } +} + + +/** * _scsih_block_io_device - set the device state to SDEV_BLOCK * @ioc: per adapter object * @handle: device handle @@ -2342,10 +3049,10 @@ _scsih_block_io_to_children_attached_to_ex(struct MPT2SAS_ADAPTER *ioc, sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, mpt2sas_port->remote_identify.sas_address); + if (sas_device) + set_bit(sas_device->handle, + ioc->blocking_handles); spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - if (!sas_device) - continue; - _scsih_block_io_device(ioc, sas_device->handle); } } @@ -2353,15 +3060,12 @@ _scsih_block_io_to_children_attached_to_ex(struct MPT2SAS_ADAPTER *ioc, &sas_expander->sas_port_list, port_list) { if (mpt2sas_port->remote_identify.device_type == - MPI2_SAS_DEVICE_INFO_EDGE_EXPANDER || + SAS_EDGE_EXPANDER_DEVICE || mpt2sas_port->remote_identify.device_type == - MPI2_SAS_DEVICE_INFO_FANOUT_EXPANDER) { - - spin_lock_irqsave(&ioc->sas_node_lock, flags); + SAS_FANOUT_EXPANDER_DEVICE) { expander_sibling = mpt2sas_scsih_expander_find_by_sas_address( ioc, mpt2sas_port->remote_identify.sas_address); - spin_unlock_irqrestore(&ioc->sas_node_lock, flags); _scsih_block_io_to_children_attached_to_ex(ioc, expander_sibling); } @@ -2403,10 +3107,10 @@ _scsih_block_io_to_children_attached_directly(struct MPT2SAS_ADAPTER *ioc, * @handle: device handle * Context: interrupt time. * - * This code is to initiate the device removal handshake protocal + * This code is to initiate the device removal handshake protocol * with controller firmware. This function will issue target reset * using high priority request queue. It will send a sas iounit - * controll request (MPI2_SAS_OP_REMOVE_DEVICE) from this completion. + * control request (MPI2_SAS_OP_REMOVE_DEVICE) from this completion. * * This is designed to send muliple task management request at the same * time to the fifo. If the fifo is full, we will append the request, @@ -2416,25 +3120,53 @@ static void _scsih_tm_tr_send(struct MPT2SAS_ADAPTER *ioc, u16 handle) { Mpi2SCSITaskManagementRequest_t *mpi_request; - struct MPT2SAS_TARGET *sas_target_priv_data; u16 smid; struct _sas_device *sas_device; + struct MPT2SAS_TARGET *sas_target_priv_data = NULL; + u64 sas_address = 0; unsigned long flags; struct _tr_list *delayed_tr; + u32 ioc_state; - if (ioc->shost_recovery) { - printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n", - __func__, ioc->name); + if (ioc->remove_host) { + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: host has been " + "removed: handle(0x%04x)\n", __func__, ioc->name, handle)); + return; + } else if (ioc->pci_error_recovery) { + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: host in pci " + "error recovery: handle(0x%04x)\n", __func__, ioc->name, + handle)); + return; + } + ioc_state = mpt2sas_base_get_iocstate(ioc, 1); + if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: host is not " + "operational: handle(0x%04x)\n", __func__, ioc->name, + handle)); return; } + /* if PD, then return */ + if (test_bit(handle, ioc->pd_handles)) + return; + spin_lock_irqsave(&ioc->sas_device_lock, flags); sas_device = _scsih_sas_device_find_by_handle(ioc, handle); + if (sas_device && sas_device->starget && + sas_device->starget->hostdata) { + sas_target_priv_data = sas_device->starget->hostdata; + sas_target_priv_data->deleted = 1; + sas_address = sas_device->sas_address; + } spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - /* skip is hidden raid component */ - if (sas_device && sas_device->hidden_raid_component) - return; + if (sas_target_priv_data) { + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "setting delete flag: " + "handle(0x%04x), sas_addr(0x%016llx)\n", ioc->name, handle, + (unsigned long long)sas_address)); + _scsih_ublock_io_device(ioc, sas_address); + sas_target_priv_data->handle = MPT2SAS_INVALID_DEVICE_HANDLE; + } smid = mpt2sas_base_get_smid_hpr(ioc, ioc->tm_tr_cb_idx); if (!smid) { @@ -2443,36 +3175,16 @@ _scsih_tm_tr_send(struct MPT2SAS_ADAPTER *ioc, u16 handle) return; INIT_LIST_HEAD(&delayed_tr->list); delayed_tr->handle = handle; - delayed_tr->state = MPT2SAS_REQ_SAS_CNTRL; - list_add_tail(&delayed_tr->list, - &ioc->delayed_tr_list); - if (sas_device && sas_device->starget) { - dewtprintk(ioc, starget_printk(KERN_INFO, - sas_device->starget, "DELAYED:tr:handle(0x%04x), " - "(open)\n", handle)); - } else { - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT - "DELAYED:tr:handle(0x%04x), (open)\n", - ioc->name, handle)); - } - return; - } - - if (sas_device) { - sas_device->state |= MPTSAS_STATE_TR_SEND; - sas_device->state |= MPT2SAS_REQ_SAS_CNTRL; - if (sas_device->starget && sas_device->starget->hostdata) { - sas_target_priv_data = sas_device->starget->hostdata; - sas_target_priv_data->tm_busy = 1; - dewtprintk(ioc, starget_printk(KERN_INFO, - sas_device->starget, "tr:handle(0x%04x), (open)\n", - handle)); - } - } else { + list_add_tail(&delayed_tr->list, &ioc->delayed_tr_list); dewtprintk(ioc, printk(MPT2SAS_INFO_FMT - "tr:handle(0x%04x), (open)\n", ioc->name, handle)); + "DELAYED:tr:handle(0x%04x), (open)\n", + ioc->name, handle)); + return; } + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "tr_send:handle(0x%04x), " + "(open), smid(%d), cb(%d)\n", ioc->name, handle, smid, + ioc->tm_tr_cb_idx)); mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); memset(mpi_request, 0, sizeof(Mpi2SCSITaskManagementRequest_t)); mpi_request->Function = MPI2_FUNCTION_SCSI_TASK_MGMT; @@ -2491,9 +3203,9 @@ _scsih_tm_tr_send(struct MPT2SAS_ADAPTER *ioc, u16 handle) * @reply: reply message frame(lower 32bit addr) * Context: interrupt time. * - * This is the sas iounit controll completion routine. + * This is the sas iounit control completion routine. * This code is part of the code to initiate the device removal - * handshake protocal with controller firmware. + * handshake protocol with controller firmware. * * Return 1 meaning mf should be freed from _base_interrupt * 0 means the mf is freed from this function. @@ -2502,36 +3214,119 @@ static u8 _scsih_sas_control_complete(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) { - unsigned long flags; - u16 handle; - struct _sas_device *sas_device; Mpi2SasIoUnitControlReply_t *mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); + if (likely(mpi_reply)) { + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT + "sc_complete:handle(0x%04x), (open) " + "smid(%d), ioc_status(0x%04x), loginfo(0x%08x)\n", + ioc->name, le16_to_cpu(mpi_reply->DevHandle), smid, + le16_to_cpu(mpi_reply->IOCStatus), + le32_to_cpu(mpi_reply->IOCLogInfo))); + } else { + printk(MPT2SAS_ERR_FMT "mpi_reply not valid at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + } + return 1; +} - handle = le16_to_cpu(mpi_reply->DevHandle); +/** + * _scsih_tm_tr_volume_send - send target reset request for volumes + * @ioc: per adapter object + * @handle: device handle + * Context: interrupt time. + * + * This is designed to send muliple task management request at the same + * time to the fifo. If the fifo is full, we will append the request, + * and process it in a future completion. + */ +static void +_scsih_tm_tr_volume_send(struct MPT2SAS_ADAPTER *ioc, u16 handle) +{ + Mpi2SCSITaskManagementRequest_t *mpi_request; + u16 smid; + struct _tr_list *delayed_tr; - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = _scsih_sas_device_find_by_handle(ioc, handle); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + if (ioc->shost_recovery || ioc->remove_host || + ioc->pci_error_recovery) { + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: host reset in " + "progress!\n", __func__, ioc->name)); + return; + } - if (sas_device) { - sas_device->state |= MPTSAS_STATE_CNTRL_COMPLETE; - if (sas_device->starget) - dewtprintk(ioc, starget_printk(KERN_INFO, - sas_device->starget, - "sc_complete:handle(0x%04x), " - "ioc_status(0x%04x), loginfo(0x%08x)\n", - handle, le16_to_cpu(mpi_reply->IOCStatus), - le32_to_cpu(mpi_reply->IOCLogInfo))); - } else { + smid = mpt2sas_base_get_smid_hpr(ioc, ioc->tm_tr_volume_cb_idx); + if (!smid) { + delayed_tr = kzalloc(sizeof(*delayed_tr), GFP_ATOMIC); + if (!delayed_tr) + return; + INIT_LIST_HEAD(&delayed_tr->list); + delayed_tr->handle = handle; + list_add_tail(&delayed_tr->list, &ioc->delayed_tr_volume_list); dewtprintk(ioc, printk(MPT2SAS_INFO_FMT - "sc_complete:handle(0x%04x), " - "ioc_status(0x%04x), loginfo(0x%08x)\n", - ioc->name, handle, le16_to_cpu(mpi_reply->IOCStatus), - le32_to_cpu(mpi_reply->IOCLogInfo))); + "DELAYED:tr:handle(0x%04x), (open)\n", + ioc->name, handle)); + return; } - return 1; + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "tr_send:handle(0x%04x), " + "(open), smid(%d), cb(%d)\n", ioc->name, handle, smid, + ioc->tm_tr_volume_cb_idx)); + mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); + memset(mpi_request, 0, sizeof(Mpi2SCSITaskManagementRequest_t)); + mpi_request->Function = MPI2_FUNCTION_SCSI_TASK_MGMT; + mpi_request->DevHandle = cpu_to_le16(handle); + mpi_request->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET; + mpt2sas_base_put_smid_hi_priority(ioc, smid); +} + +/** + * _scsih_tm_volume_tr_complete - target reset completion + * @ioc: per adapter object + * @smid: system request message index + * @msix_index: MSIX table index supplied by the OS + * @reply: reply message frame(lower 32bit addr) + * Context: interrupt time. + * + * Return 1 meaning mf should be freed from _base_interrupt + * 0 means the mf is freed from this function. + */ +static u8 +_scsih_tm_volume_tr_complete(struct MPT2SAS_ADAPTER *ioc, u16 smid, + u8 msix_index, u32 reply) +{ + u16 handle; + Mpi2SCSITaskManagementRequest_t *mpi_request_tm; + Mpi2SCSITaskManagementReply_t *mpi_reply = + mpt2sas_base_get_reply_virt_addr(ioc, reply); + + if (ioc->shost_recovery || ioc->remove_host || + ioc->pci_error_recovery) { + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: host reset in " + "progress!\n", __func__, ioc->name)); + return 1; + } + if (unlikely(!mpi_reply)) { + printk(MPT2SAS_ERR_FMT "mpi_reply not valid at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return 1; + } + mpi_request_tm = mpt2sas_base_get_msg_frame(ioc, smid); + handle = le16_to_cpu(mpi_request_tm->DevHandle); + if (handle != le16_to_cpu(mpi_reply->DevHandle)) { + dewtprintk(ioc, printk("spurious interrupt: " + "handle(0x%04x:0x%04x), smid(%d)!!!\n", handle, + le16_to_cpu(mpi_reply->DevHandle), smid)); + return 0; + } + + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT + "tr_complete:handle(0x%04x), (open) smid(%d), ioc_status(0x%04x), " + "loginfo(0x%08x), completed(%d)\n", ioc->name, + handle, smid, le16_to_cpu(mpi_reply->IOCStatus), + le32_to_cpu(mpi_reply->IOCLogInfo), + le32_to_cpu(mpi_reply->TerminationCount))); + + return _scsih_check_for_pending_tm(ioc, smid); } /** @@ -2544,8 +3339,8 @@ _scsih_sas_control_complete(struct MPT2SAS_ADAPTER *ioc, u16 smid, * * This is the target reset completion routine. * This code is part of the code to initiate the device removal - * handshake protocal with controller firmware. - * It will send a sas iounit controll request (MPI2_SAS_OP_REMOVE_DEVICE) + * handshake protocol with controller firmware. + * It will send a sas iounit control request (MPI2_SAS_OP_REMOVE_DEVICE) * * Return 1 meaning mf should be freed from _base_interrupt * 0 means the mf is freed from this function. @@ -2554,87 +3349,107 @@ static u8 _scsih_tm_tr_complete(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) { - unsigned long flags; u16 handle; - struct _sas_device *sas_device; + Mpi2SCSITaskManagementRequest_t *mpi_request_tm; Mpi2SCSITaskManagementReply_t *mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); Mpi2SasIoUnitControlRequest_t *mpi_request; u16 smid_sas_ctrl; - struct MPT2SAS_TARGET *sas_target_priv_data; - struct _tr_list *delayed_tr; - u8 rc; - - handle = le16_to_cpu(mpi_reply->DevHandle); - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = _scsih_sas_device_find_by_handle(ioc, handle); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + u32 ioc_state; - if (sas_device) { - sas_device->state |= MPTSAS_STATE_TR_COMPLETE; - if (sas_device->starget) { - dewtprintk(ioc, starget_printk(KERN_INFO, - sas_device->starget, "tr_complete:handle(0x%04x), " - "(%s) ioc_status(0x%04x), loginfo(0x%08x), " - "completed(%d)\n", sas_device->handle, - (sas_device->state & MPT2SAS_REQ_SAS_CNTRL) ? - "open" : "active", - le16_to_cpu(mpi_reply->IOCStatus), - le32_to_cpu(mpi_reply->IOCLogInfo), - le32_to_cpu(mpi_reply->TerminationCount))); - if (sas_device->starget->hostdata) { - sas_target_priv_data = - sas_device->starget->hostdata; - sas_target_priv_data->tm_busy = 0; - } - } - } else { - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT - "tr_complete:handle(0x%04x), (open) ioc_status(0x%04x), " - "loginfo(0x%08x), completed(%d)\n", ioc->name, - handle, le16_to_cpu(mpi_reply->IOCStatus), - le32_to_cpu(mpi_reply->IOCLogInfo), - le32_to_cpu(mpi_reply->TerminationCount))); + if (ioc->remove_host) { + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: host has been " + "removed\n", __func__, ioc->name)); + return 1; + } else if (ioc->pci_error_recovery) { + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: host in pci " + "error recovery\n", __func__, ioc->name)); + return 1; } - - if (!list_empty(&ioc->delayed_tr_list)) { - delayed_tr = list_entry(ioc->delayed_tr_list.next, - struct _tr_list, list); - mpt2sas_base_free_smid(ioc, smid); - if (delayed_tr->state & MPT2SAS_REQ_SAS_CNTRL) - _scsih_tm_tr_send(ioc, delayed_tr->handle); - list_del(&delayed_tr->list); - kfree(delayed_tr); - rc = 0; /* tells base_interrupt not to free mf */ - } else - rc = 1; - - if (sas_device && !(sas_device->state & MPT2SAS_REQ_SAS_CNTRL)) - return rc; - - if (ioc->shost_recovery) { - printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n", - __func__, ioc->name); - return rc; + ioc_state = mpt2sas_base_get_iocstate(ioc, 1); + if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: host is not " + "operational\n", __func__, ioc->name)); + return 1; + } + if (unlikely(!mpi_reply)) { + printk(MPT2SAS_ERR_FMT "mpi_reply not valid at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return 1; + } + mpi_request_tm = mpt2sas_base_get_msg_frame(ioc, smid); + handle = le16_to_cpu(mpi_request_tm->DevHandle); + if (handle != le16_to_cpu(mpi_reply->DevHandle)) { + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "spurious interrupt: " + "handle(0x%04x:0x%04x), smid(%d)!!!\n", ioc->name, handle, + le16_to_cpu(mpi_reply->DevHandle), smid)); + return 0; } + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT + "tr_complete:handle(0x%04x), (open) smid(%d), ioc_status(0x%04x), " + "loginfo(0x%08x), completed(%d)\n", ioc->name, + handle, smid, le16_to_cpu(mpi_reply->IOCStatus), + le32_to_cpu(mpi_reply->IOCLogInfo), + le32_to_cpu(mpi_reply->TerminationCount))); + smid_sas_ctrl = mpt2sas_base_get_smid(ioc, ioc->tm_sas_control_cb_idx); if (!smid_sas_ctrl) { printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", ioc->name, __func__); - return rc; + return 1; } - if (sas_device) - sas_device->state |= MPTSAS_STATE_CNTRL_SEND; - + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "sc_send:handle(0x%04x), " + "(open), smid(%d), cb(%d)\n", ioc->name, handle, smid_sas_ctrl, + ioc->tm_sas_control_cb_idx)); mpi_request = mpt2sas_base_get_msg_frame(ioc, smid_sas_ctrl); memset(mpi_request, 0, sizeof(Mpi2SasIoUnitControlRequest_t)); mpi_request->Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL; mpi_request->Operation = MPI2_SAS_OP_REMOVE_DEVICE; - mpi_request->DevHandle = mpi_reply->DevHandle; + mpi_request->DevHandle = mpi_request_tm->DevHandle; mpt2sas_base_put_smid_default(ioc, smid_sas_ctrl); - return rc; + + return _scsih_check_for_pending_tm(ioc, smid); +} + +/** + * _scsih_check_for_pending_tm - check for pending task management + * @ioc: per adapter object + * @smid: system request message index + * + * This will check delayed target reset list, and feed the + * next reqeust. + * + * Return 1 meaning mf should be freed from _base_interrupt + * 0 means the mf is freed from this function. + */ +static u8 +_scsih_check_for_pending_tm(struct MPT2SAS_ADAPTER *ioc, u16 smid) +{ + struct _tr_list *delayed_tr; + + if (!list_empty(&ioc->delayed_tr_volume_list)) { + delayed_tr = list_entry(ioc->delayed_tr_volume_list.next, + struct _tr_list, list); + mpt2sas_base_free_smid(ioc, smid); + _scsih_tm_tr_volume_send(ioc, delayed_tr->handle); + list_del(&delayed_tr->list); + kfree(delayed_tr); + return 0; + } + + if (!list_empty(&ioc->delayed_tr_list)) { + delayed_tr = list_entry(ioc->delayed_tr_list.next, + struct _tr_list, list); + mpt2sas_base_free_smid(ioc, smid); + _scsih_tm_tr_send(ioc, delayed_tr->handle); + list_del(&delayed_tr->list); + kfree(delayed_tr); + return 0; + } + + return 1; } /** @@ -2644,7 +3459,7 @@ _scsih_tm_tr_complete(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, * * This routine added to better handle cable breaker. * - * This handles the case where driver recieves multiple expander + * This handles the case where driver receives multiple expander * add and delete events in a single shot. When there is a delete event * the routine will void any pending add events waiting in the event queue. * @@ -2663,9 +3478,6 @@ _scsih_check_topo_delete_events(struct MPT2SAS_ADAPTER *ioc, u16 handle; for (i = 0 ; i < event_data->NumEntries; i++) { - if (event_data->PHY[i].PhyStatus & - MPI2_EVENT_SAS_TOPO_PHYSTATUS_VACANT) - continue; handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle); if (!handle) continue; @@ -2680,14 +3492,20 @@ _scsih_check_topo_delete_events(struct MPT2SAS_ADAPTER *ioc, _scsih_block_io_to_children_attached_directly(ioc, event_data); return; } - - if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING - || event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING) { + if (event_data->ExpStatus == + MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING) { + /* put expander attached devices into blocking state */ spin_lock_irqsave(&ioc->sas_node_lock, flags); sas_expander = mpt2sas_scsih_expander_find_by_handle(ioc, expander_handle); - spin_unlock_irqrestore(&ioc->sas_node_lock, flags); _scsih_block_io_to_children_attached_to_ex(ioc, sas_expander); + spin_unlock_irqrestore(&ioc->sas_node_lock, flags); + do { + handle = find_first_bit(ioc->blocking_handles, + ioc->facts.MaxDevHandle); + if (handle < ioc->facts.MaxDevHandle) + _scsih_block_io_device(ioc, handle); + } while (test_and_clear_bit(handle, ioc->blocking_handles)); } else if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_RESPONDING) _scsih_block_io_to_children_attached_directly(ioc, event_data); @@ -2707,7 +3525,7 @@ _scsih_check_topo_delete_events(struct MPT2SAS_ADAPTER *ioc, MPI2_EVENT_SAS_TOPO_ES_RESPONDING) { if (le16_to_cpu(local_event_data->ExpanderDevHandle) == expander_handle) { - dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "setting ignoring flag\n", ioc->name)); fw_event->ignore = 1; } @@ -2717,6 +3535,168 @@ _scsih_check_topo_delete_events(struct MPT2SAS_ADAPTER *ioc, } /** + * _scsih_set_volume_delete_flag - setting volume delete flag + * @ioc: per adapter object + * @handle: device handle + * + * This + * Return nothing. + */ +static void +_scsih_set_volume_delete_flag(struct MPT2SAS_ADAPTER *ioc, u16 handle) +{ + struct _raid_device *raid_device; + struct MPT2SAS_TARGET *sas_target_priv_data; + unsigned long flags; + + spin_lock_irqsave(&ioc->raid_device_lock, flags); + raid_device = _scsih_raid_device_find_by_handle(ioc, handle); + if (raid_device && raid_device->starget && + raid_device->starget->hostdata) { + sas_target_priv_data = + raid_device->starget->hostdata; + sas_target_priv_data->deleted = 1; + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT + "setting delete flag: handle(0x%04x), " + "wwid(0x%016llx)\n", ioc->name, handle, + (unsigned long long) raid_device->wwid)); + } + spin_unlock_irqrestore(&ioc->raid_device_lock, flags); +} + +/** + * _scsih_set_volume_handle_for_tr - set handle for target reset to volume + * @handle: input handle + * @a: handle for volume a + * @b: handle for volume b + * + * IR firmware only supports two raid volumes. The purpose of this + * routine is to set the volume handle in either a or b. When the given + * input handle is non-zero, or when a and b have not been set before. + */ +static void +_scsih_set_volume_handle_for_tr(u16 handle, u16 *a, u16 *b) +{ + if (!handle || handle == *a || handle == *b) + return; + if (!*a) + *a = handle; + else if (!*b) + *b = handle; +} + +/** + * _scsih_check_ir_config_unhide_events - check for UNHIDE events + * @ioc: per adapter object + * @event_data: the event data payload + * Context: interrupt time. + * + * This routine will send target reset to volume, followed by target + * resets to the PDs. This is called when a PD has been removed, or + * volume has been deleted or removed. When the target reset is sent + * to volume, the PD target resets need to be queued to start upon + * completion of the volume target reset. + * + * Return nothing. + */ +static void +_scsih_check_ir_config_unhide_events(struct MPT2SAS_ADAPTER *ioc, + Mpi2EventDataIrConfigChangeList_t *event_data) +{ + Mpi2EventIrConfigElement_t *element; + int i; + u16 handle, volume_handle, a, b; + struct _tr_list *delayed_tr; + + a = 0; + b = 0; + + if (ioc->is_warpdrive) + return; + + /* Volume Resets for Deleted or Removed */ + element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0]; + for (i = 0; i < event_data->NumElements; i++, element++) { + if (element->ReasonCode == + MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED || + element->ReasonCode == + MPI2_EVENT_IR_CHANGE_RC_REMOVED) { + volume_handle = le16_to_cpu(element->VolDevHandle); + _scsih_set_volume_delete_flag(ioc, volume_handle); + _scsih_set_volume_handle_for_tr(volume_handle, &a, &b); + } + } + + /* Volume Resets for UNHIDE events */ + element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0]; + for (i = 0; i < event_data->NumElements; i++, element++) { + if (le32_to_cpu(event_data->Flags) & + MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG) + continue; + if (element->ReasonCode == MPI2_EVENT_IR_CHANGE_RC_UNHIDE) { + volume_handle = le16_to_cpu(element->VolDevHandle); + _scsih_set_volume_handle_for_tr(volume_handle, &a, &b); + } + } + + if (a) + _scsih_tm_tr_volume_send(ioc, a); + if (b) + _scsih_tm_tr_volume_send(ioc, b); + + /* PD target resets */ + element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0]; + for (i = 0; i < event_data->NumElements; i++, element++) { + if (element->ReasonCode != MPI2_EVENT_IR_CHANGE_RC_UNHIDE) + continue; + handle = le16_to_cpu(element->PhysDiskDevHandle); + volume_handle = le16_to_cpu(element->VolDevHandle); + clear_bit(handle, ioc->pd_handles); + if (!volume_handle) + _scsih_tm_tr_send(ioc, handle); + else if (volume_handle == a || volume_handle == b) { + delayed_tr = kzalloc(sizeof(*delayed_tr), GFP_ATOMIC); + BUG_ON(!delayed_tr); + INIT_LIST_HEAD(&delayed_tr->list); + delayed_tr->handle = handle; + list_add_tail(&delayed_tr->list, &ioc->delayed_tr_list); + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT + "DELAYED:tr:handle(0x%04x), (open)\n", ioc->name, + handle)); + } else + _scsih_tm_tr_send(ioc, handle); + } +} + + +/** + * _scsih_check_volume_delete_events - set delete flag for volumes + * @ioc: per adapter object + * @event_data: the event data payload + * Context: interrupt time. + * + * This will handle the case when the cable connected to entire volume is + * pulled. We will take care of setting the deleted flag so normal IO will + * not be sent. + * + * Return nothing. + */ +static void +_scsih_check_volume_delete_events(struct MPT2SAS_ADAPTER *ioc, + Mpi2EventDataIrVolume_t *event_data) +{ + u32 state; + + if (event_data->ReasonCode != MPI2_EVENT_IR_VOLUME_RC_STATE_CHANGED) + return; + state = le32_to_cpu(event_data->NewValue); + if (state == MPI2_RAID_VOL_STATE_MISSING || state == + MPI2_RAID_VOL_STATE_FAILED) + _scsih_set_volume_delete_flag(ioc, + le16_to_cpu(event_data->VolDevHandle)); +} + +/** * _scsih_flush_running_cmds - completing outstanding commands. * @ioc: per adapter object * @@ -2733,13 +3713,16 @@ _scsih_flush_running_cmds(struct MPT2SAS_ADAPTER *ioc) u16 count = 0; for (smid = 1; smid <= ioc->scsiio_depth; smid++) { - scmd = _scsih_scsi_lookup_get(ioc, smid); + scmd = _scsih_scsi_lookup_get_clear(ioc, smid); if (!scmd) continue; count++; mpt2sas_base_free_smid(ioc, smid); scsi_dma_unmap(scmd); - scmd->result = DID_RESET << 16; + if (ioc->pci_error_recovery) + scmd->result = DID_NO_CONNECT << 16; + else + scmd->result = DID_RESET << 16; scmd->scsi_done(scmd); } dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "completing %d cmds\n", @@ -2762,9 +3745,7 @@ _scsih_setup_eedp(struct scsi_cmnd *scmd, Mpi2SCSIIORequest_t *mpi_request) unsigned char prot_op = scsi_get_prot_op(scmd); unsigned char prot_type = scsi_get_prot_type(scmd); - if (prot_type == SCSI_PROT_DIF_TYPE0 || - prot_type == SCSI_PROT_DIF_TYPE2 || - prot_op == SCSI_PROT_NORMAL) + if (prot_type == SCSI_PROT_DIF_TYPE0 || prot_op == SCSI_PROT_NORMAL) return; if (prot_op == SCSI_PROT_READ_STRIP) @@ -2776,6 +3757,7 @@ _scsih_setup_eedp(struct scsi_cmnd *scmd, Mpi2SCSIIORequest_t *mpi_request) switch (prot_type) { case SCSI_PROT_DIF_TYPE1: + case SCSI_PROT_DIF_TYPE2: /* * enable ref/guard checking @@ -2786,7 +3768,6 @@ _scsih_setup_eedp(struct scsi_cmnd *scmd, Mpi2SCSIIORequest_t *mpi_request) MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD; mpi_request->CDB.EEDP32.PrimaryReferenceTag = cpu_to_be32(scsi_get_lba(scmd)); - break; case SCSI_PROT_DIF_TYPE3: @@ -2812,8 +3793,6 @@ static void _scsih_eedp_error_handling(struct scsi_cmnd *scmd, u16 ioc_status) { u8 ascq; - u8 sk; - u8 host_byte; switch (ioc_status) { case MPI2_IOCSTATUS_EEDP_GUARD_ERROR: @@ -2830,20 +3809,136 @@ _scsih_eedp_error_handling(struct scsi_cmnd *scmd, u16 ioc_status) break; } - if (scmd->sc_data_direction == DMA_TO_DEVICE) { - sk = ILLEGAL_REQUEST; - host_byte = DID_ABORT; - } else { - sk = ABORTED_COMMAND; - host_byte = DID_OK; - } - - scsi_build_sense_buffer(0, scmd->sense_buffer, sk, 0x10, ascq); - scmd->result = DRIVER_SENSE << 24 | (host_byte << 16) | + scsi_build_sense_buffer(0, scmd->sense_buffer, ILLEGAL_REQUEST, 0x10, ascq); + scmd->result = DRIVER_SENSE << 24 | (DID_ABORT << 16) | SAM_STAT_CHECK_CONDITION; } /** + * _scsih_scsi_direct_io_get - returns direct io flag + * @ioc: per adapter object + * @smid: system request message index + * + * Returns the smid stored scmd pointer. + */ +static inline u8 +_scsih_scsi_direct_io_get(struct MPT2SAS_ADAPTER *ioc, u16 smid) +{ + return ioc->scsi_lookup[smid - 1].direct_io; +} + +/** + * _scsih_scsi_direct_io_set - sets direct io flag + * @ioc: per adapter object + * @smid: system request message index + * @direct_io: Zero or non-zero value to set in the direct_io flag + * + * Returns Nothing. + */ +static inline void +_scsih_scsi_direct_io_set(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 direct_io) +{ + ioc->scsi_lookup[smid - 1].direct_io = direct_io; +} + + +/** + * _scsih_setup_direct_io - setup MPI request for WARPDRIVE Direct I/O + * @ioc: per adapter object + * @scmd: pointer to scsi command object + * @raid_device: pointer to raid device data structure + * @mpi_request: pointer to the SCSI_IO reqest message frame + * @smid: system request message index + * + * Returns nothing + */ +static void +_scsih_setup_direct_io(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd, + struct _raid_device *raid_device, Mpi2SCSIIORequest_t *mpi_request, + u16 smid) +{ + u32 v_lba, p_lba, stripe_off, stripe_unit, column, io_size; + u32 stripe_sz, stripe_exp; + u8 num_pds, *cdb_ptr, i; + u8 cdb0 = scmd->cmnd[0]; + u64 v_llba; + + /* + * Try Direct I/O to RAID memeber disks + */ + if (cdb0 == READ_16 || cdb0 == READ_10 || + cdb0 == WRITE_16 || cdb0 == WRITE_10) { + cdb_ptr = mpi_request->CDB.CDB32; + + if ((cdb0 < READ_16) || !(cdb_ptr[2] | cdb_ptr[3] | cdb_ptr[4] + | cdb_ptr[5])) { + io_size = scsi_bufflen(scmd) >> + raid_device->block_exponent; + i = (cdb0 < READ_16) ? 2 : 6; + /* get virtual lba */ + v_lba = be32_to_cpu(*(__be32 *)(&cdb_ptr[i])); + + if (((u64)v_lba + (u64)io_size - 1) <= + (u32)raid_device->max_lba) { + stripe_sz = raid_device->stripe_sz; + stripe_exp = raid_device->stripe_exponent; + stripe_off = v_lba & (stripe_sz - 1); + + /* Check whether IO falls within a stripe */ + if ((stripe_off + io_size) <= stripe_sz) { + num_pds = raid_device->num_pds; + p_lba = v_lba >> stripe_exp; + stripe_unit = p_lba / num_pds; + column = p_lba % num_pds; + p_lba = (stripe_unit << stripe_exp) + + stripe_off; + mpi_request->DevHandle = + cpu_to_le16(raid_device-> + pd_handle[column]); + (*(__be32 *)(&cdb_ptr[i])) = + cpu_to_be32(p_lba); + /* + * WD: To indicate this I/O is directI/O + */ + _scsih_scsi_direct_io_set(ioc, smid, 1); + } + } + } else { + io_size = scsi_bufflen(scmd) >> + raid_device->block_exponent; + /* get virtual lba */ + v_llba = be64_to_cpu(*(__be64 *)(&cdb_ptr[2])); + + if ((v_llba + (u64)io_size - 1) <= + raid_device->max_lba) { + stripe_sz = raid_device->stripe_sz; + stripe_exp = raid_device->stripe_exponent; + stripe_off = (u32) (v_llba & (stripe_sz - 1)); + + /* Check whether IO falls within a stripe */ + if ((stripe_off + io_size) <= stripe_sz) { + num_pds = raid_device->num_pds; + p_lba = (u32)(v_llba >> stripe_exp); + stripe_unit = p_lba / num_pds; + column = p_lba % num_pds; + p_lba = (stripe_unit << stripe_exp) + + stripe_off; + mpi_request->DevHandle = + cpu_to_le16(raid_device-> + pd_handle[column]); + (*(__be64 *)(&cdb_ptr[2])) = + cpu_to_be64((u64)p_lba); + /* + * WD: To indicate this I/O is directI/O + */ + _scsih_scsi_direct_io_set(ioc, smid, 1); + } + } + } + } +} + +/** * _scsih_qcmd - main scsi request entry point * @scmd: pointer to scsi command object * @done: function pointer to be invoked on completion @@ -2855,36 +3950,49 @@ _scsih_eedp_error_handling(struct scsi_cmnd *scmd, u16 ioc_status) * SCSI_MLQUEUE_HOST_BUSY if the entire host queue is full */ static int -_scsih_qcmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *)) +_scsih_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *scmd) { - struct MPT2SAS_ADAPTER *ioc = shost_priv(scmd->device->host); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); struct MPT2SAS_DEVICE *sas_device_priv_data; struct MPT2SAS_TARGET *sas_target_priv_data; + struct _raid_device *raid_device; Mpi2SCSIIORequest_t *mpi_request; u32 mpi_control; u16 smid; - scmd->scsi_done = done; sas_device_priv_data = scmd->device->hostdata; - if (!sas_device_priv_data) { + if (!sas_device_priv_data || !sas_device_priv_data->sas_target) { + scmd->result = DID_NO_CONNECT << 16; + scmd->scsi_done(scmd); + return 0; + } + + if (ioc->pci_error_recovery || ioc->remove_host) { scmd->result = DID_NO_CONNECT << 16; scmd->scsi_done(scmd); return 0; } sas_target_priv_data = sas_device_priv_data->sas_target; - if (!sas_target_priv_data || sas_target_priv_data->handle == - MPT2SAS_INVALID_DEVICE_HANDLE || sas_target_priv_data->deleted) { + /* invalid device handle */ + if (sas_target_priv_data->handle == MPT2SAS_INVALID_DEVICE_HANDLE) { scmd->result = DID_NO_CONNECT << 16; scmd->scsi_done(scmd); return 0; } - /* see if we are busy with task managment stuff */ - if (sas_device_priv_data->block || sas_target_priv_data->tm_busy) - return SCSI_MLQUEUE_DEVICE_BUSY; - else if (ioc->shost_recovery || ioc->ioc_link_reset_in_progress) + /* host recovery or link resets sent via IOCTLs */ + if (ioc->shost_recovery || ioc->ioc_link_reset_in_progress) return SCSI_MLQUEUE_HOST_BUSY; + /* device busy with task management */ + else if (sas_device_priv_data->block || sas_target_priv_data->tm_busy) + return SCSI_MLQUEUE_DEVICE_BUSY; + /* device has been deleted */ + else if (sas_target_priv_data->deleted) { + scmd->result = DID_NO_CONNECT << 16; + scmd->scsi_done(scmd); + return 0; + } if (scmd->sc_data_direction == DMA_FROM_DEVICE) mpi_control = MPI2_SCSIIO_CONTROL_READ; @@ -2901,15 +4009,14 @@ _scsih_qcmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *)) else mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ; } else -/* MPI Revision I (UNIT = 0xA) - removed MPI2_SCSIIO_CONTROL_UNTAGGED */ -/* mpi_control |= MPI2_SCSIIO_CONTROL_UNTAGGED; - */ - mpi_control |= (0x500); - + mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ; } else mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ; - - if ((sas_device_priv_data->flags & MPT_DEVICE_TLR_ON)) + /* Make sure Device is not raid volume. + * We do not expose raid functionality to upper layer for warpdrive. + */ + if (!ioc->is_warpdrive && !_scsih_is_raid(&scmd->device->sdev_gendev) && + sas_is_tlr_enabled(scmd->device) && scmd->cmd_len != 32) mpi_control |= MPI2_SCSIIO_CONTROL_TLR_ON; smid = mpt2sas_base_get_smid_scsiio(ioc, ioc->scsi_io_cb_idx, scmd); @@ -2921,12 +4028,14 @@ _scsih_qcmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *)) mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); memset(mpi_request, 0, sizeof(Mpi2SCSIIORequest_t)); _scsih_setup_eedp(scmd, mpi_request); + if (scmd->cmd_len == 32) + mpi_control |= 4 << MPI2_SCSIIO_CONTROL_ADDCDBLEN_SHIFT; mpi_request->Function = MPI2_FUNCTION_SCSI_IO_REQUEST; if (sas_device_priv_data->sas_target->flags & MPT_TARGET_FLAGS_RAID_COMPONENT) mpi_request->Function = MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH; else - mpi_request->Function = MPI2_FUNCTION_SCSI_IO_REQUEST; + mpi_request->Function = MPI2_FUNCTION_SCSI_IO_REQUEST; mpi_request->DevHandle = cpu_to_le16(sas_device_priv_data->sas_target->handle); mpi_request->DataLength = cpu_to_le32(scsi_bufflen(scmd)); @@ -2954,8 +4063,16 @@ _scsih_qcmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *)) } } - mpt2sas_base_put_smid_scsi_io(ioc, smid, - sas_device_priv_data->sas_target->handle); + raid_device = sas_target_priv_data->raid_device; + if (raid_device && raid_device->direct_io_enabled) + _scsih_setup_direct_io(ioc, scmd, raid_device, mpi_request, + smid); + + if (likely(mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST)) + mpt2sas_base_put_smid_scsi_io(ioc, smid, + le16_to_cpu(mpi_request->DevHandle)); + else + mpt2sas_base_put_smid_default(ioc, smid); return 0; out: @@ -2987,7 +4104,7 @@ _scsih_normalize_sense(char *sense_buffer, struct sense_info *data) #ifdef CONFIG_SCSI_MPT2SAS_LOGGING /** - * _scsih_scsi_ioc_info - translated non-successfull SCSI_IO request + * _scsih_scsi_ioc_info - translated non-successful SCSI_IO request * @ioc: per adapter object * @scmd: pointer to scsi command object * @mpi_reply: reply mf payload returned from firmware @@ -3012,6 +4129,19 @@ _scsih_scsi_ioc_info(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd, char *desc_scsi_status = NULL; char *desc_scsi_state = ioc->tmp_string; u32 log_info = le32_to_cpu(mpi_reply->IOCLogInfo); + struct _sas_device *sas_device = NULL; + unsigned long flags; + struct scsi_target *starget = scmd->device->sdev_target; + struct MPT2SAS_TARGET *priv_target = starget->hostdata; + char *device_str = NULL; + + if (!priv_target) + return; + + if (ioc->hide_ir_msg) + device_str = "WarpDrive"; + else + device_str = "volume"; if (log_info == 0x31170000) return; @@ -3127,10 +4257,29 @@ _scsih_scsi_ioc_info(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd, strcat(desc_scsi_state, "autosense valid "); scsi_print_command(scmd); - printk(MPT2SAS_WARN_FMT "\tdev handle(0x%04x), " - "ioc_status(%s)(0x%04x), smid(%d)\n", ioc->name, - le16_to_cpu(mpi_reply->DevHandle), desc_ioc_state, - ioc_status, smid); + + if (priv_target->flags & MPT_TARGET_FLAGS_VOLUME) { + printk(MPT2SAS_WARN_FMT "\t%s wwid(0x%016llx)\n", ioc->name, + device_str, (unsigned long long)priv_target->sas_address); + } else { + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, + priv_target->sas_address); + if (sas_device) { + printk(MPT2SAS_WARN_FMT "\tsas_address(0x%016llx), " + "phy(%d)\n", ioc->name, sas_device->sas_address, + sas_device->phy); + printk(MPT2SAS_WARN_FMT + "\tenclosure_logical_id(0x%016llx), slot(%d)\n", + ioc->name, sas_device->enclosure_logical_id, + sas_device->slot); + } + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + } + + printk(MPT2SAS_WARN_FMT "\thandle(0x%04x), ioc_status(%s)(0x%04x), " + "smid(%d)\n", ioc->name, le16_to_cpu(mpi_reply->DevHandle), + desc_ioc_state, ioc_status, smid); printk(MPT2SAS_WARN_FMT "\trequest_len(%d), underflow(%d), " "resid(%d)\n", ioc->name, scsi_bufflen(scmd), scmd->underflow, scsi_get_resid(scmd)); @@ -3145,8 +4294,8 @@ _scsih_scsi_ioc_info(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd, struct sense_info data; _scsih_normalize_sense(scmd->sense_buffer, &data); printk(MPT2SAS_WARN_FMT "\t[sense_key,asc,ascq]: " - "[0x%02x,0x%02x,0x%02x]\n", ioc->name, data.skey, - data.asc, data.ascq); + "[0x%02x,0x%02x,0x%02x], count(%d)\n", ioc->name, data.skey, + data.asc, data.ascq, le32_to_cpu(mpi_reply->SenseCount)); } if (scsi_state & MPI2_SCSI_STATE_RESPONSE_INFO_VALID) { @@ -3158,17 +4307,75 @@ _scsih_scsi_ioc_info(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd, #endif /** - * _scsih_smart_predicted_fault - illuminate Fault LED + * _scsih_turn_on_fault_led - illuminate Fault LED * @ioc: per adapter object * @handle: device handle + * Context: process * * Return nothing. */ static void -_scsih_smart_predicted_fault(struct MPT2SAS_ADAPTER *ioc, u16 handle) +_scsih_turn_on_fault_led(struct MPT2SAS_ADAPTER *ioc, u16 handle) { Mpi2SepReply_t mpi_reply; Mpi2SepRequest_t mpi_request; + + memset(&mpi_request, 0, sizeof(Mpi2SepRequest_t)); + mpi_request.Function = MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR; + mpi_request.Action = MPI2_SEP_REQ_ACTION_WRITE_STATUS; + mpi_request.SlotStatus = + cpu_to_le32(MPI2_SEP_REQ_SLOTSTATUS_PREDICTED_FAULT); + mpi_request.DevHandle = cpu_to_le16(handle); + mpi_request.Flags = MPI2_SEP_REQ_FLAGS_DEVHANDLE_ADDRESS; + if ((mpt2sas_base_scsi_enclosure_processor(ioc, &mpi_reply, + &mpi_request)) != 0) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->name, + __FILE__, __LINE__, __func__); + return; + } + + if (mpi_reply.IOCStatus || mpi_reply.IOCLogInfo) { + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "enclosure_processor: " + "ioc_status (0x%04x), loginfo(0x%08x)\n", ioc->name, + le16_to_cpu(mpi_reply.IOCStatus), + le32_to_cpu(mpi_reply.IOCLogInfo))); + return; + } +} + +/** + * _scsih_send_event_to_turn_on_fault_led - fire delayed event + * @ioc: per adapter object + * @handle: device handle + * Context: interrupt. + * + * Return nothing. + */ +static void +_scsih_send_event_to_turn_on_fault_led(struct MPT2SAS_ADAPTER *ioc, u16 handle) +{ + struct fw_event_work *fw_event; + + fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC); + if (!fw_event) + return; + fw_event->event = MPT2SAS_TURN_ON_FAULT_LED; + fw_event->device_handle = handle; + fw_event->ioc = ioc; + _scsih_fw_event_add(ioc, fw_event); +} + +/** + * _scsih_smart_predicted_fault - process smart errors + * @ioc: per adapter object + * @handle: device handle + * Context: interrupt. + * + * Return nothing. + */ +static void +_scsih_smart_predicted_fault(struct MPT2SAS_ADAPTER *ioc, u16 handle) +{ struct scsi_target *starget; struct MPT2SAS_TARGET *sas_target_priv_data; Mpi2EventNotificationReply_t *event_reply; @@ -3195,35 +4402,13 @@ _scsih_smart_predicted_fault(struct MPT2SAS_ADAPTER *ioc, u16 handle) starget_printk(KERN_WARNING, starget, "predicted fault\n"); spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - if (ioc->pdev->subsystem_vendor == PCI_VENDOR_ID_IBM) { - memset(&mpi_request, 0, sizeof(Mpi2SepRequest_t)); - mpi_request.Function = MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR; - mpi_request.Action = MPI2_SEP_REQ_ACTION_WRITE_STATUS; - mpi_request.SlotStatus = - MPI2_SEP_REQ_SLOTSTATUS_PREDICTED_FAULT; - mpi_request.DevHandle = cpu_to_le16(handle); - mpi_request.Flags = MPI2_SEP_REQ_FLAGS_DEVHANDLE_ADDRESS; - if ((mpt2sas_base_scsi_enclosure_processor(ioc, &mpi_reply, - &mpi_request)) != 0) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - return; - } - - if (mpi_reply.IOCStatus || mpi_reply.IOCLogInfo) { - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT - "enclosure_processor: ioc_status (0x%04x), " - "loginfo(0x%08x)\n", ioc->name, - le16_to_cpu(mpi_reply.IOCStatus), - le32_to_cpu(mpi_reply.IOCLogInfo))); - return; - } - } + if (ioc->pdev->subsystem_vendor == PCI_VENDOR_ID_IBM) + _scsih_send_event_to_turn_on_fault_led(ioc, handle); /* insert into event log */ sz = offsetof(Mpi2EventNotificationReply_t, EventData) + sizeof(Mpi2EventDataSasDeviceStatusChange_t); - event_reply = kzalloc(sz, GFP_KERNEL); + event_reply = kzalloc(sz, GFP_ATOMIC); if (!event_reply) { printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, __func__); @@ -3271,9 +4456,10 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) u32 log_info; struct MPT2SAS_DEVICE *sas_device_priv_data; u32 response_code = 0; + unsigned long flags; mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); - scmd = _scsih_scsi_lookup_get(ioc, smid); + scmd = _scsih_scsi_lookup_get_clear(ioc, smid); if (scmd == NULL) return 1; @@ -3290,6 +4476,26 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) scmd->result = DID_NO_CONNECT << 16; goto out; } + ioc_status = le16_to_cpu(mpi_reply->IOCStatus); + /* + * WARPDRIVE: If direct_io is set then it is directIO, + * the failed direct I/O should be redirected to volume + */ + if (_scsih_scsi_direct_io_get(ioc, smid) && + ((ioc_status & MPI2_IOCSTATUS_MASK) + != MPI2_IOCSTATUS_SCSI_TASK_TERMINATED)) { + spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); + ioc->scsi_lookup[smid - 1].scmd = scmd; + _scsih_scsi_direct_io_set(ioc, smid, 0); + spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); + memcpy(mpi_request->CDB.CDB32, scmd->cmnd, scmd->cmd_len); + mpi_request->DevHandle = + cpu_to_le16(sas_device_priv_data->sas_target->handle); + mpt2sas_base_put_smid_scsi_io(ioc, smid, + sas_device_priv_data->sas_target->handle); + return 0; + } + /* turning off TLR */ scsi_state = mpi_reply->SCSIState; @@ -3298,15 +4504,19 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) le32_to_cpu(mpi_reply->ResponseInfo) & 0xFF; if (!sas_device_priv_data->tlr_snoop_check) { sas_device_priv_data->tlr_snoop_check++; - if ((sas_device_priv_data->flags & MPT_DEVICE_TLR_ON) && - response_code == MPI2_SCSITASKMGMT_RSP_INVALID_FRAME) - sas_device_priv_data->flags &= - ~MPT_DEVICE_TLR_ON; + /* Make sure Device is not raid volume. + * We do not expose raid functionality to upper layer for warpdrive. + */ + if (!ioc->is_warpdrive && !_scsih_is_raid(&scmd->device->sdev_gendev) && + sas_is_tlr_enabled(scmd->device) && + response_code == MPI2_SCSITASKMGMT_RSP_INVALID_FRAME) { + sas_disable_tlr(scmd->device); + sdev_printk(KERN_INFO, scmd->device, "TLR disabled\n"); + } } xfer_cnt = le32_to_cpu(mpi_reply->TransferCount); scsi_set_resid(scmd, scsi_bufflen(scmd) - xfer_cnt); - ioc_status = le16_to_cpu(mpi_reply->IOCStatus); if (ioc_status & MPI2_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE) log_info = le32_to_cpu(mpi_reply->IOCLogInfo); else @@ -3350,6 +4560,8 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) scmd->result = DID_TRANSPORT_DISRUPTED << 16; goto out; } + scmd->result = DID_SOFT_ERROR << 16; + break; case MPI2_IOCSTATUS_SCSI_TASK_TERMINATED: case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED: scmd->result = DID_RESET << 16; @@ -3454,6 +4666,7 @@ _scsih_sas_host_refresh(struct MPT2SAS_ADAPTER *ioc) Mpi2ConfigReply_t mpi_reply; Mpi2SasIOUnitPage0_t *sas_iounit_pg0 = NULL; u16 attached_handle; + u8 link_rate; dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "updating handles for sas_host(0x%016llx)\n", @@ -3475,15 +4688,17 @@ _scsih_sas_host_refresh(struct MPT2SAS_ADAPTER *ioc) if (ioc_status != MPI2_IOCSTATUS_SUCCESS) goto out; for (i = 0; i < ioc->sas_hba.num_phys ; i++) { + link_rate = sas_iounit_pg0->PhyData[i].NegotiatedLinkRate >> 4; if (i == 0) ioc->sas_hba.handle = le16_to_cpu(sas_iounit_pg0-> PhyData[0].ControllerDevHandle); ioc->sas_hba.phy[i].handle = ioc->sas_hba.handle; attached_handle = le16_to_cpu(sas_iounit_pg0->PhyData[i]. AttachedDevHandle); + if (attached_handle && link_rate < MPI2_SAS_NEG_LINK_RATE_1_5) + link_rate = MPI2_SAS_NEG_LINK_RATE_1_5; mpt2sas_transport_update_links(ioc, ioc->sas_hba.sas_address, - attached_handle, i, sas_iounit_pg0->PhyData[i]. - NegotiatedLinkRate >> 4); + attached_handle, i, link_rate); } out: kfree(sas_iounit_pg0); @@ -3654,7 +4869,7 @@ _scsih_expander_add(struct MPT2SAS_ADAPTER *ioc, u16 handle) Mpi2SasEnclosurePage0_t enclosure_pg0; u32 ioc_status; u16 parent_handle; - __le64 sas_address, sas_address_parent = 0; + u64 sas_address, sas_address_parent = 0; int i; unsigned long flags; struct _sas_port *mpt2sas_port = NULL; @@ -3663,7 +4878,7 @@ _scsih_expander_add(struct MPT2SAS_ADAPTER *ioc, u16 handle) if (!handle) return -1; - if (ioc->shost_recovery) + if (ioc->shost_recovery || ioc->pci_error_recovery) return -1; if ((mpt2sas_config_get_expander_pg0(ioc, &mpi_reply, &expander_pg0, @@ -3827,14 +5042,14 @@ _scsih_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) } /** - * _scsih_expander_remove - removing expander object + * mpt2sas_expander_remove - removing expander object * @ioc: per adapter object * @sas_address: expander sas_address * * Return nothing. */ -static void -_scsih_expander_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address) +void +mpt2sas_expander_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address) { struct _sas_node *sas_expander; unsigned long flags; @@ -3845,8 +5060,143 @@ _scsih_expander_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address) spin_lock_irqsave(&ioc->sas_node_lock, flags); sas_expander = mpt2sas_scsih_expander_find_by_sas_address(ioc, sas_address); + if (sas_expander) + list_del(&sas_expander->list); spin_unlock_irqrestore(&ioc->sas_node_lock, flags); - _scsih_expander_node_remove(ioc, sas_expander); + if (sas_expander) + _scsih_expander_node_remove(ioc, sas_expander); +} + +/** + * _scsih_check_access_status - check access flags + * @ioc: per adapter object + * @sas_address: sas address + * @handle: sas device handle + * @access_flags: errors returned during discovery of the device + * + * Return 0 for success, else failure + */ +static u8 +_scsih_check_access_status(struct MPT2SAS_ADAPTER *ioc, u64 sas_address, + u16 handle, u8 access_status) +{ + u8 rc = 1; + char *desc = NULL; + + switch (access_status) { + case MPI2_SAS_DEVICE0_ASTATUS_NO_ERRORS: + case MPI2_SAS_DEVICE0_ASTATUS_SATA_NEEDS_INITIALIZATION: + rc = 0; + break; + case MPI2_SAS_DEVICE0_ASTATUS_SATA_CAPABILITY_FAILED: + desc = "sata capability failed"; + break; + case MPI2_SAS_DEVICE0_ASTATUS_SATA_AFFILIATION_CONFLICT: + desc = "sata affiliation conflict"; + break; + case MPI2_SAS_DEVICE0_ASTATUS_ROUTE_NOT_ADDRESSABLE: + desc = "route not addressable"; + break; + case MPI2_SAS_DEVICE0_ASTATUS_SMP_ERROR_NOT_ADDRESSABLE: + desc = "smp error not addressable"; + break; + case MPI2_SAS_DEVICE0_ASTATUS_DEVICE_BLOCKED: + desc = "device blocked"; + break; + case MPI2_SAS_DEVICE0_ASTATUS_SATA_INIT_FAILED: + case MPI2_SAS_DEVICE0_ASTATUS_SIF_UNKNOWN: + case MPI2_SAS_DEVICE0_ASTATUS_SIF_AFFILIATION_CONFLICT: + case MPI2_SAS_DEVICE0_ASTATUS_SIF_DIAG: + case MPI2_SAS_DEVICE0_ASTATUS_SIF_IDENTIFICATION: + case MPI2_SAS_DEVICE0_ASTATUS_SIF_CHECK_POWER: + case MPI2_SAS_DEVICE0_ASTATUS_SIF_PIO_SN: + case MPI2_SAS_DEVICE0_ASTATUS_SIF_MDMA_SN: + case MPI2_SAS_DEVICE0_ASTATUS_SIF_UDMA_SN: + case MPI2_SAS_DEVICE0_ASTATUS_SIF_ZONING_VIOLATION: + case MPI2_SAS_DEVICE0_ASTATUS_SIF_NOT_ADDRESSABLE: + case MPI2_SAS_DEVICE0_ASTATUS_SIF_MAX: + desc = "sata initialization failed"; + break; + default: + desc = "unknown"; + break; + } + + if (!rc) + return 0; + + printk(MPT2SAS_ERR_FMT "discovery errors(%s): sas_address(0x%016llx), " + "handle(0x%04x)\n", ioc->name, desc, + (unsigned long long)sas_address, handle); + return rc; +} + +static void +_scsih_check_device(struct MPT2SAS_ADAPTER *ioc, u16 handle) +{ + Mpi2ConfigReply_t mpi_reply; + Mpi2SasDevicePage0_t sas_device_pg0; + struct _sas_device *sas_device; + u32 ioc_status; + unsigned long flags; + u64 sas_address; + struct scsi_target *starget; + struct MPT2SAS_TARGET *sas_target_priv_data; + u32 device_info; + + + if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, + MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) + return; + + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) + return; + + /* check if this is end device */ + device_info = le32_to_cpu(sas_device_pg0.DeviceInfo); + if (!(_scsih_is_end_device(device_info))) + return; + + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_address = le64_to_cpu(sas_device_pg0.SASAddress); + sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, + sas_address); + + if (!sas_device) { + printk(MPT2SAS_ERR_FMT "device is not present " + "handle(0x%04x), no sas_device!!!\n", ioc->name, handle); + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + return; + } + + if (unlikely(sas_device->handle != handle)) { + starget = sas_device->starget; + sas_target_priv_data = starget->hostdata; + starget_printk(KERN_INFO, starget, "handle changed from(0x%04x)" + " to (0x%04x)!!!\n", sas_device->handle, handle); + sas_target_priv_data->handle = handle; + sas_device->handle = handle; + } + + /* check if device is present */ + if (!(le16_to_cpu(sas_device_pg0.Flags) & + MPI2_SAS_DEVICE0_FLAGS_DEVICE_PRESENT)) { + printk(MPT2SAS_ERR_FMT "device is not present " + "handle(0x%04x), flags!!!\n", ioc->name, handle); + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + return; + } + + /* check if there were any issues with discovery */ + if (_scsih_check_access_status(ioc, sas_address, handle, + sas_device_pg0.AccessStatus)) { + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + return; + } + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + _scsih_ublock_io_device(ioc, sas_address); + } /** @@ -3887,6 +5237,8 @@ _scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd) return -1; } + sas_address = le64_to_cpu(sas_device_pg0.SASAddress); + /* check if device is present */ if (!(le16_to_cpu(sas_device_pg0.Flags) & MPI2_SAS_DEVICE0_FLAGS_DEVICE_PRESENT)) { @@ -3897,15 +5249,10 @@ _scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd) return -1; } - /* check if there were any issus with discovery */ - if (sas_device_pg0.AccessStatus == - MPI2_SAS_DEVICE0_ASTATUS_SATA_INIT_FAILED) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - printk(MPT2SAS_ERR_FMT "AccessStatus = 0x%02x\n", - ioc->name, sas_device_pg0.AccessStatus); + /* check if there were any issues with discovery */ + if (_scsih_check_access_status(ioc, sas_address, handle, + sas_device_pg0.AccessStatus)) return -1; - } /* check if this is end device */ device_info = le32_to_cpu(sas_device_pg0.DeviceInfo); @@ -3915,17 +5262,14 @@ _scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd) return -1; } - sas_address = le64_to_cpu(sas_device_pg0.SASAddress); spin_lock_irqsave(&ioc->sas_device_lock, flags); sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, sas_address); spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - if (sas_device) { - _scsih_ublock_io_device(ioc, handle); + if (sas_device) return 0; - } sas_device = kzalloc(sizeof(struct _sas_device), GFP_KERNEL); @@ -3947,7 +5291,7 @@ _scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd) le16_to_cpu(sas_device_pg0.Slot); sas_device->device_info = device_info; sas_device->sas_address = sas_address; - sas_device->hidden_raid_component = is_pd; + sas_device->phy = sas_device_pg0.PhyNum; /* get enclosure_logical_id */ if (sas_device->enclosure_handle && !(mpt2sas_config_get_enclosure_pg0( @@ -3959,7 +5303,7 @@ _scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd) /* get device name */ sas_device->device_name = le64_to_cpu(sas_device_pg0.DeviceName); - if (ioc->wait_for_port_enable_to_complete) + if (ioc->wait_for_discovery_to_complete) _scsih_sas_device_init_add(ioc, sas_device); else _scsih_sas_device_add(ioc, sas_device); @@ -3970,102 +5314,95 @@ _scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd) /** * _scsih_remove_device - removing sas device object * @ioc: per adapter object - * @sas_device: the sas_device object + * @sas_device_delete: the sas_device object * * Return nothing. */ static void -_scsih_remove_device(struct MPT2SAS_ADAPTER *ioc, struct _sas_device - *sas_device) +_scsih_remove_device(struct MPT2SAS_ADAPTER *ioc, + struct _sas_device *sas_device) { struct MPT2SAS_TARGET *sas_target_priv_data; - Mpi2SasIoUnitControlReply_t mpi_reply; - Mpi2SasIoUnitControlRequest_t mpi_request; - u16 device_handle, handle; - - if (!sas_device) - return; - handle = sas_device->handle; - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter: handle(0x%04x)," - " sas_addr(0x%016llx)\n", ioc->name, __func__, handle, - (unsigned long long) sas_device->sas_address)); + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter: " + "handle(0x%04x), sas_addr(0x%016llx)\n", ioc->name, __func__, + sas_device->handle, (unsigned long long) + sas_device->sas_address)); if (sas_device->starget && sas_device->starget->hostdata) { sas_target_priv_data = sas_device->starget->hostdata; sas_target_priv_data->deleted = 1; + _scsih_ublock_io_device(ioc, sas_device->sas_address); + sas_target_priv_data->handle = + MPT2SAS_INVALID_DEVICE_HANDLE; } - if (ioc->remove_host || ioc->shost_recovery || !handle) - goto out; - - if ((sas_device->state & MPTSAS_STATE_TR_COMPLETE)) { - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "\tskip " - "target_reset handle(0x%04x)\n", ioc->name, - handle)); - goto skip_tr; - } - - /* Target Reset to flush out all the outstanding IO */ - device_handle = (sas_device->hidden_raid_component) ? - sas_device->volume_handle : handle; - if (device_handle) { - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "issue target reset: " - "handle(0x%04x)\n", ioc->name, device_handle)); - mutex_lock(&ioc->tm_cmds.mutex); - mpt2sas_scsih_issue_tm(ioc, device_handle, 0, - MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, 10); - ioc->tm_cmds.status = MPT2_CMD_NOT_USED; - mutex_unlock(&ioc->tm_cmds.mutex); - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "issue target reset " - "done: handle(0x%04x)\n", ioc->name, device_handle)); - if (ioc->shost_recovery) - goto out; - } - skip_tr: - - if ((sas_device->state & MPTSAS_STATE_CNTRL_COMPLETE)) { - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "\tskip " - "sas_cntrl handle(0x%04x)\n", ioc->name, handle)); - goto out; - } + if (!ioc->hide_drives) + mpt2sas_transport_port_remove(ioc, + sas_device->sas_address, + sas_device->sas_address_parent); - /* SAS_IO_UNIT_CNTR - send REMOVE_DEVICE */ - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "sas_iounit: handle" - "(0x%04x)\n", ioc->name, handle)); - memset(&mpi_request, 0, sizeof(Mpi2SasIoUnitControlRequest_t)); - mpi_request.Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL; - mpi_request.Operation = MPI2_SAS_OP_REMOVE_DEVICE; - mpi_request.DevHandle = handle; - mpi_request.VF_ID = 0; /* TODO */ - mpi_request.VP_ID = 0; - if ((mpt2sas_base_sas_iounit_control(ioc, &mpi_reply, - &mpi_request)) != 0) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - } + printk(MPT2SAS_INFO_FMT "removing handle(0x%04x), sas_addr" + "(0x%016llx)\n", ioc->name, sas_device->handle, + (unsigned long long) sas_device->sas_address); - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "sas_iounit: ioc_status" - "(0x%04x), loginfo(0x%08x)\n", ioc->name, - le16_to_cpu(mpi_reply.IOCStatus), - le32_to_cpu(mpi_reply.IOCLogInfo))); + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: exit: " + "handle(0x%04x), sas_addr(0x%016llx)\n", ioc->name, __func__, + sas_device->handle, (unsigned long long) + sas_device->sas_address)); + kfree(sas_device); +} +/** + * _scsih_device_remove_by_handle - removing device object by handle + * @ioc: per adapter object + * @handle: device handle + * + * Return nothing. + */ +static void +_scsih_device_remove_by_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle) +{ + struct _sas_device *sas_device; + unsigned long flags; - out: + if (ioc->shost_recovery) + return; - _scsih_ublock_io_device(ioc, handle); + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_device = _scsih_sas_device_find_by_handle(ioc, handle); + if (sas_device) + list_del(&sas_device->list); + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + if (sas_device) + _scsih_remove_device(ioc, sas_device); +} - mpt2sas_transport_port_remove(ioc, sas_device->sas_address, - sas_device->sas_address_parent); +/** + * mpt2sas_device_remove_by_sas_address - removing device object by sas address + * @ioc: per adapter object + * @sas_address: device sas_address + * + * Return nothing. + */ +void +mpt2sas_device_remove_by_sas_address(struct MPT2SAS_ADAPTER *ioc, + u64 sas_address) +{ + struct _sas_device *sas_device; + unsigned long flags; - printk(MPT2SAS_INFO_FMT "removing handle(0x%04x), sas_addr" - "(0x%016llx)\n", ioc->name, handle, - (unsigned long long) sas_device->sas_address); - _scsih_sas_device_remove(ioc, sas_device); + if (ioc->shost_recovery) + return; - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: exit: handle" - "(0x%04x)\n", ioc->name, __func__, handle)); + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, + sas_address); + if (sas_device) + list_del(&sas_device->list); + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + if (sas_device) + _scsih_remove_device(ioc, sas_device); } - #ifdef CONFIG_SCSI_MPT2SAS_LOGGING /** * _scsih_sas_topology_change_event_debug - debug for topology event @@ -4102,9 +5439,9 @@ _scsih_sas_topology_change_event_debug(struct MPT2SAS_ADAPTER *ioc, status_str = "unknown status"; break; } - printk(MPT2SAS_DEBUG_FMT "sas topology change: (%s)\n", + printk(MPT2SAS_INFO_FMT "sas topology change: (%s)\n", ioc->name, status_str); - printk(KERN_DEBUG "\thandle(0x%04x), enclosure_handle(0x%04x) " + printk(KERN_INFO "\thandle(0x%04x), enclosure_handle(0x%04x) " "start_phy(%02d), count(%d)\n", le16_to_cpu(event_data->ExpanderDevHandle), le16_to_cpu(event_data->EnclosureHandle), @@ -4138,7 +5475,7 @@ _scsih_sas_topology_change_event_debug(struct MPT2SAS_ADAPTER *ioc, } link_rate = event_data->PHY[i].LinkRate >> 4; prev_link_rate = event_data->PHY[i].LinkRate & 0xF; - printk(KERN_DEBUG "\tphy(%02d), attached_handle(0x%04x): %s:" + printk(KERN_INFO "\tphy(%02d), attached_handle(0x%04x): %s:" " link rate: new(0x%02x), old(0x%02x)\n", phy_number, handle, status_str, link_rate, prev_link_rate); @@ -4160,9 +5497,8 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc, int i; u16 parent_handle, handle; u16 reason_code; - u8 phy_number; + u8 phy_number, max_phys; struct _sas_node *sas_expander; - struct _sas_device *sas_device; u64 sas_address; unsigned long flags; u8 link_rate, prev_link_rate; @@ -4173,7 +5509,7 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc, _scsih_sas_topology_change_event_debug(ioc, event_data); #endif - if (ioc->shost_recovery) + if (ioc->remove_host || ioc->pci_error_recovery) return; if (!ioc->sas_hba.num_phys) @@ -4182,7 +5518,7 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc, _scsih_sas_host_refresh(ioc); if (fw_event->ignore) { - dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT "ignoring expander " + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "ignoring expander " "event\n", ioc->name)); return; } @@ -4197,24 +5533,31 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc, spin_lock_irqsave(&ioc->sas_node_lock, flags); sas_expander = mpt2sas_scsih_expander_find_by_handle(ioc, parent_handle); - spin_unlock_irqrestore(&ioc->sas_node_lock, flags); - if (sas_expander) + if (sas_expander) { sas_address = sas_expander->sas_address; - else if (parent_handle < ioc->sas_hba.num_phys) + max_phys = sas_expander->num_phys; + } else if (parent_handle < ioc->sas_hba.num_phys) { sas_address = ioc->sas_hba.sas_address; - else + max_phys = ioc->sas_hba.num_phys; + } else { + spin_unlock_irqrestore(&ioc->sas_node_lock, flags); return; + } + spin_unlock_irqrestore(&ioc->sas_node_lock, flags); /* handle siblings events */ for (i = 0; i < event_data->NumEntries; i++) { if (fw_event->ignore) { - dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT "ignoring " + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "ignoring " "expander event\n", ioc->name)); return; } - if (ioc->shost_recovery) + if (ioc->shost_recovery || ioc->remove_host || + ioc->pci_error_recovery) return; phy_number = event_data->StartPhyNum + i; + if (phy_number >= max_phys) + continue; reason_code = event_data->PHY[i].PhyStatus & MPI2_EVENT_SAS_TOPO_RC_MASK; if ((event_data->PHY[i].PhyStatus & @@ -4229,17 +5572,25 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc, switch (reason_code) { case MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED: + if (ioc->shost_recovery) + break; + if (link_rate == prev_link_rate) break; mpt2sas_transport_update_links(ioc, sas_address, handle, phy_number, link_rate); - if (link_rate >= MPI2_SAS_NEG_LINK_RATE_1_5) - _scsih_ublock_io_device(ioc, handle); + if (link_rate < MPI2_SAS_NEG_LINK_RATE_1_5) + break; + + _scsih_check_device(ioc, handle); break; case MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED: + if (ioc->shost_recovery) + break; + mpt2sas_transport_update_links(ioc, sas_address, handle, phy_number, link_rate); @@ -4247,16 +5598,7 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc, break; case MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING: - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = _scsih_sas_device_find_by_handle(ioc, - handle); - if (!sas_device) { - spin_unlock_irqrestore(&ioc->sas_device_lock, - flags); - break; - } - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - _scsih_remove_device(ioc, sas_device); + _scsih_device_remove_by_handle(ioc, handle); break; } } @@ -4264,7 +5606,7 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc, /* handle expander removal */ if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING && sas_expander) - _scsih_expander_remove(ioc, sas_address); + mpt2sas_expander_remove(ioc, sas_address); } @@ -4326,12 +5668,13 @@ _scsih_sas_device_status_change_event_debug(struct MPT2SAS_ADAPTER *ioc, reason_str = "unknown reason"; break; } - printk(MPT2SAS_DEBUG_FMT "device status change: (%s)\n" - "\thandle(0x%04x), sas address(0x%016llx)", ioc->name, - reason_str, le16_to_cpu(event_data->DevHandle), - (unsigned long long)le64_to_cpu(event_data->SASAddress)); + printk(MPT2SAS_INFO_FMT "device status change: (%s)\n" + "\thandle(0x%04x), sas address(0x%016llx), tag(%d)", + ioc->name, reason_str, le16_to_cpu(event_data->DevHandle), + (unsigned long long)le64_to_cpu(event_data->SASAddress), + le16_to_cpu(event_data->TaskTag)); if (event_data->ReasonCode == MPI2_EVENT_SAS_DEV_STAT_RC_SMART_DATA) - printk(MPT2SAS_DEBUG_FMT ", ASC(0x%x), ASCQ(0x%x)\n", ioc->name, + printk(MPT2SAS_INFO_FMT ", ASC(0x%x), ASCQ(0x%x)\n", ioc->name, event_data->ASC, event_data->ASCQ); printk(KERN_INFO "\n"); } @@ -4351,7 +5694,7 @@ _scsih_sas_device_status_change_event(struct MPT2SAS_ADAPTER *ioc, { struct MPT2SAS_TARGET *target_priv_data; struct _sas_device *sas_device; - __le64 sas_address; + u64 sas_address; unsigned long flags; Mpi2EventDataSasDeviceStatusChange_t *event_data = fw_event->event_data; @@ -4362,30 +5705,40 @@ _scsih_sas_device_status_change_event(struct MPT2SAS_ADAPTER *ioc, event_data); #endif - if (!(event_data->ReasonCode == + /* In MPI Revision K (0xC), the internal device reset complete was + * implemented, so avoid setting tm_busy flag for older firmware. + */ + if ((ioc->facts.HeaderVersion >> 8) < 0xC) + return; + + if (event_data->ReasonCode != MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET && - event_data->ReasonCode == - MPI2_EVENT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET)) + event_data->ReasonCode != + MPI2_EVENT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET) return; spin_lock_irqsave(&ioc->sas_device_lock, flags); sas_address = le64_to_cpu(event_data->SASAddress); sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, sas_address); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - if (!sas_device || !sas_device->starget) + if (!sas_device || !sas_device->starget) { + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); return; + } target_priv_data = sas_device->starget->hostdata; - if (!target_priv_data) + if (!target_priv_data) { + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); return; + } if (event_data->ReasonCode == MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET) target_priv_data->tm_busy = 1; else target_priv_data->tm_busy = 0; + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); } #ifdef CONFIG_SCSI_MPT2SAS_LOGGING @@ -4415,7 +5768,7 @@ _scsih_sas_enclosure_dev_status_change_event_debug(struct MPT2SAS_ADAPTER *ioc, break; } - printk(MPT2SAS_DEBUG_FMT "enclosure status change: (%s)\n" + printk(MPT2SAS_INFO_FMT "enclosure status change: (%s)\n" "\thandle(0x%04x), enclosure logical id(0x%016llx)" " number slots(%d)\n", ioc->name, reason_str, le16_to_cpu(event_data->EnclosureHandle), @@ -4444,7 +5797,7 @@ _scsih_sas_enclosure_dev_status_change_event(struct MPT2SAS_ADAPTER *ioc, } /** - * _scsih_sas_broadcast_primative_event - handle broadcast events + * _scsih_sas_broadcast_primitive_event - handle broadcast events * @ioc: per adapter object * @fw_event: The fw_event_work object * Context: user. @@ -4452,35 +5805,55 @@ _scsih_sas_enclosure_dev_status_change_event(struct MPT2SAS_ADAPTER *ioc, * Return nothing. */ static void -_scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc, +_scsih_sas_broadcast_primitive_event(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work *fw_event) { struct scsi_cmnd *scmd; + struct scsi_device *sdev; u16 smid, handle; u32 lun; struct MPT2SAS_DEVICE *sas_device_priv_data; u32 termination_count; u32 query_count; Mpi2SCSITaskManagementReply_t *mpi_reply; -#ifdef CONFIG_SCSI_MPT2SAS_LOGGING Mpi2EventDataSasBroadcastPrimitive_t *event_data = fw_event->event_data; -#endif u16 ioc_status; - dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT "broadcast primative: " - "phy number(%d), width(%d)\n", ioc->name, event_data->PhyNum, - event_data->PortWidth)); - dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: enter\n", ioc->name, - __func__)); + unsigned long flags; + int r; + u8 max_retries = 0; + u8 task_abort_retries; mutex_lock(&ioc->tm_cmds.mutex); + pr_info(MPT2SAS_FMT + "%s: enter: phy number(%d), width(%d)\n", + ioc->name, __func__, event_data->PhyNum, + event_data->PortWidth); + + _scsih_block_io_all_device(ioc); + + spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); + mpi_reply = ioc->tm_cmds.reply; +broadcast_aen_retry: + + /* sanity checks for retrying this loop */ + if (max_retries++ == 5) { + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: giving up\n", + ioc->name, __func__)); + goto out; + } else if (max_retries > 1) + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: %d retry\n", + ioc->name, __func__, max_retries - 1)); + termination_count = 0; query_count = 0; - mpi_reply = ioc->tm_cmds.reply; for (smid = 1; smid <= ioc->scsiio_depth; smid++) { + if (ioc->shost_recovery) + goto out; scmd = _scsih_scsi_lookup_get(ioc, smid); if (!scmd) continue; - sas_device_priv_data = scmd->device->hostdata; + sdev = scmd->device; + sas_device_priv_data = sdev->hostdata; if (!sas_device_priv_data || !sas_device_priv_data->sas_target) continue; /* skip hidden raid components */ @@ -4496,29 +5869,90 @@ _scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc, lun = sas_device_priv_data->lun; query_count++; - mpt2sas_scsih_issue_tm(ioc, handle, lun, - MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK, smid, 30); - ioc->tm_cmds.status = MPT2_CMD_NOT_USED; + if (ioc->shost_recovery) + goto out; + + spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); + r = mpt2sas_scsih_issue_tm(ioc, handle, 0, 0, lun, + MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK, smid, 30, + TM_MUTEX_OFF); + if (r == FAILED) { + sdev_printk(KERN_WARNING, sdev, + "mpt2sas_scsih_issue_tm: FAILED when sending " + "QUERY_TASK: scmd(%p)\n", scmd); + spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); + goto broadcast_aen_retry; + } ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK; - if ((ioc_status == MPI2_IOCSTATUS_SUCCESS) && - (mpi_reply->ResponseCode == + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + sdev_printk(KERN_WARNING, sdev, "query task: FAILED " + "with IOCSTATUS(0x%04x), scmd(%p)\n", ioc_status, + scmd); + spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); + goto broadcast_aen_retry; + } + + /* see if IO is still owned by IOC and target */ + if (mpi_reply->ResponseCode == MPI2_SCSITASKMGMT_RSP_TM_SUCCEEDED || mpi_reply->ResponseCode == - MPI2_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC)) + MPI2_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC) { + spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); continue; + } + task_abort_retries = 0; + tm_retry: + if (task_abort_retries++ == 60) { + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT + "%s: ABORT_TASK: giving up\n", ioc->name, + __func__)); + spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); + goto broadcast_aen_retry; + } + + if (ioc->shost_recovery) + goto out_no_lock; + + r = mpt2sas_scsih_issue_tm(ioc, handle, sdev->channel, sdev->id, + sdev->lun, MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK, smid, 30, + TM_MUTEX_OFF); + if (r == FAILED) { + sdev_printk(KERN_WARNING, sdev, + "mpt2sas_scsih_issue_tm: ABORT_TASK: FAILED : " + "scmd(%p)\n", scmd); + goto tm_retry; + } + + if (task_abort_retries > 1) + sdev_printk(KERN_WARNING, sdev, + "mpt2sas_scsih_issue_tm: ABORT_TASK: RETRIES (%d):" + " scmd(%p)\n", + task_abort_retries - 1, scmd); - mpt2sas_scsih_issue_tm(ioc, handle, lun, - MPI2_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET, 0, 30); - ioc->tm_cmds.status = MPT2_CMD_NOT_USED; termination_count += le32_to_cpu(mpi_reply->TerminationCount); + spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); + } + + if (ioc->broadcast_aen_pending) { + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: loop back due to" + " pending AEN\n", ioc->name, __func__)); + ioc->broadcast_aen_pending = 0; + goto broadcast_aen_retry; } - ioc->broadcast_aen_busy = 0; - mutex_unlock(&ioc->tm_cmds.mutex); - dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT + out: + spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); + out_no_lock: + + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s - exit, query_count = %d termination_count = %d\n", ioc->name, __func__, query_count, termination_count)); + + ioc->broadcast_aen_busy = 0; + if (!ioc->shost_recovery) + _scsih_ublock_io_all_device(ioc); + mutex_unlock(&ioc->tm_cmds.mutex); } /** @@ -4537,7 +5971,7 @@ _scsih_sas_discovery_event(struct MPT2SAS_ADAPTER *ioc, #ifdef CONFIG_SCSI_MPT2SAS_LOGGING if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) { - printk(MPT2SAS_DEBUG_FMT "discovery event: (%s)", ioc->name, + printk(MPT2SAS_INFO_FMT "discovery event: (%s)", ioc->name, (event_data->ReasonCode == MPI2_EVENT_SAS_DISC_RC_STARTED) ? "start" : "stop"); if (event_data->DiscoveryStatus) @@ -4548,8 +5982,14 @@ _scsih_sas_discovery_event(struct MPT2SAS_ADAPTER *ioc, #endif if (event_data->ReasonCode == MPI2_EVENT_SAS_DISC_RC_STARTED && - !ioc->sas_hba.num_phys) + !ioc->sas_hba.num_phys) { + if (disable_discovery > 0 && ioc->shost_recovery) { + /* Wait for the reset to complete */ + while (ioc->shost_recovery) + ssleep(1); + } _scsih_sas_host_add(ioc); + } } /** @@ -4570,27 +6010,6 @@ _scsih_reprobe_lun(struct scsi_device *sdev, void *no_uld_attach) } /** - * _scsih_reprobe_target - reprobing target - * @starget: scsi target struct - * @no_uld_attach: sdev->no_uld_attach flag setting - * - * Note: no_uld_attach flag determines whether the disk device is attached - * to block layer. A value of `1` means to not attach. - **/ -static void -_scsih_reprobe_target(struct scsi_target *starget, int no_uld_attach) -{ - struct MPT2SAS_TARGET *sas_target_priv_data = starget->hostdata; - - if (no_uld_attach) - sas_target_priv_data->flags |= MPT_TARGET_FLAGS_RAID_COMPONENT; - else - sas_target_priv_data->flags &= ~MPT_TARGET_FLAGS_RAID_COMPONENT; - - starget_for_each_device(starget, no_uld_attach ? (void *)1 : NULL, - _scsih_reprobe_lun); -} -/** * _scsih_sas_volume_add - add new volume * @ioc: per adapter object * @element: IR config element data @@ -4636,43 +6055,51 @@ _scsih_sas_volume_add(struct MPT2SAS_ADAPTER *ioc, raid_device->handle = handle; raid_device->wwid = wwid; _scsih_raid_device_add(ioc, raid_device); - if (!ioc->wait_for_port_enable_to_complete) { + if (!ioc->wait_for_discovery_to_complete) { rc = scsi_add_device(ioc->shost, RAID_CHANNEL, raid_device->id, 0); if (rc) _scsih_raid_device_remove(ioc, raid_device); - } else + } else { + spin_lock_irqsave(&ioc->raid_device_lock, flags); _scsih_determine_boot_device(ioc, raid_device, 1); + spin_unlock_irqrestore(&ioc->raid_device_lock, flags); + } } /** * _scsih_sas_volume_delete - delete volume * @ioc: per adapter object - * @element: IR config element data + * @handle: volume device handle * Context: user. * * Return nothing. */ static void -_scsih_sas_volume_delete(struct MPT2SAS_ADAPTER *ioc, - Mpi2EventIrConfigElement_t *element) +_scsih_sas_volume_delete(struct MPT2SAS_ADAPTER *ioc, u16 handle) { struct _raid_device *raid_device; - u16 handle = le16_to_cpu(element->VolDevHandle); unsigned long flags; struct MPT2SAS_TARGET *sas_target_priv_data; + struct scsi_target *starget = NULL; spin_lock_irqsave(&ioc->raid_device_lock, flags); raid_device = _scsih_raid_device_find_by_handle(ioc, handle); - spin_unlock_irqrestore(&ioc->raid_device_lock, flags); - if (!raid_device) - return; - if (raid_device->starget) { - sas_target_priv_data = raid_device->starget->hostdata; - sas_target_priv_data->deleted = 1; - scsi_remove_target(&raid_device->starget->dev); + if (raid_device) { + if (raid_device->starget) { + starget = raid_device->starget; + sas_target_priv_data = starget->hostdata; + sas_target_priv_data->deleted = 1; + } + printk(MPT2SAS_INFO_FMT "removing handle(0x%04x), wwid" + "(0x%016llx)\n", ioc->name, raid_device->handle, + (unsigned long long) raid_device->wwid); + list_del(&raid_device->list); + kfree(raid_device); } - _scsih_raid_device_remove(ioc, raid_device); + spin_unlock_irqrestore(&ioc->raid_device_lock, flags); + if (starget) + scsi_remove_target(&starget->dev); } /** @@ -4688,20 +6115,31 @@ _scsih_sas_pd_expose(struct MPT2SAS_ADAPTER *ioc, Mpi2EventIrConfigElement_t *element) { struct _sas_device *sas_device; + struct scsi_target *starget = NULL; + struct MPT2SAS_TARGET *sas_target_priv_data; unsigned long flags; u16 handle = le16_to_cpu(element->PhysDiskDevHandle); spin_lock_irqsave(&ioc->sas_device_lock, flags); sas_device = _scsih_sas_device_find_by_handle(ioc, handle); + if (sas_device) { + sas_device->volume_handle = 0; + sas_device->volume_wwid = 0; + clear_bit(handle, ioc->pd_handles); + if (sas_device->starget && sas_device->starget->hostdata) { + starget = sas_device->starget; + sas_target_priv_data = starget->hostdata; + sas_target_priv_data->flags &= + ~MPT_TARGET_FLAGS_RAID_COMPONENT; + } + } spin_unlock_irqrestore(&ioc->sas_device_lock, flags); if (!sas_device) return; /* exposing raid component */ - sas_device->volume_handle = 0; - sas_device->volume_wwid = 0; - sas_device->hidden_raid_component = 0; - _scsih_reprobe_target(sas_device->starget, 0); + if (starget) + starget_for_each_device(starget, NULL, _scsih_reprobe_lun); } /** @@ -4717,22 +6155,38 @@ _scsih_sas_pd_hide(struct MPT2SAS_ADAPTER *ioc, Mpi2EventIrConfigElement_t *element) { struct _sas_device *sas_device; + struct scsi_target *starget = NULL; + struct MPT2SAS_TARGET *sas_target_priv_data; unsigned long flags; u16 handle = le16_to_cpu(element->PhysDiskDevHandle); + u16 volume_handle = 0; + u64 volume_wwid = 0; + + mpt2sas_config_get_volume_handle(ioc, handle, &volume_handle); + if (volume_handle) + mpt2sas_config_get_volume_wwid(ioc, volume_handle, + &volume_wwid); spin_lock_irqsave(&ioc->sas_device_lock, flags); sas_device = _scsih_sas_device_find_by_handle(ioc, handle); + if (sas_device) { + set_bit(handle, ioc->pd_handles); + if (sas_device->starget && sas_device->starget->hostdata) { + starget = sas_device->starget; + sas_target_priv_data = starget->hostdata; + sas_target_priv_data->flags |= + MPT_TARGET_FLAGS_RAID_COMPONENT; + sas_device->volume_handle = volume_handle; + sas_device->volume_wwid = volume_wwid; + } + } spin_unlock_irqrestore(&ioc->sas_device_lock, flags); if (!sas_device) return; /* hiding raid component */ - mpt2sas_config_get_volume_handle(ioc, handle, - &sas_device->volume_handle); - mpt2sas_config_get_volume_wwid(ioc, sas_device->volume_handle, - &sas_device->volume_wwid); - sas_device->hidden_raid_component = 1; - _scsih_reprobe_target(sas_device->starget, 1); + if (starget) + starget_for_each_device(starget, (void *)1, _scsih_reprobe_lun); } /** @@ -4747,16 +6201,9 @@ static void _scsih_sas_pd_delete(struct MPT2SAS_ADAPTER *ioc, Mpi2EventIrConfigElement_t *element) { - struct _sas_device *sas_device; - unsigned long flags; u16 handle = le16_to_cpu(element->PhysDiskDevHandle); - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = _scsih_sas_device_find_by_handle(ioc, handle); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - if (!sas_device) - return; - _scsih_remove_device(ioc, sas_device); + _scsih_device_remove_by_handle(ioc, handle); } /** @@ -4780,13 +6227,13 @@ _scsih_sas_pd_add(struct MPT2SAS_ADAPTER *ioc, u64 sas_address; u16 parent_handle; + set_bit(handle, ioc->pd_handles); + spin_lock_irqsave(&ioc->sas_device_lock, flags); sas_device = _scsih_sas_device_find_by_handle(ioc, handle); spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - if (sas_device) { - sas_device->hidden_raid_component = 1; + if (sas_device) return; - } if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) { @@ -4831,7 +6278,7 @@ _scsih_sas_ir_config_change_event_debug(struct MPT2SAS_ADAPTER *ioc, element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0]; - printk(MPT2SAS_DEBUG_FMT "raid config change: (%s), elements(%d)\n", + printk(MPT2SAS_INFO_FMT "raid config change: (%s), elements(%d)\n", ioc->name, (le32_to_cpu(event_data->Flags) & MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG) ? "foreign" : "native", event_data->NumElements); @@ -4884,7 +6331,7 @@ _scsih_sas_ir_config_change_event_debug(struct MPT2SAS_ADAPTER *ioc, element_str = "unknown element"; break; } - printk(KERN_DEBUG "\t(%s:%s), vol handle(0x%04x), " + printk(KERN_INFO "\t(%s:%s), vol handle(0x%04x), " "pd handle(0x%04x), pd num(0x%02x)\n", element_str, reason_str, le16_to_cpu(element->VolDevHandle), le16_to_cpu(element->PhysDiskDevHandle), @@ -4911,10 +6358,15 @@ _scsih_sas_ir_config_change_event(struct MPT2SAS_ADAPTER *ioc, Mpi2EventDataIrConfigChangeList_t *event_data = fw_event->event_data; #ifdef CONFIG_SCSI_MPT2SAS_LOGGING - if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) + if ((ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) + && !ioc->hide_ir_msg) _scsih_sas_ir_config_change_event_debug(ioc, event_data); #endif + + if (ioc->shost_recovery) + return; + foreign_config = (le32_to_cpu(event_data->Flags) & MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG) ? 1 : 0; @@ -4930,19 +6382,24 @@ _scsih_sas_ir_config_change_event(struct MPT2SAS_ADAPTER *ioc, case MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED: case MPI2_EVENT_IR_CHANGE_RC_REMOVED: if (!foreign_config) - _scsih_sas_volume_delete(ioc, element); + _scsih_sas_volume_delete(ioc, + le16_to_cpu(element->VolDevHandle)); break; case MPI2_EVENT_IR_CHANGE_RC_PD_CREATED: - _scsih_sas_pd_hide(ioc, element); + if (!ioc->is_warpdrive) + _scsih_sas_pd_hide(ioc, element); break; case MPI2_EVENT_IR_CHANGE_RC_PD_DELETED: - _scsih_sas_pd_expose(ioc, element); + if (!ioc->is_warpdrive) + _scsih_sas_pd_expose(ioc, element); break; case MPI2_EVENT_IR_CHANGE_RC_HIDE: - _scsih_sas_pd_add(ioc, element); + if (!ioc->is_warpdrive) + _scsih_sas_pd_add(ioc, element); break; case MPI2_EVENT_IR_CHANGE_RC_UNHIDE: - _scsih_sas_pd_delete(ioc, element); + if (!ioc->is_warpdrive) + _scsih_sas_pd_delete(ioc, element); break; } } @@ -4966,38 +6423,35 @@ _scsih_sas_ir_volume_event(struct MPT2SAS_ADAPTER *ioc, u16 handle; u32 state; int rc; - struct MPT2SAS_TARGET *sas_target_priv_data; Mpi2EventDataIrVolume_t *event_data = fw_event->event_data; + if (ioc->shost_recovery) + return; + if (event_data->ReasonCode != MPI2_EVENT_IR_VOLUME_RC_STATE_CHANGED) return; handle = le16_to_cpu(event_data->VolDevHandle); state = le32_to_cpu(event_data->NewValue); - dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: handle(0x%04x), " - "old(0x%08x), new(0x%08x)\n", ioc->name, __func__, handle, - le32_to_cpu(event_data->PreviousValue), state)); - - spin_lock_irqsave(&ioc->raid_device_lock, flags); - raid_device = _scsih_raid_device_find_by_handle(ioc, handle); - spin_unlock_irqrestore(&ioc->raid_device_lock, flags); + if (!ioc->hide_ir_msg) + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: handle(0x%04x), " + "old(0x%08x), new(0x%08x)\n", ioc->name, __func__, handle, + le32_to_cpu(event_data->PreviousValue), state)); switch (state) { case MPI2_RAID_VOL_STATE_MISSING: case MPI2_RAID_VOL_STATE_FAILED: - if (!raid_device) - break; - if (raid_device->starget) { - sas_target_priv_data = raid_device->starget->hostdata; - sas_target_priv_data->deleted = 1; - scsi_remove_target(&raid_device->starget->dev); - } - _scsih_raid_device_remove(ioc, raid_device); + _scsih_sas_volume_delete(ioc, handle); break; case MPI2_RAID_VOL_STATE_ONLINE: case MPI2_RAID_VOL_STATE_DEGRADED: case MPI2_RAID_VOL_STATE_OPTIMAL: + + spin_lock_irqsave(&ioc->raid_device_lock, flags); + raid_device = _scsih_raid_device_find_by_handle(ioc, handle); + spin_unlock_irqrestore(&ioc->raid_device_lock, flags); + if (raid_device) break; @@ -5056,29 +6510,36 @@ _scsih_sas_ir_physical_disk_event(struct MPT2SAS_ADAPTER *ioc, Mpi2EventDataIrPhysicalDisk_t *event_data = fw_event->event_data; u64 sas_address; + if (ioc->shost_recovery) + return; + if (event_data->ReasonCode != MPI2_EVENT_IR_PHYSDISK_RC_STATE_CHANGED) return; handle = le16_to_cpu(event_data->PhysDiskDevHandle); state = le32_to_cpu(event_data->NewValue); - dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: handle(0x%04x), " - "old(0x%08x), new(0x%08x)\n", ioc->name, __func__, handle, - le32_to_cpu(event_data->PreviousValue), state)); - - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = _scsih_sas_device_find_by_handle(ioc, handle); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + if (!ioc->hide_ir_msg) + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: handle(0x%04x), " + "old(0x%08x), new(0x%08x)\n", ioc->name, __func__, handle, + le32_to_cpu(event_data->PreviousValue), state)); switch (state) { case MPI2_RAID_PD_STATE_ONLINE: case MPI2_RAID_PD_STATE_DEGRADED: case MPI2_RAID_PD_STATE_REBUILDING: case MPI2_RAID_PD_STATE_OPTIMAL: - if (sas_device) { - sas_device->hidden_raid_component = 1; + case MPI2_RAID_PD_STATE_HOT_SPARE: + + if (!ioc->is_warpdrive) + set_bit(handle, ioc->pd_handles); + + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_device = _scsih_sas_device_find_by_handle(ioc, handle); + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + + if (sas_device) return; - } if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, @@ -5108,7 +6569,6 @@ _scsih_sas_ir_physical_disk_event(struct MPT2SAS_ADAPTER *ioc, case MPI2_RAID_PD_STATE_OFFLINE: case MPI2_RAID_PD_STATE_NOT_CONFIGURED: case MPI2_RAID_PD_STATE_NOT_COMPATIBLE: - case MPI2_RAID_PD_STATE_HOT_SPARE: default: break; } @@ -5170,94 +6630,48 @@ static void _scsih_sas_ir_operation_status_event(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work *fw_event) { + Mpi2EventDataIrOperationStatus_t *event_data = fw_event->event_data; + static struct _raid_device *raid_device; + unsigned long flags; + u16 handle; + #ifdef CONFIG_SCSI_MPT2SAS_LOGGING - if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) + if ((ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) + && !ioc->hide_ir_msg) _scsih_sas_ir_operation_status_event_debug(ioc, - fw_event->event_data); + event_data); #endif + + /* code added for raid transport support */ + if (event_data->RAIDOperation == MPI2_EVENT_IR_RAIDOP_RESYNC) { + + spin_lock_irqsave(&ioc->raid_device_lock, flags); + handle = le16_to_cpu(event_data->VolDevHandle); + raid_device = _scsih_raid_device_find_by_handle(ioc, handle); + if (raid_device) + raid_device->percent_complete = + event_data->PercentComplete; + spin_unlock_irqrestore(&ioc->raid_device_lock, flags); + } } /** - * _scsih_task_set_full - handle task set full + * _scsih_prep_device_scan - initialize parameters prior to device scan * @ioc: per adapter object - * @fw_event: The fw_event_work object - * Context: user. * - * Throttle back qdepth. + * Set the deleted flag prior to device scan. If the device is found during + * the scan, then we clear the deleted flag. */ static void -_scsih_task_set_full(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work - *fw_event) +_scsih_prep_device_scan(struct MPT2SAS_ADAPTER *ioc) { - unsigned long flags; - struct _sas_device *sas_device; - static struct _raid_device *raid_device; + struct MPT2SAS_DEVICE *sas_device_priv_data; struct scsi_device *sdev; - int depth; - u16 current_depth; - u16 handle; - int id, channel; - u64 sas_address; - Mpi2EventDataTaskSetFull_t *event_data = fw_event->event_data; - - current_depth = le16_to_cpu(event_data->CurrentDepth); - handle = le16_to_cpu(event_data->DevHandle); - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = _scsih_sas_device_find_by_handle(ioc, handle); - if (!sas_device) { - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - return; - } - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - id = sas_device->id; - channel = sas_device->channel; - sas_address = sas_device->sas_address; - - /* if hidden raid component, then change to volume characteristics */ - if (sas_device->hidden_raid_component && sas_device->volume_handle) { - spin_lock_irqsave(&ioc->raid_device_lock, flags); - raid_device = _scsih_raid_device_find_by_handle( - ioc, sas_device->volume_handle); - spin_unlock_irqrestore(&ioc->raid_device_lock, flags); - if (raid_device) { - id = raid_device->id; - channel = raid_device->channel; - handle = raid_device->handle; - sas_address = raid_device->wwid; - } - } - - if (ioc->logging_level & MPT_DEBUG_TASK_SET_FULL) - starget_printk(KERN_DEBUG, sas_device->starget, "task set " - "full: handle(0x%04x), sas_addr(0x%016llx), depth(%d)\n", - handle, (unsigned long long)sas_address, current_depth); shost_for_each_device(sdev, ioc->shost) { - if (sdev->id == id && sdev->channel == channel) { - if (current_depth > sdev->queue_depth) { - if (ioc->logging_level & - MPT_DEBUG_TASK_SET_FULL) - sdev_printk(KERN_INFO, sdev, "strange " - "observation, the queue depth is" - " (%d) meanwhile fw queue depth " - "is (%d)\n", sdev->queue_depth, - current_depth); - continue; - } - depth = scsi_track_queue_full(sdev, - current_depth - 1); - if (depth > 0) - sdev_printk(KERN_INFO, sdev, "Queue depth " - "reduced to (%d)\n", depth); - else if (depth < 0) - sdev_printk(KERN_INFO, sdev, "Tagged Command " - "Queueing is being disabled\n"); - else if (depth == 0) - if (ioc->logging_level & - MPT_DEBUG_TASK_SET_FULL) - sdev_printk(KERN_INFO, sdev, - "Queue depth not changed yet\n"); - } + sas_device_priv_data = sdev->hostdata; + if (sas_device_priv_data && sas_device_priv_data->sas_target) + sas_device_priv_data->sas_target->deleted = 1; } } @@ -5277,7 +6691,7 @@ static void _scsih_mark_responding_sas_device(struct MPT2SAS_ADAPTER *ioc, u64 sas_address, u16 slot, u16 handle) { - struct MPT2SAS_TARGET *sas_target_priv_data; + struct MPT2SAS_TARGET *sas_target_priv_data = NULL; struct scsi_target *starget; struct _sas_device *sas_device; unsigned long flags; @@ -5285,25 +6699,31 @@ _scsih_mark_responding_sas_device(struct MPT2SAS_ADAPTER *ioc, u64 sas_address, spin_lock_irqsave(&ioc->sas_device_lock, flags); list_for_each_entry(sas_device, &ioc->sas_device_list, list) { if (sas_device->sas_address == sas_address && - sas_device->slot == slot && sas_device->starget) { + sas_device->slot == slot) { sas_device->responding = 1; - sas_device->state = 0; starget = sas_device->starget; - sas_target_priv_data = starget->hostdata; - sas_target_priv_data->tm_busy = 0; - starget_printk(KERN_INFO, sas_device->starget, - "handle(0x%04x), sas_addr(0x%016llx), enclosure " - "logical id(0x%016llx), slot(%d)\n", handle, - (unsigned long long)sas_device->sas_address, - (unsigned long long) - sas_device->enclosure_logical_id, - sas_device->slot); + if (starget && starget->hostdata) { + sas_target_priv_data = starget->hostdata; + sas_target_priv_data->tm_busy = 0; + sas_target_priv_data->deleted = 0; + } else + sas_target_priv_data = NULL; + if (starget) + starget_printk(KERN_INFO, starget, + "handle(0x%04x), sas_addr(0x%016llx), " + "enclosure logical id(0x%016llx), " + "slot(%d)\n", handle, + (unsigned long long)sas_device->sas_address, + (unsigned long long) + sas_device->enclosure_logical_id, + sas_device->slot); if (sas_device->handle == handle) goto out; printk(KERN_INFO "\thandle changed from(0x%04x)!!!\n", sas_device->handle); sas_device->handle = handle; - sas_target_priv_data->handle = handle; + if (sas_target_priv_data) + sas_target_priv_data->handle = handle; goto out; } } @@ -5331,10 +6751,10 @@ _scsih_search_responding_sas_devices(struct MPT2SAS_ADAPTER *ioc) u32 device_info; u16 slot; - printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__); + printk(MPT2SAS_INFO_FMT "search for end-devices: start\n", ioc->name); if (list_empty(&ioc->sas_device_list)) - return; + goto out; handle = 0xFFFF; while (!(mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, @@ -5342,7 +6762,7 @@ _scsih_search_responding_sas_devices(struct MPT2SAS_ADAPTER *ioc) handle))) { ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; - if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) break; handle = le16_to_cpu(sas_device_pg0.DevHandle); device_info = le32_to_cpu(sas_device_pg0.DeviceInfo); @@ -5353,6 +6773,9 @@ _scsih_search_responding_sas_devices(struct MPT2SAS_ADAPTER *ioc) _scsih_mark_responding_sas_device(ioc, sas_address, slot, handle); } +out: + printk(MPT2SAS_INFO_FMT "search for end-devices: complete\n", + ioc->name); } /** @@ -5378,22 +6801,39 @@ _scsih_mark_responding_raid_device(struct MPT2SAS_ADAPTER *ioc, u64 wwid, spin_lock_irqsave(&ioc->raid_device_lock, flags); list_for_each_entry(raid_device, &ioc->raid_device_list, list) { if (raid_device->wwid == wwid && raid_device->starget) { + starget = raid_device->starget; + if (starget && starget->hostdata) { + sas_target_priv_data = starget->hostdata; + sas_target_priv_data->deleted = 0; + } else + sas_target_priv_data = NULL; raid_device->responding = 1; + spin_unlock_irqrestore(&ioc->raid_device_lock, flags); starget_printk(KERN_INFO, raid_device->starget, "handle(0x%04x), wwid(0x%016llx)\n", handle, (unsigned long long)raid_device->wwid); - if (raid_device->handle == handle) - goto out; + /* + * WARPDRIVE: The handles of the PDs might have changed + * across the host reset so re-initialize the + * required data for Direct IO + */ + _scsih_init_warpdrive_properties(ioc, raid_device); + spin_lock_irqsave(&ioc->raid_device_lock, flags); + if (raid_device->handle == handle) { + spin_unlock_irqrestore(&ioc->raid_device_lock, + flags); + return; + } printk(KERN_INFO "\thandle changed from(0x%04x)!!!\n", raid_device->handle); raid_device->handle = handle; - starget = raid_device->starget; - sas_target_priv_data = starget->hostdata; - sas_target_priv_data->handle = handle; - goto out; + if (sas_target_priv_data) + sas_target_priv_data->handle = handle; + spin_unlock_irqrestore(&ioc->raid_device_lock, flags); + return; } } - out: + spin_unlock_irqrestore(&ioc->raid_device_lock, flags); } @@ -5410,26 +6850,62 @@ static void _scsih_search_responding_raid_devices(struct MPT2SAS_ADAPTER *ioc) { Mpi2RaidVolPage1_t volume_pg1; + Mpi2RaidVolPage0_t volume_pg0; + Mpi2RaidPhysDiskPage0_t pd_pg0; Mpi2ConfigReply_t mpi_reply; u16 ioc_status; u16 handle; + u8 phys_disk_num; + + if (!ioc->ir_firmware) + return; - printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__); + printk(MPT2SAS_INFO_FMT "search for raid volumes: start\n", + ioc->name); if (list_empty(&ioc->raid_device_list)) - return; + goto out; handle = 0xFFFF; while (!(mpt2sas_config_get_raid_volume_pg1(ioc, &mpi_reply, &volume_pg1, MPI2_RAID_VOLUME_PGAD_FORM_GET_NEXT_HANDLE, handle))) { ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; - if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) break; handle = le16_to_cpu(volume_pg1.DevHandle); - _scsih_mark_responding_raid_device(ioc, - le64_to_cpu(volume_pg1.WWID), handle); + + if (mpt2sas_config_get_raid_volume_pg0(ioc, &mpi_reply, + &volume_pg0, MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, handle, + sizeof(Mpi2RaidVolPage0_t))) + continue; + + if (volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_OPTIMAL || + volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_ONLINE || + volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_DEGRADED) + _scsih_mark_responding_raid_device(ioc, + le64_to_cpu(volume_pg1.WWID), handle); + } + + /* refresh the pd_handles */ + if (!ioc->is_warpdrive) { + phys_disk_num = 0xFF; + memset(ioc->pd_handles, 0, ioc->pd_handles_sz); + while (!(mpt2sas_config_get_phys_disk_pg0(ioc, &mpi_reply, + &pd_pg0, MPI2_PHYSDISK_PGAD_FORM_GET_NEXT_PHYSDISKNUM, + phys_disk_num))) { + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) + break; + phys_disk_num = pd_pg0.PhysDiskNum; + handle = le16_to_cpu(pd_pg0.DevHandle); + set_bit(handle, ioc->pd_handles); + } } +out: + printk(MPT2SAS_INFO_FMT "search for responding raid volumes: " + "complete\n", ioc->name); } /** @@ -5486,13 +6962,13 @@ _scsih_search_responding_expanders(struct MPT2SAS_ADAPTER *ioc) Mpi2ExpanderPage0_t expander_pg0; Mpi2ConfigReply_t mpi_reply; u16 ioc_status; - __le64 sas_address; + u64 sas_address; u16 handle; - printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__); + printk(MPT2SAS_INFO_FMT "search for expanders: start\n", ioc->name); if (list_empty(&ioc->sas_expander_list)) - return; + goto out; handle = 0xFFFF; while (!(mpt2sas_config_get_expander_pg0(ioc, &mpi_reply, &expander_pg0, @@ -5500,7 +6976,7 @@ _scsih_search_responding_expanders(struct MPT2SAS_ADAPTER *ioc) ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; - if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) break; handle = le16_to_cpu(expander_pg0.DevHandle); @@ -5511,70 +6987,348 @@ _scsih_search_responding_expanders(struct MPT2SAS_ADAPTER *ioc) _scsih_mark_responding_expander(ioc, sas_address, handle); } + out: + printk(MPT2SAS_INFO_FMT "search for expanders: complete\n", ioc->name); } /** - * _scsih_remove_unresponding_devices - removing unresponding devices + * _scsih_remove_unresponding_sas_devices - removing unresponding devices * @ioc: per adapter object * * Return nothing. */ static void -_scsih_remove_unresponding_devices(struct MPT2SAS_ADAPTER *ioc) +_scsih_remove_unresponding_sas_devices(struct MPT2SAS_ADAPTER *ioc) { struct _sas_device *sas_device, *sas_device_next; - struct _sas_node *sas_expander; + struct _sas_node *sas_expander, *sas_expander_next; struct _raid_device *raid_device, *raid_device_next; + struct list_head tmp_list; + unsigned long flags; + printk(MPT2SAS_INFO_FMT "removing unresponding devices: start\n", + ioc->name); + /* removing unresponding end devices */ + printk(MPT2SAS_INFO_FMT "removing unresponding devices: end-devices\n", + ioc->name); list_for_each_entry_safe(sas_device, sas_device_next, &ioc->sas_device_list, list) { - if (sas_device->responding) { + if (!sas_device->responding) + mpt2sas_device_remove_by_sas_address(ioc, + sas_device->sas_address); + else sas_device->responding = 0; + } + + /* removing unresponding volumes */ + if (ioc->ir_firmware) { + printk(MPT2SAS_INFO_FMT "removing unresponding devices: " + "volumes\n", ioc->name); + list_for_each_entry_safe(raid_device, raid_device_next, + &ioc->raid_device_list, list) { + if (!raid_device->responding) + _scsih_sas_volume_delete(ioc, + raid_device->handle); + else + raid_device->responding = 0; + } + } + /* removing unresponding expanders */ + printk(MPT2SAS_INFO_FMT "removing unresponding devices: expanders\n", + ioc->name); + spin_lock_irqsave(&ioc->sas_node_lock, flags); + INIT_LIST_HEAD(&tmp_list); + list_for_each_entry_safe(sas_expander, sas_expander_next, + &ioc->sas_expander_list, list) { + if (!sas_expander->responding) + list_move_tail(&sas_expander->list, &tmp_list); + else + sas_expander->responding = 0; + } + spin_unlock_irqrestore(&ioc->sas_node_lock, flags); + list_for_each_entry_safe(sas_expander, sas_expander_next, &tmp_list, + list) { + list_del(&sas_expander->list); + _scsih_expander_node_remove(ioc, sas_expander); + } + printk(MPT2SAS_INFO_FMT "removing unresponding devices: complete\n", + ioc->name); + /* unblock devices */ + _scsih_ublock_io_all_device(ioc); +} + +static void +_scsih_refresh_expander_links(struct MPT2SAS_ADAPTER *ioc, + struct _sas_node *sas_expander, u16 handle) +{ + Mpi2ExpanderPage1_t expander_pg1; + Mpi2ConfigReply_t mpi_reply; + int i; + + for (i = 0 ; i < sas_expander->num_phys ; i++) { + if ((mpt2sas_config_get_expander_pg1(ioc, &mpi_reply, + &expander_pg1, i, handle))) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return; + } + + mpt2sas_transport_update_links(ioc, sas_expander->sas_address, + le16_to_cpu(expander_pg1.AttachedDevHandle), i, + expander_pg1.NegotiatedLinkRate >> 4); + } +} + +/** + * _scsih_scan_for_devices_after_reset - scan for devices after host reset + * @ioc: per adapter object + * + * Return nothing. + */ +static void +_scsih_scan_for_devices_after_reset(struct MPT2SAS_ADAPTER *ioc) +{ + Mpi2ExpanderPage0_t expander_pg0; + Mpi2SasDevicePage0_t sas_device_pg0; + Mpi2RaidVolPage1_t volume_pg1; + Mpi2RaidVolPage0_t volume_pg0; + Mpi2RaidPhysDiskPage0_t pd_pg0; + Mpi2EventIrConfigElement_t element; + Mpi2ConfigReply_t mpi_reply; + u8 phys_disk_num; + u16 ioc_status; + u16 handle, parent_handle; + u64 sas_address; + struct _sas_device *sas_device; + struct _sas_node *expander_device; + static struct _raid_device *raid_device; + u8 retry_count; + unsigned long flags; + + printk(MPT2SAS_INFO_FMT "scan devices: start\n", ioc->name); + + _scsih_sas_host_refresh(ioc); + + printk(MPT2SAS_INFO_FMT "\tscan devices: expanders start\n", + ioc->name); + /* expanders */ + handle = 0xFFFF; + while (!(mpt2sas_config_get_expander_pg0(ioc, &mpi_reply, &expander_pg0, + MPI2_SAS_EXPAND_PGAD_FORM_GET_NEXT_HNDL, handle))) { + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + printk(MPT2SAS_INFO_FMT "\tbreak from expander scan: " + "ioc_status(0x%04x), loginfo(0x%08x)\n", + ioc->name, ioc_status, + le32_to_cpu(mpi_reply.IOCLogInfo)); + break; + } + handle = le16_to_cpu(expander_pg0.DevHandle); + spin_lock_irqsave(&ioc->sas_node_lock, flags); + expander_device = mpt2sas_scsih_expander_find_by_sas_address( + ioc, le64_to_cpu(expander_pg0.SASAddress)); + spin_unlock_irqrestore(&ioc->sas_node_lock, flags); + if (expander_device) + _scsih_refresh_expander_links(ioc, expander_device, + handle); + else { + printk(MPT2SAS_INFO_FMT "\tBEFORE adding expander: " + "handle (0x%04x), sas_addr(0x%016llx)\n", + ioc->name, handle, (unsigned long long) + le64_to_cpu(expander_pg0.SASAddress)); + _scsih_expander_add(ioc, handle); + printk(MPT2SAS_INFO_FMT "\tAFTER adding expander: " + "handle (0x%04x), sas_addr(0x%016llx)\n", + ioc->name, handle, (unsigned long long) + le64_to_cpu(expander_pg0.SASAddress)); + } + } + + printk(MPT2SAS_INFO_FMT "\tscan devices: expanders complete\n", + ioc->name); + + if (!ioc->ir_firmware) + goto skip_to_sas; + + printk(MPT2SAS_INFO_FMT "\tscan devices phys disk start\n", ioc->name); + /* phys disk */ + phys_disk_num = 0xFF; + while (!(mpt2sas_config_get_phys_disk_pg0(ioc, &mpi_reply, + &pd_pg0, MPI2_PHYSDISK_PGAD_FORM_GET_NEXT_PHYSDISKNUM, + phys_disk_num))) { + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + printk(MPT2SAS_INFO_FMT "\tbreak from phys disk scan:" + "ioc_status(0x%04x), loginfo(0x%08x)\n", + ioc->name, ioc_status, + le32_to_cpu(mpi_reply.IOCLogInfo)); + break; + } + phys_disk_num = pd_pg0.PhysDiskNum; + handle = le16_to_cpu(pd_pg0.DevHandle); + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_device = _scsih_sas_device_find_by_handle(ioc, handle); + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + if (sas_device) + continue; + if (mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, + &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, + handle) != 0) continue; + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + printk(MPT2SAS_INFO_FMT "\tbreak from phys disk scan " + "ioc_status(0x%04x), loginfo(0x%08x)\n", + ioc->name, ioc_status, + le32_to_cpu(mpi_reply.IOCLogInfo)); + break; + } + parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle); + if (!_scsih_get_sas_address(ioc, parent_handle, + &sas_address)) { + printk(MPT2SAS_INFO_FMT "\tBEFORE adding phys disk: " + " handle (0x%04x), sas_addr(0x%016llx)\n", + ioc->name, handle, (unsigned long long) + le64_to_cpu(sas_device_pg0.SASAddress)); + mpt2sas_transport_update_links(ioc, sas_address, + handle, sas_device_pg0.PhyNum, + MPI2_SAS_NEG_LINK_RATE_1_5); + set_bit(handle, ioc->pd_handles); + retry_count = 0; + /* This will retry adding the end device. + * _scsih_add_device() will decide on retries and + * return "1" when it should be retried + */ + while (_scsih_add_device(ioc, handle, retry_count++, + 1)) { + ssleep(1); + } + printk(MPT2SAS_INFO_FMT "\tAFTER adding phys disk: " + " handle (0x%04x), sas_addr(0x%016llx)\n", + ioc->name, handle, (unsigned long long) + le64_to_cpu(sas_device_pg0.SASAddress)); } - if (sas_device->starget) - starget_printk(KERN_INFO, sas_device->starget, - "removing: handle(0x%04x), sas_addr(0x%016llx), " - "enclosure logical id(0x%016llx), slot(%d)\n", - sas_device->handle, - (unsigned long long)sas_device->sas_address, - (unsigned long long) - sas_device->enclosure_logical_id, - sas_device->slot); - /* invalidate the device handle */ - sas_device->handle = 0; - _scsih_remove_device(ioc, sas_device); } - list_for_each_entry_safe(raid_device, raid_device_next, - &ioc->raid_device_list, list) { - if (raid_device->responding) { - raid_device->responding = 0; + printk(MPT2SAS_INFO_FMT "\tscan devices: phys disk complete\n", + ioc->name); + + printk(MPT2SAS_INFO_FMT "\tscan devices: volumes start\n", ioc->name); + /* volumes */ + handle = 0xFFFF; + while (!(mpt2sas_config_get_raid_volume_pg1(ioc, &mpi_reply, + &volume_pg1, MPI2_RAID_VOLUME_PGAD_FORM_GET_NEXT_HANDLE, handle))) { + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + printk(MPT2SAS_INFO_FMT "\tbreak from volume scan: " + "ioc_status(0x%04x), loginfo(0x%08x)\n", + ioc->name, ioc_status, + le32_to_cpu(mpi_reply.IOCLogInfo)); + break; + } + handle = le16_to_cpu(volume_pg1.DevHandle); + spin_lock_irqsave(&ioc->raid_device_lock, flags); + raid_device = _scsih_raid_device_find_by_wwid(ioc, + le64_to_cpu(volume_pg1.WWID)); + spin_unlock_irqrestore(&ioc->raid_device_lock, flags); + if (raid_device) continue; + if (mpt2sas_config_get_raid_volume_pg0(ioc, &mpi_reply, + &volume_pg0, MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, handle, + sizeof(Mpi2RaidVolPage0_t))) + continue; + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + printk(MPT2SAS_INFO_FMT "\tbreak from volume scan: " + "ioc_status(0x%04x), loginfo(0x%08x)\n", + ioc->name, ioc_status, + le32_to_cpu(mpi_reply.IOCLogInfo)); + break; } - if (raid_device->starget) { - starget_printk(KERN_INFO, raid_device->starget, - "removing: handle(0x%04x), wwid(0x%016llx)\n", - raid_device->handle, - (unsigned long long)raid_device->wwid); - scsi_remove_target(&raid_device->starget->dev); + if (volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_OPTIMAL || + volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_ONLINE || + volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_DEGRADED) { + memset(&element, 0, sizeof(Mpi2EventIrConfigElement_t)); + element.ReasonCode = MPI2_EVENT_IR_CHANGE_RC_ADDED; + element.VolDevHandle = volume_pg1.DevHandle; + printk(MPT2SAS_INFO_FMT "\tBEFORE adding volume: " + " handle (0x%04x)\n", ioc->name, + volume_pg1.DevHandle); + _scsih_sas_volume_add(ioc, &element); + printk(MPT2SAS_INFO_FMT "\tAFTER adding volume: " + " handle (0x%04x)\n", ioc->name, + volume_pg1.DevHandle); } - _scsih_raid_device_remove(ioc, raid_device); } - retry_expander_search: - sas_expander = NULL; - list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) { - if (sas_expander->responding) { - sas_expander->responding = 0; + printk(MPT2SAS_INFO_FMT "\tscan devices: volumes complete\n", + ioc->name); + + skip_to_sas: + + printk(MPT2SAS_INFO_FMT "\tscan devices: end devices start\n", + ioc->name); + /* sas devices */ + handle = 0xFFFF; + while (!(mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, + &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE, + handle))) { + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + printk(MPT2SAS_INFO_FMT "\tbreak from end device scan:" + " ioc_status(0x%04x), loginfo(0x%08x)\n", + ioc->name, ioc_status, + le32_to_cpu(mpi_reply.IOCLogInfo)); + break; + } + handle = le16_to_cpu(sas_device_pg0.DevHandle); + if (!(_scsih_is_end_device( + le32_to_cpu(sas_device_pg0.DeviceInfo)))) + continue; + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, + le64_to_cpu(sas_device_pg0.SASAddress)); + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + if (sas_device) continue; + parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle); + if (!_scsih_get_sas_address(ioc, parent_handle, &sas_address)) { + printk(MPT2SAS_INFO_FMT "\tBEFORE adding end device: " + "handle (0x%04x), sas_addr(0x%016llx)\n", + ioc->name, handle, (unsigned long long) + le64_to_cpu(sas_device_pg0.SASAddress)); + mpt2sas_transport_update_links(ioc, sas_address, handle, + sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5); + retry_count = 0; + /* This will retry adding the end device. + * _scsih_add_device() will decide on retries and + * return "1" when it should be retried + */ + while (_scsih_add_device(ioc, handle, retry_count++, + 0)) { + ssleep(1); + } + printk(MPT2SAS_INFO_FMT "\tAFTER adding end device: " + "handle (0x%04x), sas_addr(0x%016llx)\n", + ioc->name, handle, (unsigned long long) + le64_to_cpu(sas_device_pg0.SASAddress)); } - _scsih_expander_remove(ioc, sas_expander->sas_address); - goto retry_expander_search; } + + printk(MPT2SAS_INFO_FMT "\tscan devices: end devices complete\n", + ioc->name); + + printk(MPT2SAS_INFO_FMT "scan devices: complete\n", ioc->name); } + /** * mpt2sas_scsih_reset_handler - reset callback handler (for scsih) * @ioc: per adapter object @@ -5592,33 +7346,41 @@ mpt2sas_scsih_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase) { switch (reset_phase) { case MPT2_IOC_PRE_RESET: - dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " + dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: " "MPT2_IOC_PRE_RESET\n", ioc->name, __func__)); - _scsih_fw_event_off(ioc); break; case MPT2_IOC_AFTER_RESET: - dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " + dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: " "MPT2_IOC_AFTER_RESET\n", ioc->name, __func__)); + if (ioc->scsih_cmds.status & MPT2_CMD_PENDING) { + ioc->scsih_cmds.status |= MPT2_CMD_RESET; + mpt2sas_base_free_smid(ioc, ioc->scsih_cmds.smid); + complete(&ioc->scsih_cmds.done); + } if (ioc->tm_cmds.status & MPT2_CMD_PENDING) { ioc->tm_cmds.status |= MPT2_CMD_RESET; mpt2sas_base_free_smid(ioc, ioc->tm_cmds.smid); complete(&ioc->tm_cmds.done); } - _scsih_fw_event_on(ioc); + _scsih_fw_event_cleanup_queue(ioc); _scsih_flush_running_cmds(ioc); break; case MPT2_IOC_DONE_RESET: - dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " + dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: " "MPT2_IOC_DONE_RESET\n", ioc->name, __func__)); _scsih_sas_host_refresh(ioc); + _scsih_prep_device_scan(ioc); _scsih_search_responding_sas_devices(ioc); _scsih_search_responding_raid_devices(ioc); _scsih_search_responding_expanders(ioc); - break; - case MPT2_IOC_RUNNING: - dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " - "MPT2_IOC_RUNNING\n", ioc->name, __func__)); - _scsih_remove_unresponding_devices(ioc); + if ((!ioc->is_driver_loading) && !(disable_discovery > 0 && + !ioc->sas_hba.num_phys)) { + _scsih_prep_device_scan(ioc); + _scsih_search_responding_sas_devices(ioc); + _scsih_search_responding_raid_devices(ioc); + _scsih_search_responding_expanders(ioc); + _scsih_error_recovery_delete_devices(ioc); + } break; } } @@ -5635,25 +7397,36 @@ static void _firmware_event_work(struct work_struct *work) { struct fw_event_work *fw_event = container_of(work, - struct fw_event_work, work); - unsigned long flags; + struct fw_event_work, delayed_work.work); struct MPT2SAS_ADAPTER *ioc = fw_event->ioc; /* the queue is being flushed so ignore this event */ - spin_lock_irqsave(&ioc->fw_event_lock, flags); - if (ioc->fw_events_off || ioc->remove_host) { - spin_unlock_irqrestore(&ioc->fw_event_lock, flags); + if (ioc->remove_host || fw_event->cancel_pending_work || + ioc->pci_error_recovery) { _scsih_fw_event_free(ioc, fw_event); return; } - spin_unlock_irqrestore(&ioc->fw_event_lock, flags); - - if (ioc->shost_recovery) { - _scsih_fw_event_requeue(ioc, fw_event, 1000); - return; - } switch (fw_event->event) { + case MPT2SAS_REMOVE_UNRESPONDING_DEVICES: + while (scsi_host_in_recovery(ioc->shost) || ioc->shost_recovery) + ssleep(1); + _scsih_remove_unresponding_sas_devices(ioc); + _scsih_scan_for_devices_after_reset(ioc); + break; + case MPT2SAS_PORT_ENABLE_COMPLETE: + ioc->start_scan = 0; + + if (missing_delay[0] != -1 && missing_delay[1] != -1) + mpt2sas_base_update_missing_delay(ioc, missing_delay[0], + missing_delay[1]); + + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "port enable: complete " + "from worker thread\n", ioc->name)); + break; + case MPT2SAS_TURN_ON_FAULT_LED: + _scsih_turn_on_fault_led(ioc, fw_event->device_handle); + break; case MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST: _scsih_sas_topology_change_event(ioc, fw_event); break; @@ -5666,7 +7439,7 @@ _firmware_event_work(struct work_struct *work) fw_event); break; case MPI2_EVENT_SAS_BROADCAST_PRIMITIVE: - _scsih_sas_broadcast_primative_event(ioc, + _scsih_sas_broadcast_primitive_event(ioc, fw_event); break; case MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE: @@ -5685,9 +7458,6 @@ _firmware_event_work(struct work_struct *work) case MPI2_EVENT_IR_OPERATION_STATUS: _scsih_sas_ir_operation_status_event(ioc, fw_event); break; - case MPI2_EVENT_TASK_SET_FULL: - _scsih_task_set_full(ioc, fw_event); - break; } _scsih_fw_event_free(ioc, fw_event); } @@ -5702,27 +7472,29 @@ _firmware_event_work(struct work_struct *work) * This function merely adds a new work task into ioc->firmware_event_thread. * The tasks are worked from _firmware_event_work in user context. * - * Return 1 meaning mf should be freed from _base_interrupt - * 0 means the mf is freed from this function. + * Returns void. */ -u8 +void mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, u32 reply) { struct fw_event_work *fw_event; Mpi2EventNotificationReply_t *mpi_reply; - unsigned long flags; u16 event; + u16 sz; /* events turned off due to host reset or driver unloading */ - spin_lock_irqsave(&ioc->fw_event_lock, flags); - if (ioc->fw_events_off || ioc->remove_host) { - spin_unlock_irqrestore(&ioc->fw_event_lock, flags); - return 1; - } - spin_unlock_irqrestore(&ioc->fw_event_lock, flags); + if (ioc->remove_host || ioc->pci_error_recovery) + return; mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); + + if (unlikely(!mpi_reply)) { + printk(MPT2SAS_ERR_FMT "mpi_reply not valid at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return; + } + event = le16_to_cpu(mpi_reply->Event); switch (event) { @@ -5734,10 +7506,14 @@ mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, mpi_reply->EventData; if (baen_data->Primitive != - MPI2_EVENT_PRIMITIVE_ASYNCHRONOUS_EVENT || - ioc->broadcast_aen_busy) - return 1; - ioc->broadcast_aen_busy = 1; + MPI2_EVENT_PRIMITIVE_ASYNCHRONOUS_EVENT) + return; + + if (ioc->broadcast_aen_busy) { + ioc->broadcast_aen_pending++; + return; + } else + ioc->broadcast_aen_busy = 1; break; } @@ -5746,44 +7522,97 @@ mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, (Mpi2EventDataSasTopologyChangeList_t *) mpi_reply->EventData); break; + case MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST: + _scsih_check_ir_config_unhide_events(ioc, + (Mpi2EventDataIrConfigChangeList_t *) + mpi_reply->EventData); + break; + case MPI2_EVENT_IR_VOLUME: + _scsih_check_volume_delete_events(ioc, + (Mpi2EventDataIrVolume_t *) + mpi_reply->EventData); + break; + case MPI2_EVENT_LOG_ENTRY_ADDED: + { + Mpi2EventDataLogEntryAdded_t *log_entry; + __le32 *log_code; + + if (!ioc->is_warpdrive) + break; + + log_entry = (Mpi2EventDataLogEntryAdded_t *) + mpi_reply->EventData; + log_code = (__le32 *)log_entry->LogData; + + if (le16_to_cpu(log_entry->LogEntryQualifier) + != MPT2_WARPDRIVE_LOGENTRY) + break; + + switch (le32_to_cpu(*log_code)) { + case MPT2_WARPDRIVE_LC_SSDT: + printk(MPT2SAS_WARN_FMT "WarpDrive Warning: " + "IO Throttling has occurred in the WarpDrive " + "subsystem. Check WarpDrive documentation for " + "additional details.\n", ioc->name); + break; + case MPT2_WARPDRIVE_LC_SSDLW: + printk(MPT2SAS_WARN_FMT "WarpDrive Warning: " + "Program/Erase Cycles for the WarpDrive subsystem " + "in degraded range. Check WarpDrive documentation " + "for additional details.\n", ioc->name); + break; + case MPT2_WARPDRIVE_LC_SSDLF: + printk(MPT2SAS_ERR_FMT "WarpDrive Fatal Error: " + "There are no Program/Erase Cycles for the " + "WarpDrive subsystem. The storage device will be " + "in read-only mode. Check WarpDrive documentation " + "for additional details.\n", ioc->name); + break; + case MPT2_WARPDRIVE_LC_BRMF: + printk(MPT2SAS_ERR_FMT "WarpDrive Fatal Error: " + "The Backup Rail Monitor has failed on the " + "WarpDrive subsystem. Check WarpDrive " + "documentation for additional details.\n", + ioc->name); + break; + } + break; + } case MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE: case MPI2_EVENT_IR_OPERATION_STATUS: case MPI2_EVENT_SAS_DISCOVERY: case MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE: - case MPI2_EVENT_IR_VOLUME: case MPI2_EVENT_IR_PHYSICAL_DISK: - case MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST: - case MPI2_EVENT_TASK_SET_FULL: break; default: /* ignore the rest */ - return 1; + return; } fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC); if (!fw_event) { printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, __func__); - return 1; + return; } - fw_event->event_data = - kzalloc(mpi_reply->EventDataLength*4, GFP_ATOMIC); + sz = le16_to_cpu(mpi_reply->EventDataLength) * 4; + fw_event->event_data = kzalloc(sz, GFP_ATOMIC); if (!fw_event->event_data) { printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, __func__); kfree(fw_event); - return 1; + return; } memcpy(fw_event->event_data, mpi_reply->EventData, - mpi_reply->EventDataLength*4); + sz); fw_event->ioc = ioc; fw_event->VF_ID = mpi_reply->VF_ID; fw_event->VP_ID = mpi_reply->VP_ID; fw_event->event = event; _scsih_fw_event_add(ioc, fw_event); - return 1; + return; } /* shost template */ @@ -5797,6 +7626,8 @@ static struct scsi_host_template scsih_driver_template = { .slave_configure = _scsih_slave_configure, .target_destroy = _scsih_target_destroy, .slave_destroy = _scsih_slave_destroy, + .scan_finished = _scsih_scan_finished, + .scan_start = _scsih_scan_start, .change_queue_depth = _scsih_change_queue_depth, .change_queue_type = _scsih_change_queue_type, .eh_abort_handler = _scsih_abort, @@ -5807,7 +7638,7 @@ static struct scsi_host_template scsih_driver_template = { .can_queue = 1, .this_id = -1, .sg_tablesize = MPT2SAS_SG_DEPTH, - .max_sectors = 8192, + .max_sectors = 32767, .cmd_per_lun = 7, .use_clustering = ENABLE_CLUSTERING, .shost_attrs = mpt2sas_host_attrs, @@ -5829,56 +7660,23 @@ static void _scsih_expander_node_remove(struct MPT2SAS_ADAPTER *ioc, struct _sas_node *sas_expander) { - struct _sas_port *mpt2sas_port; - struct _sas_device *sas_device; - struct _sas_node *expander_sibling; - unsigned long flags; - - if (!sas_expander) - return; + struct _sas_port *mpt2sas_port, *next; /* remove sibling ports attached to this expander */ - retry_device_search: - list_for_each_entry(mpt2sas_port, + list_for_each_entry_safe(mpt2sas_port, next, &sas_expander->sas_port_list, port_list) { + if (ioc->shost_recovery) + return; if (mpt2sas_port->remote_identify.device_type == - SAS_END_DEVICE) { - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = - mpt2sas_scsih_sas_device_find_by_sas_address(ioc, - mpt2sas_port->remote_identify.sas_address); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - if (!sas_device) - continue; - _scsih_remove_device(ioc, sas_device); - if (ioc->shost_recovery) - return; - goto retry_device_search; - } - } - - retry_expander_search: - list_for_each_entry(mpt2sas_port, - &sas_expander->sas_port_list, port_list) { - - if (mpt2sas_port->remote_identify.device_type == - MPI2_SAS_DEVICE_INFO_EDGE_EXPANDER || + SAS_END_DEVICE) + mpt2sas_device_remove_by_sas_address(ioc, + mpt2sas_port->remote_identify.sas_address); + else if (mpt2sas_port->remote_identify.device_type == + SAS_EDGE_EXPANDER_DEVICE || mpt2sas_port->remote_identify.device_type == - MPI2_SAS_DEVICE_INFO_FANOUT_EXPANDER) { - - spin_lock_irqsave(&ioc->sas_node_lock, flags); - expander_sibling = - mpt2sas_scsih_expander_find_by_sas_address( - ioc, mpt2sas_port->remote_identify.sas_address); - spin_unlock_irqrestore(&ioc->sas_node_lock, flags); - if (!expander_sibling) - continue; - _scsih_expander_remove(ioc, - expander_sibling->sas_address); - if (ioc->shost_recovery) - return; - goto retry_expander_search; - } + SAS_FANOUT_EXPANDER_DEVICE) + mpt2sas_expander_remove(ioc, + mpt2sas_port->remote_identify.sas_address); } mpt2sas_transport_port_remove(ioc, sas_expander->sas_address, @@ -5889,7 +7687,6 @@ _scsih_expander_node_remove(struct MPT2SAS_ADAPTER *ioc, sas_expander->handle, (unsigned long long) sas_expander->sas_address); - list_del(&sas_expander->list); kfree(sas_expander->phy); kfree(sas_expander); } @@ -5914,10 +7711,6 @@ _scsih_ir_shutdown(struct MPT2SAS_ADAPTER *ioc) if (!ioc->ir_firmware) return; - /* are there any volumes ? */ - if (list_empty(&ioc->raid_device_list)) - return; - mutex_lock(&ioc->scsih_cmds.mutex); if (ioc->scsih_cmds.status != MPT2_CMD_NOT_USED) { @@ -5942,7 +7735,8 @@ _scsih_ir_shutdown(struct MPT2SAS_ADAPTER *ioc) mpi_request->Function = MPI2_FUNCTION_RAID_ACTION; mpi_request->Action = MPI2_RAID_ACTION_SYSTEM_SHUTDOWN_INITIATED; - printk(MPT2SAS_INFO_FMT "IR shutdown (sending)\n", ioc->name); + if (!ioc->hide_ir_msg) + printk(MPT2SAS_INFO_FMT "IR shutdown (sending)\n", ioc->name); init_completion(&ioc->scsih_cmds.done); mpt2sas_base_put_smid_default(ioc, smid); wait_for_completion_timeout(&ioc->scsih_cmds.done, 10*HZ); @@ -5956,10 +7750,11 @@ _scsih_ir_shutdown(struct MPT2SAS_ADAPTER *ioc) if (ioc->scsih_cmds.status & MPT2_CMD_REPLY_VALID) { mpi_reply = ioc->scsih_cmds.reply; - printk(MPT2SAS_INFO_FMT "IR shutdown (complete): " - "ioc_status(0x%04x), loginfo(0x%08x)\n", - ioc->name, le16_to_cpu(mpi_reply->IOCStatus), - le32_to_cpu(mpi_reply->IOCLogInfo)); + if (!ioc->hide_ir_msg) + printk(MPT2SAS_INFO_FMT "IR shutdown (complete): " + "ioc_status(0x%04x), loginfo(0x%08x)\n", + ioc->name, le16_to_cpu(mpi_reply->IOCStatus), + le32_to_cpu(mpi_reply->IOCLogInfo)); } out: @@ -5978,6 +7773,18 @@ _scsih_shutdown(struct pci_dev *pdev) { struct Scsi_Host *shost = pci_get_drvdata(pdev); struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + struct workqueue_struct *wq; + unsigned long flags; + + ioc->remove_host = 1; + _scsih_fw_event_cleanup_queue(ioc); + + spin_lock_irqsave(&ioc->fw_event_lock, flags); + wq = ioc->firmware_event_thread; + ioc->firmware_event_thread = NULL; + spin_unlock_irqrestore(&ioc->fw_event_lock, flags); + if (wq) + destroy_workqueue(wq); _scsih_ir_shutdown(ioc); mpt2sas_base_detach(ioc); @@ -5990,19 +7797,19 @@ _scsih_shutdown(struct pci_dev *pdev) * Routine called when unloading the driver. * Return nothing. */ -static void __devexit +static void _scsih_remove(struct pci_dev *pdev) { struct Scsi_Host *shost = pci_get_drvdata(pdev); struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - struct _sas_port *mpt2sas_port; - struct _sas_device *sas_device; - struct _sas_node *expander_sibling; + struct _sas_port *mpt2sas_port, *next_port; + struct _raid_device *raid_device, *next; + struct MPT2SAS_TARGET *sas_target_priv_data; struct workqueue_struct *wq; unsigned long flags; ioc->remove_host = 1; - _scsih_fw_event_off(ioc); + _scsih_fw_event_cleanup_queue(ioc); spin_lock_irqsave(&ioc->fw_event_lock, flags); wq = ioc->firmware_event_thread; @@ -6011,29 +7818,35 @@ _scsih_remove(struct pci_dev *pdev) if (wq) destroy_workqueue(wq); + /* release all the volumes */ + _scsih_ir_shutdown(ioc); + list_for_each_entry_safe(raid_device, next, &ioc->raid_device_list, + list) { + if (raid_device->starget) { + sas_target_priv_data = + raid_device->starget->hostdata; + sas_target_priv_data->deleted = 1; + scsi_remove_target(&raid_device->starget->dev); + } + printk(MPT2SAS_INFO_FMT "removing handle(0x%04x), wwid" + "(0x%016llx)\n", ioc->name, raid_device->handle, + (unsigned long long) raid_device->wwid); + _scsih_raid_device_remove(ioc, raid_device); + } + /* free ports attached to the sas_host */ - retry_again: - list_for_each_entry(mpt2sas_port, + list_for_each_entry_safe(mpt2sas_port, next_port, &ioc->sas_hba.sas_port_list, port_list) { if (mpt2sas_port->remote_identify.device_type == - SAS_END_DEVICE) { - sas_device = - mpt2sas_scsih_sas_device_find_by_sas_address(ioc, - mpt2sas_port->remote_identify.sas_address); - if (sas_device) { - _scsih_remove_device(ioc, sas_device); - goto retry_again; - } - } else { - expander_sibling = - mpt2sas_scsih_expander_find_by_sas_address(ioc, + SAS_END_DEVICE) + mpt2sas_device_remove_by_sas_address(ioc, + mpt2sas_port->remote_identify.sas_address); + else if (mpt2sas_port->remote_identify.device_type == + SAS_EDGE_EXPANDER_DEVICE || + mpt2sas_port->remote_identify.device_type == + SAS_FANOUT_EXPANDER_DEVICE) + mpt2sas_expander_remove(ioc, mpt2sas_port->remote_identify.sas_address); - if (expander_sibling) { - _scsih_expander_remove(ioc, - expander_sibling->sas_address); - goto retry_again; - } - } } /* free phys attached to the sas_host */ @@ -6044,7 +7857,7 @@ _scsih_remove(struct pci_dev *pdev) } sas_remove_host(shost); - _scsih_shutdown(pdev); + mpt2sas_base_detach(ioc); list_del(&ioc->list); scsi_remove_host(shost); scsi_host_put(shost); @@ -6071,7 +7884,12 @@ _scsih_probe_boot_devices(struct MPT2SAS_ADAPTER *ioc) unsigned long flags; int rc; + /* no Bios, return immediately */ + if (!ioc->bios_pg3.BiosVersion) + return; + device = NULL; + is_raid = 0; if (ioc->req_boot_device.device) { device = ioc->req_boot_device.device; is_raid = ioc->req_boot_device.is_raid; @@ -6093,20 +7911,26 @@ _scsih_probe_boot_devices(struct MPT2SAS_ADAPTER *ioc) if (rc) _scsih_raid_device_remove(ioc, raid_device); } else { + spin_lock_irqsave(&ioc->sas_device_lock, flags); sas_device = device; handle = sas_device->handle; sas_address_parent = sas_device->sas_address_parent; sas_address = sas_device->sas_address; - spin_lock_irqsave(&ioc->sas_device_lock, flags); list_move_tail(&sas_device->list, &ioc->sas_device_list); spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + + if (ioc->hide_drives) + return; if (!mpt2sas_transport_port_add(ioc, sas_device->handle, sas_device->sas_address_parent)) { _scsih_sas_device_remove(ioc, sas_device); } else if (!sas_device->starget) { - mpt2sas_transport_port_remove(ioc, sas_address, - sas_address_parent); - _scsih_sas_device_remove(ioc, sas_device); + if (!ioc->is_driver_loading) { + mpt2sas_transport_port_remove(ioc, + sas_address, + sas_address_parent); + _scsih_sas_device_remove(ioc, sas_device); + } } } } @@ -6149,19 +7973,28 @@ _scsih_probe_sas(struct MPT2SAS_ADAPTER *ioc) /* SAS Device List */ list_for_each_entry_safe(sas_device, next, &ioc->sas_device_init_list, list) { - spin_lock_irqsave(&ioc->sas_device_lock, flags); - list_move_tail(&sas_device->list, &ioc->sas_device_list); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + + if (ioc->hide_drives) + continue; if (!mpt2sas_transport_port_add(ioc, sas_device->handle, sas_device->sas_address_parent)) { - _scsih_sas_device_remove(ioc, sas_device); + list_del(&sas_device->list); + kfree(sas_device); + continue; } else if (!sas_device->starget) { - mpt2sas_transport_port_remove(ioc, - sas_device->sas_address, - sas_device->sas_address_parent); - _scsih_sas_device_remove(ioc, sas_device); + if (!ioc->is_driver_loading) { + mpt2sas_transport_port_remove(ioc, + sas_device->sas_address, + sas_device->sas_address_parent); + list_del(&sas_device->list); + kfree(sas_device); + continue; + } } + spin_lock_irqsave(&ioc->sas_device_lock, flags); + list_move_tail(&sas_device->list, &ioc->sas_device_list); + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); } } @@ -6174,9 +8007,7 @@ _scsih_probe_sas(struct MPT2SAS_ADAPTER *ioc) static void _scsih_probe_devices(struct MPT2SAS_ADAPTER *ioc) { - u16 volume_mapping_flags = - le16_to_cpu(ioc->ioc_pg8.IRVolumeMappingFlags) & - MPI2_IOCPAGE8_IRFLAGS_MASK_VOLUME_MAPPING_MODE; + u16 volume_mapping_flags; if (!(ioc->facts.ProtocolFlags & MPI2_IOCFACTS_PROTOCOL_SCSI_INITIATOR)) return; /* return when IOC doesn't support initiator mode */ @@ -6184,18 +8015,102 @@ _scsih_probe_devices(struct MPT2SAS_ADAPTER *ioc) _scsih_probe_boot_devices(ioc); if (ioc->ir_firmware) { - if ((volume_mapping_flags & - MPI2_IOCPAGE8_IRFLAGS_HIGH_VOLUME_MAPPING)) { - _scsih_probe_sas(ioc); + volume_mapping_flags = + le16_to_cpu(ioc->ioc_pg8.IRVolumeMappingFlags) & + MPI2_IOCPAGE8_IRFLAGS_MASK_VOLUME_MAPPING_MODE; + if (volume_mapping_flags == + MPI2_IOCPAGE8_IRFLAGS_LOW_VOLUME_MAPPING) { _scsih_probe_raid(ioc); + _scsih_probe_sas(ioc); } else { - _scsih_probe_raid(ioc); _scsih_probe_sas(ioc); + _scsih_probe_raid(ioc); } } else _scsih_probe_sas(ioc); } + +/** + * _scsih_scan_start - scsi lld callback for .scan_start + * @shost: SCSI host pointer + * + * The shost has the ability to discover targets on its own instead + * of scanning the entire bus. In our implemention, we will kick off + * firmware discovery. + */ +static void +_scsih_scan_start(struct Scsi_Host *shost) +{ + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + int rc; + + if (diag_buffer_enable != -1 && diag_buffer_enable != 0) + mpt2sas_enable_diag_buffer(ioc, diag_buffer_enable); + + if (disable_discovery > 0) + return; + + ioc->start_scan = 1; + rc = mpt2sas_port_enable(ioc); + + if (rc != 0) + printk(MPT2SAS_INFO_FMT "port enable: FAILED\n", ioc->name); +} + +/** + * _scsih_scan_finished - scsi lld callback for .scan_finished + * @shost: SCSI host pointer + * @time: elapsed time of the scan in jiffies + * + * This function will be called periodically until it returns 1 with the + * scsi_host and the elapsed time of the scan in jiffies. In our implemention, + * we wait for firmware discovery to complete, then return 1. + */ +static int +_scsih_scan_finished(struct Scsi_Host *shost, unsigned long time) +{ + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + + if (disable_discovery > 0) { + ioc->is_driver_loading = 0; + ioc->wait_for_discovery_to_complete = 0; + return 1; + } + + if (time >= (300 * HZ)) { + ioc->base_cmds.status = MPT2_CMD_NOT_USED; + printk(MPT2SAS_INFO_FMT "port enable: FAILED with timeout " + "(timeout=300s)\n", ioc->name); + ioc->is_driver_loading = 0; + return 1; + } + + if (ioc->start_scan) + return 0; + + if (ioc->start_scan_failed) { + printk(MPT2SAS_INFO_FMT "port enable: FAILED with " + "(ioc_status=0x%08x)\n", ioc->name, ioc->start_scan_failed); + ioc->is_driver_loading = 0; + ioc->wait_for_discovery_to_complete = 0; + ioc->remove_host = 1; + return 1; + } + + printk(MPT2SAS_INFO_FMT "port enable: SUCCESS\n", ioc->name); + ioc->base_cmds.status = MPT2_CMD_NOT_USED; + + if (ioc->wait_for_discovery_to_complete) { + ioc->wait_for_discovery_to_complete = 0; + _scsih_probe_devices(ioc); + } + mpt2sas_base_start_watchdog(ioc); + ioc->is_driver_loading = 0; + return 1; +} + + /** * _scsih_probe - attach and add scsi host * @pdev: PCI device struct @@ -6223,17 +8138,26 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) ioc->id = mpt_ids++; sprintf(ioc->name, "%s%d", MPT2SAS_DRIVER_NAME, ioc->id); ioc->pdev = pdev; + if (id->device == MPI2_MFGPAGE_DEVID_SSS6200) { + ioc->is_warpdrive = 1; + ioc->hide_ir_msg = 1; + } else + ioc->mfg_pg10_hide_flag = MFG_PAGE10_EXPOSE_ALL_DISKS; ioc->scsi_io_cb_idx = scsi_io_cb_idx; ioc->tm_cb_idx = tm_cb_idx; ioc->ctl_cb_idx = ctl_cb_idx; ioc->base_cb_idx = base_cb_idx; + ioc->port_enable_cb_idx = port_enable_cb_idx; ioc->transport_cb_idx = transport_cb_idx; ioc->scsih_cb_idx = scsih_cb_idx; ioc->config_cb_idx = config_cb_idx; ioc->tm_tr_cb_idx = tm_tr_cb_idx; + ioc->tm_tr_volume_cb_idx = tm_tr_volume_cb_idx; ioc->tm_sas_control_cb_idx = tm_sas_control_cb_idx; ioc->logging_level = logging_level; + ioc->schedule_dead_ioc_flush_running_cmds = &_scsih_flush_running_cmds; /* misc semaphores and spin locks */ + mutex_init(&ioc->reset_in_progress_mutex); spin_lock_init(&ioc->ioc_reset_in_progress_lock); spin_lock_init(&ioc->scsi_lookup_lock); spin_lock_init(&ioc->sas_device_lock); @@ -6248,13 +8172,34 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) INIT_LIST_HEAD(&ioc->raid_device_list); INIT_LIST_HEAD(&ioc->sas_hba.sas_port_list); INIT_LIST_HEAD(&ioc->delayed_tr_list); + INIT_LIST_HEAD(&ioc->delayed_tr_volume_list); + INIT_LIST_HEAD(&ioc->reply_queue_list); /* init shost parameters */ - shost->max_cmd_len = 16; + shost->max_cmd_len = 32; shost->max_lun = max_lun; shost->transportt = mpt2sas_transport_template; shost->unique_id = ioc->id; + if (max_sectors != 0xFFFF) { + if (max_sectors < 64) { + shost->max_sectors = 64; + printk(MPT2SAS_WARN_FMT "Invalid value %d passed " + "for max_sectors, range is 64 to 32767. Assigning " + "value of 64.\n", ioc->name, max_sectors); + } else if (max_sectors > 32767) { + shost->max_sectors = 32767; + printk(MPT2SAS_WARN_FMT "Invalid value %d passed " + "for max_sectors, range is 64 to 8192. Assigning " + "default value of 32767.\n", ioc->name, + max_sectors); + } else { + shost->max_sectors = max_sectors & 0xFFFE; + printk(MPT2SAS_INFO_FMT "The max_sectors value is " + "set to %d\n", ioc->name, shost->max_sectors); + } + } + if ((scsi_add_host(shost, &pdev->dev))) { printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, __func__); @@ -6262,8 +8207,14 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto out_add_shost_fail; } - scsi_host_set_prot(shost, SHOST_DIF_TYPE1_PROTECTION - | SHOST_DIF_TYPE3_PROTECTION); + /* register EEDP capabilities with SCSI layer */ + if (prot_mask) + scsi_host_set_prot(shost, prot_mask); + else + scsi_host_set_prot(shost, SHOST_DIF_TYPE1_PROTECTION + | SHOST_DIF_TYPE2_PROTECTION + | SHOST_DIF_TYPE3_PROTECTION); + scsi_host_set_guard(shost, SHOST_DIX_GUARD_CRC); /* event thread */ @@ -6277,15 +8228,28 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto out_thread_fail; } - ioc->wait_for_port_enable_to_complete = 1; + ioc->is_driver_loading = 1; if ((mpt2sas_base_attach(ioc))) { printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, __func__); goto out_attach_fail; } - ioc->wait_for_port_enable_to_complete = 0; - _scsih_probe_devices(ioc); + if (ioc->is_warpdrive) { + if (ioc->mfg_pg10_hide_flag == MFG_PAGE10_EXPOSE_ALL_DISKS) + ioc->hide_drives = 0; + else if (ioc->mfg_pg10_hide_flag == MFG_PAGE10_HIDE_ALL_DISKS) + ioc->hide_drives = 1; + else { + if (_scsih_get_num_volumes(ioc)) + ioc->hide_drives = 1; + else + ioc->hide_drives = 0; + } + } else + ioc->hide_drives = 0; + scsi_scan_host(shost); + return 0; out_attach_fail: @@ -6294,6 +8258,7 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) list_del(&ioc->list); scsi_remove_host(shost); out_add_shost_fail: + scsi_host_put(shost); return -ENODEV; } @@ -6310,11 +8275,11 @@ _scsih_suspend(struct pci_dev *pdev, pm_message_t state) { struct Scsi_Host *shost = pci_get_drvdata(pdev); struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - u32 device_state; + pci_power_t device_state; mpt2sas_base_stop_watchdog(ioc); - flush_scheduled_work(); scsi_block_requests(shost); + _scsih_ir_shutdown(ioc); device_state = pci_choose_state(pdev, state); printk(MPT2SAS_INFO_FMT "pdev=0x%p, slot=%s, entering " "operating state [D%d]\n", ioc->name, pdev, @@ -6322,7 +8287,6 @@ _scsih_suspend(struct pci_dev *pdev, pm_message_t state) mpt2sas_base_free_resources(ioc); pci_save_state(pdev); - pci_disable_device(pdev); pci_set_power_state(pdev, device_state); return 0; } @@ -6338,7 +8302,7 @@ _scsih_resume(struct pci_dev *pdev) { struct Scsi_Host *shost = pci_get_drvdata(pdev); struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - u32 device_state = pdev->current_state; + pci_power_t device_state = pdev->current_state; int r; printk(MPT2SAS_INFO_FMT "pdev=0x%p, slot=%s, previous " @@ -6360,19 +8324,150 @@ _scsih_resume(struct pci_dev *pdev) } #endif /* CONFIG_PM */ +/** + * _scsih_pci_error_detected - Called when a PCI error is detected. + * @pdev: PCI device struct + * @state: PCI channel state + * + * Description: Called when a PCI error is detected. + * + * Return value: + * PCI_ERS_RESULT_NEED_RESET or PCI_ERS_RESULT_DISCONNECT + */ +static pci_ers_result_t +_scsih_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state) +{ + struct Scsi_Host *shost = pci_get_drvdata(pdev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + + printk(MPT2SAS_INFO_FMT "PCI error: detected callback, state(%d)!!\n", + ioc->name, state); + + switch (state) { + case pci_channel_io_normal: + return PCI_ERS_RESULT_CAN_RECOVER; + case pci_channel_io_frozen: + /* Fatal error, prepare for slot reset */ + ioc->pci_error_recovery = 1; + scsi_block_requests(ioc->shost); + mpt2sas_base_stop_watchdog(ioc); + mpt2sas_base_free_resources(ioc); + return PCI_ERS_RESULT_NEED_RESET; + case pci_channel_io_perm_failure: + /* Permanent error, prepare for device removal */ + ioc->pci_error_recovery = 1; + mpt2sas_base_stop_watchdog(ioc); + _scsih_flush_running_cmds(ioc); + return PCI_ERS_RESULT_DISCONNECT; + } + return PCI_ERS_RESULT_NEED_RESET; +} + +/** + * _scsih_pci_slot_reset - Called when PCI slot has been reset. + * @pdev: PCI device struct + * + * Description: This routine is called by the pci error recovery + * code after the PCI slot has been reset, just before we + * should resume normal operations. + */ +static pci_ers_result_t +_scsih_pci_slot_reset(struct pci_dev *pdev) +{ + struct Scsi_Host *shost = pci_get_drvdata(pdev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + int rc; + + printk(MPT2SAS_INFO_FMT "PCI error: slot reset callback!!\n", + ioc->name); + + ioc->pci_error_recovery = 0; + ioc->pdev = pdev; + pci_restore_state(pdev); + rc = mpt2sas_base_map_resources(ioc); + if (rc) + return PCI_ERS_RESULT_DISCONNECT; + + + rc = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, + FORCE_BIG_HAMMER); + + printk(MPT2SAS_WARN_FMT "hard reset: %s\n", ioc->name, + (rc == 0) ? "success" : "failed"); + + if (!rc) + return PCI_ERS_RESULT_RECOVERED; + else + return PCI_ERS_RESULT_DISCONNECT; +} + +/** + * _scsih_pci_resume() - resume normal ops after PCI reset + * @pdev: pointer to PCI device + * + * Called when the error recovery driver tells us that its + * OK to resume normal operation. Use completion to allow + * halted scsi ops to resume. + */ +static void +_scsih_pci_resume(struct pci_dev *pdev) +{ + struct Scsi_Host *shost = pci_get_drvdata(pdev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + + printk(MPT2SAS_INFO_FMT "PCI error: resume callback!!\n", ioc->name); + + pci_cleanup_aer_uncorrect_error_status(pdev); + mpt2sas_base_start_watchdog(ioc); + scsi_unblock_requests(ioc->shost); +} + +/** + * _scsih_pci_mmio_enabled - Enable MMIO and dump debug registers + * @pdev: pointer to PCI device + */ +static pci_ers_result_t +_scsih_pci_mmio_enabled(struct pci_dev *pdev) +{ + struct Scsi_Host *shost = pci_get_drvdata(pdev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + + printk(MPT2SAS_INFO_FMT "PCI error: mmio enabled callback!!\n", + ioc->name); + + /* TODO - dump whatever for debugging purposes */ + + /* Request a slot reset. */ + return PCI_ERS_RESULT_NEED_RESET; +} + +static const struct pci_error_handlers _scsih_err_handler = { + .error_detected = _scsih_pci_error_detected, + .mmio_enabled = _scsih_pci_mmio_enabled, + .slot_reset = _scsih_pci_slot_reset, + .resume = _scsih_pci_resume, +}; static struct pci_driver scsih_driver = { .name = MPT2SAS_DRIVER_NAME, .id_table = scsih_pci_table, .probe = _scsih_probe, - .remove = __devexit_p(_scsih_remove), + .remove = _scsih_remove, .shutdown = _scsih_shutdown, + .err_handler = &_scsih_err_handler, #ifdef CONFIG_PM .suspend = _scsih_suspend, .resume = _scsih_resume, #endif }; +/* raid transport support */ +static struct raid_function_template mpt2sas_raid_functions = { + .cookie = &scsih_driver_template, + .is_raid = _scsih_is_raid, + .get_resync = _scsih_get_resync, + .get_state = _scsih_get_state, +}; /** * _scsih_init - main entry point for this driver. @@ -6392,17 +8487,25 @@ _scsih_init(void) sas_attach_transport(&mpt2sas_transport_functions); if (!mpt2sas_transport_template) return -ENODEV; + /* raid transport support */ + mpt2sas_raid_template = raid_class_attach(&mpt2sas_raid_functions); + if (!mpt2sas_raid_template) { + sas_release_transport(mpt2sas_transport_template); + return -ENODEV; + } mpt2sas_base_initialize_callback_handler(); /* queuecommand callback hander */ scsi_io_cb_idx = mpt2sas_base_register_callback_handler(_scsih_io_done); - /* task managment callback handler */ + /* task management callback handler */ tm_cb_idx = mpt2sas_base_register_callback_handler(_scsih_tm_done); /* base internal commands callback handler */ base_cb_idx = mpt2sas_base_register_callback_handler(mpt2sas_base_done); + port_enable_cb_idx = mpt2sas_base_register_callback_handler( + mpt2sas_port_enable_done); /* transport internal commands callback handler */ transport_cb_idx = mpt2sas_base_register_callback_handler( @@ -6420,14 +8523,21 @@ _scsih_init(void) tm_tr_cb_idx = mpt2sas_base_register_callback_handler( _scsih_tm_tr_complete); + + tm_tr_volume_cb_idx = mpt2sas_base_register_callback_handler( + _scsih_tm_volume_tr_complete); + tm_sas_control_cb_idx = mpt2sas_base_register_callback_handler( _scsih_sas_control_complete); mpt2sas_ctl_init(); error = pci_register_driver(&scsih_driver); - if (error) + if (error) { + /* raid transport support */ + raid_class_release(mpt2sas_raid_template); sas_release_transport(mpt2sas_transport_template); + } return error; } @@ -6445,19 +8555,25 @@ _scsih_exit(void) pci_unregister_driver(&scsih_driver); - sas_release_transport(mpt2sas_transport_template); + mpt2sas_ctl_exit(); + mpt2sas_base_release_callback_handler(scsi_io_cb_idx); mpt2sas_base_release_callback_handler(tm_cb_idx); mpt2sas_base_release_callback_handler(base_cb_idx); + mpt2sas_base_release_callback_handler(port_enable_cb_idx); mpt2sas_base_release_callback_handler(transport_cb_idx); mpt2sas_base_release_callback_handler(scsih_cb_idx); mpt2sas_base_release_callback_handler(config_cb_idx); mpt2sas_base_release_callback_handler(ctl_cb_idx); mpt2sas_base_release_callback_handler(tm_tr_cb_idx); + mpt2sas_base_release_callback_handler(tm_tr_volume_cb_idx); mpt2sas_base_release_callback_handler(tm_sas_control_cb_idx); - mpt2sas_ctl_exit(); + /* raid transport support */ + raid_class_release(mpt2sas_raid_template); + sas_release_transport(mpt2sas_transport_template); + } module_init(_scsih_init); diff --git a/drivers/scsi/mpt2sas/mpt2sas_transport.c b/drivers/scsi/mpt2sas/mpt2sas_transport.c index 3a82872bad4..410f4a3e888 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_transport.c +++ b/drivers/scsi/mpt2sas/mpt2sas_transport.c @@ -2,7 +2,7 @@ * SAS Transport Layer for MPT (Message Passing Technology) based controllers * * This code is based on drivers/scsi/mpt2sas/mpt2_transport.c - * Copyright (C) 2007-2009 LSI Corporation + * Copyright (C) 2007-2013 LSI Corporation * (mailto:DL-MPTFusionLinux@lsi.com) * * This program is free software; you can redistribute it and/or @@ -49,6 +49,7 @@ #include <linux/workqueue.h> #include <linux/delay.h> #include <linux/pci.h> +#include <linux/slab.h> #include <scsi/scsi.h> #include <scsi/scsi_cmnd.h> @@ -139,7 +140,7 @@ _transport_set_identify(struct MPT2SAS_ADAPTER *ioc, u16 handle, u32 device_info; u32 ioc_status; - if (ioc->shost_recovery) { + if (ioc->shost_recovery || ioc->pci_error_recovery) { printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n", __func__, ioc->name); return -EFAULT; @@ -162,12 +163,15 @@ _transport_set_identify(struct MPT2SAS_ADAPTER *ioc, u16 handle, return -EIO; } - memset(identify, 0, sizeof(identify)); + memset(identify, 0, sizeof(struct sas_identify)); device_info = le32_to_cpu(sas_device_pg0.DeviceInfo); /* sas_address */ identify->sas_address = le64_to_cpu(sas_device_pg0.SASAddress); + /* phy number of the parent device this device is linked to */ + identify->phy_identifier = sas_device_pg0.PhyNum; + /* device_type */ switch (device_info & MPI2_SAS_DEVICE_INFO_MASK_DEVICE_TYPE) { case MPI2_SAS_DEVICE_INFO_NO_DEVICE: @@ -298,10 +302,9 @@ _transport_expander_report_manufacture(struct MPT2SAS_ADAPTER *ioc, void *data_out = NULL; dma_addr_t data_out_dma; u32 sz; - u64 *sas_address_le; u16 wait_state_count; - if (ioc->shost_recovery) { + if (ioc->shost_recovery || ioc->pci_error_recovery) { printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n", __func__, ioc->name); return -EFAULT; @@ -371,8 +374,7 @@ _transport_expander_report_manufacture(struct MPT2SAS_ADAPTER *ioc, mpi_request->PhysicalPort = 0xFF; mpi_request->VF_ID = 0; /* TODO */ mpi_request->VP_ID = 0; - sas_address_le = (u64 *)&mpi_request->SASAddress; - *sas_address_le = cpu_to_le64(sas_address); + mpi_request->SASAddress = cpu_to_le64(sas_address); mpi_request->RequestDataLength = cpu_to_le16(sizeof(struct rep_manu_request)); psge = &mpi_request->SGL; @@ -396,11 +398,11 @@ _transport_expander_report_manufacture(struct MPT2SAS_ADAPTER *ioc, sizeof(struct rep_manu_reply), data_out_dma + sizeof(struct rep_manu_request)); - dtransportprintk(ioc, printk(MPT2SAS_DEBUG_FMT "report_manufacture - " + dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT "report_manufacture - " "send to sas_addr(0x%016llx)\n", ioc->name, (unsigned long long)sas_address)); - mpt2sas_base_put_smid_default(ioc, smid); init_completion(&ioc->transport_cmds.done); + mpt2sas_base_put_smid_default(ioc, smid); timeleft = wait_for_completion_timeout(&ioc->transport_cmds.done, 10*HZ); @@ -414,7 +416,7 @@ _transport_expander_report_manufacture(struct MPT2SAS_ADAPTER *ioc, goto issue_host_reset; } - dtransportprintk(ioc, printk(MPT2SAS_DEBUG_FMT "report_manufacture - " + dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT "report_manufacture - " "complete\n", ioc->name)); if (ioc->transport_cmds.status & MPT2_CMD_REPLY_VALID) { @@ -422,7 +424,7 @@ _transport_expander_report_manufacture(struct MPT2SAS_ADAPTER *ioc, mpi_reply = ioc->transport_cmds.reply; - dtransportprintk(ioc, printk(MPT2SAS_DEBUG_FMT + dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT "report_manufacture - reply data transfer size(%d)\n", ioc->name, le16_to_cpu(mpi_reply->ResponseDataLength))); @@ -448,7 +450,7 @@ _transport_expander_report_manufacture(struct MPT2SAS_ADAPTER *ioc, manufacture_reply->component_revision_id; } } else - dtransportprintk(ioc, printk(MPT2SAS_DEBUG_FMT + dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT "report_manufacture - no reply\n", ioc->name)); issue_host_reset: @@ -465,6 +467,174 @@ _transport_expander_report_manufacture(struct MPT2SAS_ADAPTER *ioc, } /** + * _transport_delete_port - helper function to removing a port + * @ioc: per adapter object + * @mpt2sas_port: mpt2sas per port object + * + * Returns nothing. + */ +static void +_transport_delete_port(struct MPT2SAS_ADAPTER *ioc, + struct _sas_port *mpt2sas_port) +{ + u64 sas_address = mpt2sas_port->remote_identify.sas_address; + enum sas_device_type device_type = + mpt2sas_port->remote_identify.device_type; + + dev_printk(KERN_INFO, &mpt2sas_port->port->dev, + "remove: sas_addr(0x%016llx)\n", + (unsigned long long) sas_address); + + ioc->logging_level |= MPT_DEBUG_TRANSPORT; + if (device_type == SAS_END_DEVICE) + mpt2sas_device_remove_by_sas_address(ioc, sas_address); + else if (device_type == SAS_EDGE_EXPANDER_DEVICE || + device_type == SAS_FANOUT_EXPANDER_DEVICE) + mpt2sas_expander_remove(ioc, sas_address); + ioc->logging_level &= ~MPT_DEBUG_TRANSPORT; +} + +/** + * _transport_delete_phy - helper function to removing single phy from port + * @ioc: per adapter object + * @mpt2sas_port: mpt2sas per port object + * @mpt2sas_phy: mpt2sas per phy object + * + * Returns nothing. + */ +static void +_transport_delete_phy(struct MPT2SAS_ADAPTER *ioc, + struct _sas_port *mpt2sas_port, struct _sas_phy *mpt2sas_phy) +{ + u64 sas_address = mpt2sas_port->remote_identify.sas_address; + + dev_printk(KERN_INFO, &mpt2sas_phy->phy->dev, + "remove: sas_addr(0x%016llx), phy(%d)\n", + (unsigned long long) sas_address, mpt2sas_phy->phy_id); + + list_del(&mpt2sas_phy->port_siblings); + mpt2sas_port->num_phys--; + sas_port_delete_phy(mpt2sas_port->port, mpt2sas_phy->phy); + mpt2sas_phy->phy_belongs_to_port = 0; +} + +/** + * _transport_add_phy - helper function to adding single phy to port + * @ioc: per adapter object + * @mpt2sas_port: mpt2sas per port object + * @mpt2sas_phy: mpt2sas per phy object + * + * Returns nothing. + */ +static void +_transport_add_phy(struct MPT2SAS_ADAPTER *ioc, struct _sas_port *mpt2sas_port, + struct _sas_phy *mpt2sas_phy) +{ + u64 sas_address = mpt2sas_port->remote_identify.sas_address; + + dev_printk(KERN_INFO, &mpt2sas_phy->phy->dev, + "add: sas_addr(0x%016llx), phy(%d)\n", (unsigned long long) + sas_address, mpt2sas_phy->phy_id); + + list_add_tail(&mpt2sas_phy->port_siblings, &mpt2sas_port->phy_list); + mpt2sas_port->num_phys++; + sas_port_add_phy(mpt2sas_port->port, mpt2sas_phy->phy); + mpt2sas_phy->phy_belongs_to_port = 1; +} + +/** + * _transport_add_phy_to_an_existing_port - adding new phy to existing port + * @ioc: per adapter object + * @sas_node: sas node object (either expander or sas host) + * @mpt2sas_phy: mpt2sas per phy object + * @sas_address: sas address of device/expander were phy needs to be added to + * + * Returns nothing. + */ +static void +_transport_add_phy_to_an_existing_port(struct MPT2SAS_ADAPTER *ioc, +struct _sas_node *sas_node, struct _sas_phy *mpt2sas_phy, u64 sas_address) +{ + struct _sas_port *mpt2sas_port; + struct _sas_phy *phy_srch; + + if (mpt2sas_phy->phy_belongs_to_port == 1) + return; + + list_for_each_entry(mpt2sas_port, &sas_node->sas_port_list, + port_list) { + if (mpt2sas_port->remote_identify.sas_address != + sas_address) + continue; + list_for_each_entry(phy_srch, &mpt2sas_port->phy_list, + port_siblings) { + if (phy_srch == mpt2sas_phy) + return; + } + _transport_add_phy(ioc, mpt2sas_port, mpt2sas_phy); + return; + } + +} + +/** + * _transport_del_phy_from_an_existing_port - delete phy from existing port + * @ioc: per adapter object + * @sas_node: sas node object (either expander or sas host) + * @mpt2sas_phy: mpt2sas per phy object + * + * Returns nothing. + */ +static void +_transport_del_phy_from_an_existing_port(struct MPT2SAS_ADAPTER *ioc, + struct _sas_node *sas_node, struct _sas_phy *mpt2sas_phy) +{ + struct _sas_port *mpt2sas_port, *next; + struct _sas_phy *phy_srch; + + if (mpt2sas_phy->phy_belongs_to_port == 0) + return; + + list_for_each_entry_safe(mpt2sas_port, next, &sas_node->sas_port_list, + port_list) { + list_for_each_entry(phy_srch, &mpt2sas_port->phy_list, + port_siblings) { + if (phy_srch != mpt2sas_phy) + continue; + if (mpt2sas_port->num_phys == 1) + _transport_delete_port(ioc, mpt2sas_port); + else + _transport_delete_phy(ioc, mpt2sas_port, + mpt2sas_phy); + return; + } + } +} + +/** + * _transport_sanity_check - sanity check when adding a new port + * @ioc: per adapter object + * @sas_node: sas node object (either expander or sas host) + * @sas_address: sas address of device being added + * + * See the explanation above from _transport_delete_duplicate_port + */ +static void +_transport_sanity_check(struct MPT2SAS_ADAPTER *ioc, struct _sas_node *sas_node, + u64 sas_address) +{ + int i; + + for (i = 0; i < sas_node->num_phys; i++) { + if (sas_node->phy[i].remote_identify.sas_address != sas_address) + continue; + if (sas_node->phy[i].phy_belongs_to_port == 1) + _transport_del_phy_from_an_existing_port(ioc, sas_node, + &sas_node->phy[i]); + } +} + +/** * mpt2sas_transport_port_add - insert port to the list * @ioc: per adapter object * @handle: handle of attached device @@ -521,6 +691,9 @@ mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc, u16 handle, goto out_fail; } + _transport_sanity_check(ioc, sas_node, + mpt2sas_port->remote_identify.sas_address); + for (i = 0; i < sas_node->num_phys; i++) { if (sas_node->phy[i].remote_identify.sas_address != mpt2sas_port->remote_identify.sas_address) @@ -552,6 +725,7 @@ mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc, u16 handle, mpt2sas_port->remote_identify.sas_address, mpt2sas_phy->phy_id); sas_port_add_phy(port, mpt2sas_phy->phy); + mpt2sas_phy->phy_belongs_to_port = 1; } mpt2sas_port->port = port; @@ -621,9 +795,10 @@ mpt2sas_transport_port_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address, spin_lock_irqsave(&ioc->sas_node_lock, flags); sas_node = _transport_sas_node_find_by_sas_address(ioc, sas_address_parent); - spin_unlock_irqrestore(&ioc->sas_node_lock, flags); - if (!sas_node) + if (!sas_node) { + spin_unlock_irqrestore(&ioc->sas_node_lock, flags); return; + } list_for_each_entry_safe(mpt2sas_port, next, &sas_node->sas_port_list, port_list) { if (mpt2sas_port->remote_identify.sas_address != sas_address) @@ -633,8 +808,10 @@ mpt2sas_transport_port_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address, goto out; } out: - if (!found) + if (!found) { + spin_unlock_irqrestore(&ioc->sas_node_lock, flags); return; + } for (i = 0; i < sas_node->num_phys; i++) { if (sas_node->phy[i].remote_identify.sas_address == sas_address) @@ -642,6 +819,7 @@ mpt2sas_transport_port_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address, sizeof(struct sas_identify)); } + spin_unlock_irqrestore(&ioc->sas_node_lock, flags); list_for_each_entry_safe(mpt2sas_phy, next_phy, &mpt2sas_port->phy_list, port_siblings) { if ((ioc->logging_level & MPT_DEBUG_TRANSPORT)) @@ -650,6 +828,7 @@ mpt2sas_transport_port_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address, (unsigned long long) mpt2sas_port->remote_identify.sas_address, mpt2sas_phy->phy_id); + mpt2sas_phy->phy_belongs_to_port = 0; sas_port_delete_phy(mpt2sas_port->port, mpt2sas_phy->phy); list_del(&mpt2sas_phy->port_siblings); } @@ -809,23 +988,30 @@ mpt2sas_transport_update_links(struct MPT2SAS_ADAPTER *ioc, struct _sas_node *sas_node; struct _sas_phy *mpt2sas_phy; - if (ioc->shost_recovery) + if (ioc->shost_recovery || ioc->pci_error_recovery) return; spin_lock_irqsave(&ioc->sas_node_lock, flags); sas_node = _transport_sas_node_find_by_sas_address(ioc, sas_address); - spin_unlock_irqrestore(&ioc->sas_node_lock, flags); - if (!sas_node) + if (!sas_node) { + spin_unlock_irqrestore(&ioc->sas_node_lock, flags); return; + } mpt2sas_phy = &sas_node->phy[phy_number]; mpt2sas_phy->attached_handle = handle; - if (handle && (link_rate >= MPI2_SAS_NEG_LINK_RATE_1_5)) + spin_unlock_irqrestore(&ioc->sas_node_lock, flags); + if (handle && (link_rate >= MPI2_SAS_NEG_LINK_RATE_1_5)) { _transport_set_identify(ioc, handle, &mpt2sas_phy->remote_identify); - else + _transport_add_phy_to_an_existing_port(ioc, sas_node, + mpt2sas_phy, mpt2sas_phy->remote_identify.sas_address); + } else { memset(&mpt2sas_phy->remote_identify, 0 , sizeof(struct sas_identify)); + _transport_del_phy_from_an_existing_port(ioc, sas_node, + mpt2sas_phy); + } if (mpt2sas_phy->phy) mpt2sas_phy->phy->negotiated_linkrate = @@ -855,11 +1041,228 @@ rphy_to_ioc(struct sas_rphy *rphy) return shost_priv(shost); } + +/* report phy error log structure */ +struct phy_error_log_request{ + u8 smp_frame_type; /* 0x40 */ + u8 function; /* 0x11 */ + u8 allocated_response_length; + u8 request_length; /* 02 */ + u8 reserved_1[5]; + u8 phy_identifier; + u8 reserved_2[2]; +}; + +/* report phy error log reply structure */ +struct phy_error_log_reply{ + u8 smp_frame_type; /* 0x41 */ + u8 function; /* 0x11 */ + u8 function_result; + u8 response_length; + __be16 expander_change_count; + u8 reserved_1[3]; + u8 phy_identifier; + u8 reserved_2[2]; + __be32 invalid_dword; + __be32 running_disparity_error; + __be32 loss_of_dword_sync; + __be32 phy_reset_problem; +}; + /** - * _transport_get_linkerrors - + * _transport_get_expander_phy_error_log - return expander counters + * @ioc: per adapter object + * @phy: The sas phy object + * + * Returns 0 for success, non-zero for failure. + * + */ +static int +_transport_get_expander_phy_error_log(struct MPT2SAS_ADAPTER *ioc, + struct sas_phy *phy) +{ + Mpi2SmpPassthroughRequest_t *mpi_request; + Mpi2SmpPassthroughReply_t *mpi_reply; + struct phy_error_log_request *phy_error_log_request; + struct phy_error_log_reply *phy_error_log_reply; + int rc; + u16 smid; + u32 ioc_state; + unsigned long timeleft; + void *psge; + u32 sgl_flags; + u8 issue_reset = 0; + void *data_out = NULL; + dma_addr_t data_out_dma; + u32 sz; + u16 wait_state_count; + + if (ioc->shost_recovery || ioc->pci_error_recovery) { + printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n", + __func__, ioc->name); + return -EFAULT; + } + + mutex_lock(&ioc->transport_cmds.mutex); + + if (ioc->transport_cmds.status != MPT2_CMD_NOT_USED) { + printk(MPT2SAS_ERR_FMT "%s: transport_cmds in use\n", + ioc->name, __func__); + rc = -EAGAIN; + goto out; + } + ioc->transport_cmds.status = MPT2_CMD_PENDING; + + wait_state_count = 0; + ioc_state = mpt2sas_base_get_iocstate(ioc, 1); + while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { + if (wait_state_count++ == 10) { + printk(MPT2SAS_ERR_FMT + "%s: failed due to ioc not operational\n", + ioc->name, __func__); + rc = -EFAULT; + goto out; + } + ssleep(1); + ioc_state = mpt2sas_base_get_iocstate(ioc, 1); + printk(MPT2SAS_INFO_FMT "%s: waiting for " + "operational state(count=%d)\n", ioc->name, + __func__, wait_state_count); + } + if (wait_state_count) + printk(MPT2SAS_INFO_FMT "%s: ioc is operational\n", + ioc->name, __func__); + + smid = mpt2sas_base_get_smid(ioc, ioc->transport_cb_idx); + if (!smid) { + printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", + ioc->name, __func__); + rc = -EAGAIN; + goto out; + } + + mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); + ioc->transport_cmds.smid = smid; + + sz = sizeof(struct phy_error_log_request) + + sizeof(struct phy_error_log_reply); + data_out = pci_alloc_consistent(ioc->pdev, sz, &data_out_dma); + if (!data_out) { + printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, + __LINE__, __func__); + rc = -ENOMEM; + mpt2sas_base_free_smid(ioc, smid); + goto out; + } + + rc = -EINVAL; + memset(data_out, 0, sz); + phy_error_log_request = data_out; + phy_error_log_request->smp_frame_type = 0x40; + phy_error_log_request->function = 0x11; + phy_error_log_request->request_length = 2; + phy_error_log_request->allocated_response_length = 0; + phy_error_log_request->phy_identifier = phy->number; + + memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t)); + mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH; + mpi_request->PhysicalPort = 0xFF; + mpi_request->VF_ID = 0; /* TODO */ + mpi_request->VP_ID = 0; + mpi_request->SASAddress = cpu_to_le64(phy->identify.sas_address); + mpi_request->RequestDataLength = + cpu_to_le16(sizeof(struct phy_error_log_request)); + psge = &mpi_request->SGL; + + /* WRITE sgel first */ + sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT | + MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_HOST_TO_IOC); + sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; + ioc->base_add_sg_single(psge, sgl_flags | + sizeof(struct phy_error_log_request), data_out_dma); + + /* incr sgel */ + psge += ioc->sge_size; + + /* READ sgel last */ + sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT | + MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER | + MPI2_SGE_FLAGS_END_OF_LIST); + sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; + ioc->base_add_sg_single(psge, sgl_flags | + sizeof(struct phy_error_log_reply), data_out_dma + + sizeof(struct phy_error_log_request)); + + dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT "phy_error_log - " + "send to sas_addr(0x%016llx), phy(%d)\n", ioc->name, + (unsigned long long)phy->identify.sas_address, phy->number)); + init_completion(&ioc->transport_cmds.done); + mpt2sas_base_put_smid_default(ioc, smid); + timeleft = wait_for_completion_timeout(&ioc->transport_cmds.done, + 10*HZ); + + if (!(ioc->transport_cmds.status & MPT2_CMD_COMPLETE)) { + printk(MPT2SAS_ERR_FMT "%s: timeout\n", + ioc->name, __func__); + _debug_dump_mf(mpi_request, + sizeof(Mpi2SmpPassthroughRequest_t)/4); + if (!(ioc->transport_cmds.status & MPT2_CMD_RESET)) + issue_reset = 1; + goto issue_host_reset; + } + + dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT "phy_error_log - " + "complete\n", ioc->name)); + + if (ioc->transport_cmds.status & MPT2_CMD_REPLY_VALID) { + + mpi_reply = ioc->transport_cmds.reply; + + dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT + "phy_error_log - reply data transfer size(%d)\n", + ioc->name, le16_to_cpu(mpi_reply->ResponseDataLength))); + + if (le16_to_cpu(mpi_reply->ResponseDataLength) != + sizeof(struct phy_error_log_reply)) + goto out; + + phy_error_log_reply = data_out + + sizeof(struct phy_error_log_request); + + dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT + "phy_error_log - function_result(%d)\n", + ioc->name, phy_error_log_reply->function_result)); + + phy->invalid_dword_count = + be32_to_cpu(phy_error_log_reply->invalid_dword); + phy->running_disparity_error_count = + be32_to_cpu(phy_error_log_reply->running_disparity_error); + phy->loss_of_dword_sync_count = + be32_to_cpu(phy_error_log_reply->loss_of_dword_sync); + phy->phy_reset_problem_count = + be32_to_cpu(phy_error_log_reply->phy_reset_problem); + rc = 0; + } else + dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT + "phy_error_log - no reply\n", ioc->name)); + + issue_host_reset: + if (issue_reset) + mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, + FORCE_BIG_HAMMER); + out: + ioc->transport_cmds.status = MPT2_CMD_NOT_USED; + if (data_out) + pci_free_consistent(ioc->pdev, sz, data_out, data_out_dma); + + mutex_unlock(&ioc->transport_cmds.mutex); + return rc; +} + +/** + * _transport_get_linkerrors - return phy counters for both hba and expanders * @phy: The sas phy object * - * Only support sas_host direct attached phys. * Returns 0 for success, non-zero for failure. * */ @@ -867,23 +1270,24 @@ static int _transport_get_linkerrors(struct sas_phy *phy) { struct MPT2SAS_ADAPTER *ioc = phy_to_ioc(phy); - struct _sas_phy *mpt2sas_phy; + unsigned long flags; Mpi2ConfigReply_t mpi_reply; Mpi2SasPhyPage1_t phy_pg1; - int i; - for (i = 0, mpt2sas_phy = NULL; i < ioc->sas_hba.num_phys && - !mpt2sas_phy; i++) { - if (ioc->sas_hba.phy[i].phy != phy) - continue; - mpt2sas_phy = &ioc->sas_hba.phy[i]; + spin_lock_irqsave(&ioc->sas_node_lock, flags); + if (_transport_sas_node_find_by_sas_address(ioc, + phy->identify.sas_address) == NULL) { + spin_unlock_irqrestore(&ioc->sas_node_lock, flags); + return -EINVAL; } + spin_unlock_irqrestore(&ioc->sas_node_lock, flags); - if (!mpt2sas_phy) /* this phy not on sas_host */ - return -EINVAL; + if (phy->identify.sas_address != ioc->sas_hba.sas_address) + return _transport_get_expander_phy_error_log(ioc, phy); + /* get hba phy error logs */ if ((mpt2sas_config_get_phy_pg1(ioc, &mpi_reply, &phy_pg1, - mpt2sas_phy->phy_id))) { + phy->number))) { printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, __func__); return -ENXIO; @@ -892,8 +1296,7 @@ _transport_get_linkerrors(struct sas_phy *phy) if (mpi_reply.IOCStatus || mpi_reply.IOCLogInfo) printk(MPT2SAS_INFO_FMT "phy(%d), ioc_status" "(0x%04x), loginfo(0x%08x)\n", ioc->name, - mpt2sas_phy->phy_id, - le16_to_cpu(mpi_reply.IOCStatus), + phy->number, le16_to_cpu(mpi_reply.IOCStatus), le32_to_cpu(mpi_reply.IOCLogInfo)); phy->invalid_dword_count = le32_to_cpu(phy_pg1.InvalidDwordCount); @@ -917,19 +1320,22 @@ static int _transport_get_enclosure_identifier(struct sas_rphy *rphy, u64 *identifier) { struct MPT2SAS_ADAPTER *ioc = rphy_to_ioc(rphy); - struct _sas_node *sas_expander; + struct _sas_device *sas_device; unsigned long flags; + int rc; - spin_lock_irqsave(&ioc->sas_node_lock, flags); - sas_expander = mpt2sas_scsih_expander_find_by_sas_address(ioc, + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, rphy->identify.sas_address); - spin_unlock_irqrestore(&ioc->sas_node_lock, flags); - - if (!sas_expander) - return -ENXIO; - - *identifier = sas_expander->enclosure_logical_id; - return 0; + if (sas_device) { + *identifier = sas_device->enclosure_logical_id; + rc = 0; + } else { + *identifier = 0; + rc = -ENXIO; + } + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + return rc; } /** @@ -944,16 +1350,235 @@ _transport_get_bay_identifier(struct sas_rphy *rphy) struct MPT2SAS_ADAPTER *ioc = rphy_to_ioc(rphy); struct _sas_device *sas_device; unsigned long flags; + int rc; spin_lock_irqsave(&ioc->sas_device_lock, flags); sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, rphy->identify.sas_address); + if (sas_device) + rc = sas_device->slot; + else + rc = -ENXIO; spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + return rc; +} - if (!sas_device) - return -ENXIO; +/* phy control request structure */ +struct phy_control_request{ + u8 smp_frame_type; /* 0x40 */ + u8 function; /* 0x91 */ + u8 allocated_response_length; + u8 request_length; /* 0x09 */ + u16 expander_change_count; + u8 reserved_1[3]; + u8 phy_identifier; + u8 phy_operation; + u8 reserved_2[13]; + u64 attached_device_name; + u8 programmed_min_physical_link_rate; + u8 programmed_max_physical_link_rate; + u8 reserved_3[6]; +}; + +/* phy control reply structure */ +struct phy_control_reply{ + u8 smp_frame_type; /* 0x41 */ + u8 function; /* 0x11 */ + u8 function_result; + u8 response_length; +}; + +#define SMP_PHY_CONTROL_LINK_RESET (0x01) +#define SMP_PHY_CONTROL_HARD_RESET (0x02) +#define SMP_PHY_CONTROL_DISABLE (0x03) + +/** + * _transport_expander_phy_control - expander phy control + * @ioc: per adapter object + * @phy: The sas phy object + * + * Returns 0 for success, non-zero for failure. + * + */ +static int +_transport_expander_phy_control(struct MPT2SAS_ADAPTER *ioc, + struct sas_phy *phy, u8 phy_operation) +{ + Mpi2SmpPassthroughRequest_t *mpi_request; + Mpi2SmpPassthroughReply_t *mpi_reply; + struct phy_control_request *phy_control_request; + struct phy_control_reply *phy_control_reply; + int rc; + u16 smid; + u32 ioc_state; + unsigned long timeleft; + void *psge; + u32 sgl_flags; + u8 issue_reset = 0; + void *data_out = NULL; + dma_addr_t data_out_dma; + u32 sz; + u16 wait_state_count; + + if (ioc->shost_recovery) { + printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n", + __func__, ioc->name); + return -EFAULT; + } + + mutex_lock(&ioc->transport_cmds.mutex); + + if (ioc->transport_cmds.status != MPT2_CMD_NOT_USED) { + printk(MPT2SAS_ERR_FMT "%s: transport_cmds in use\n", + ioc->name, __func__); + rc = -EAGAIN; + goto out; + } + ioc->transport_cmds.status = MPT2_CMD_PENDING; + + wait_state_count = 0; + ioc_state = mpt2sas_base_get_iocstate(ioc, 1); + while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { + if (wait_state_count++ == 10) { + printk(MPT2SAS_ERR_FMT + "%s: failed due to ioc not operational\n", + ioc->name, __func__); + rc = -EFAULT; + goto out; + } + ssleep(1); + ioc_state = mpt2sas_base_get_iocstate(ioc, 1); + printk(MPT2SAS_INFO_FMT "%s: waiting for " + "operational state(count=%d)\n", ioc->name, + __func__, wait_state_count); + } + if (wait_state_count) + printk(MPT2SAS_INFO_FMT "%s: ioc is operational\n", + ioc->name, __func__); + + smid = mpt2sas_base_get_smid(ioc, ioc->transport_cb_idx); + if (!smid) { + printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", + ioc->name, __func__); + rc = -EAGAIN; + goto out; + } + + mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); + ioc->transport_cmds.smid = smid; + + sz = sizeof(struct phy_control_request) + + sizeof(struct phy_control_reply); + data_out = pci_alloc_consistent(ioc->pdev, sz, &data_out_dma); + if (!data_out) { + printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, + __LINE__, __func__); + rc = -ENOMEM; + mpt2sas_base_free_smid(ioc, smid); + goto out; + } - return sas_device->slot; + rc = -EINVAL; + memset(data_out, 0, sz); + phy_control_request = data_out; + phy_control_request->smp_frame_type = 0x40; + phy_control_request->function = 0x91; + phy_control_request->request_length = 9; + phy_control_request->allocated_response_length = 0; + phy_control_request->phy_identifier = phy->number; + phy_control_request->phy_operation = phy_operation; + phy_control_request->programmed_min_physical_link_rate = + phy->minimum_linkrate << 4; + phy_control_request->programmed_max_physical_link_rate = + phy->maximum_linkrate << 4; + + memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t)); + mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH; + mpi_request->PhysicalPort = 0xFF; + mpi_request->VF_ID = 0; /* TODO */ + mpi_request->VP_ID = 0; + mpi_request->SASAddress = cpu_to_le64(phy->identify.sas_address); + mpi_request->RequestDataLength = + cpu_to_le16(sizeof(struct phy_error_log_request)); + psge = &mpi_request->SGL; + + /* WRITE sgel first */ + sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT | + MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_HOST_TO_IOC); + sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; + ioc->base_add_sg_single(psge, sgl_flags | + sizeof(struct phy_control_request), data_out_dma); + + /* incr sgel */ + psge += ioc->sge_size; + + /* READ sgel last */ + sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT | + MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER | + MPI2_SGE_FLAGS_END_OF_LIST); + sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; + ioc->base_add_sg_single(psge, sgl_flags | + sizeof(struct phy_control_reply), data_out_dma + + sizeof(struct phy_control_request)); + + dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT "phy_control - " + "send to sas_addr(0x%016llx), phy(%d), opcode(%d)\n", ioc->name, + (unsigned long long)phy->identify.sas_address, phy->number, + phy_operation)); + + init_completion(&ioc->transport_cmds.done); + mpt2sas_base_put_smid_default(ioc, smid); + timeleft = wait_for_completion_timeout(&ioc->transport_cmds.done, + 10*HZ); + + if (!(ioc->transport_cmds.status & MPT2_CMD_COMPLETE)) { + printk(MPT2SAS_ERR_FMT "%s: timeout\n", + ioc->name, __func__); + _debug_dump_mf(mpi_request, + sizeof(Mpi2SmpPassthroughRequest_t)/4); + if (!(ioc->transport_cmds.status & MPT2_CMD_RESET)) + issue_reset = 1; + goto issue_host_reset; + } + + dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT "phy_control - " + "complete\n", ioc->name)); + + if (ioc->transport_cmds.status & MPT2_CMD_REPLY_VALID) { + + mpi_reply = ioc->transport_cmds.reply; + + dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT + "phy_control - reply data transfer size(%d)\n", + ioc->name, le16_to_cpu(mpi_reply->ResponseDataLength))); + + if (le16_to_cpu(mpi_reply->ResponseDataLength) != + sizeof(struct phy_control_reply)) + goto out; + + phy_control_reply = data_out + + sizeof(struct phy_control_request); + + dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT + "phy_control - function_result(%d)\n", + ioc->name, phy_control_reply->function_result)); + + rc = 0; + } else + dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT + "phy_control - no reply\n", ioc->name)); + + issue_host_reset: + if (issue_reset) + mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, + FORCE_BIG_HAMMER); + out: + ioc->transport_cmds.status = MPT2_CMD_NOT_USED; + if (data_out) + pci_free_consistent(ioc->pdev, sz, data_out, data_out_dma); + + mutex_unlock(&ioc->transport_cmds.mutex); + return rc; } /** @@ -961,33 +1586,36 @@ _transport_get_bay_identifier(struct sas_rphy *rphy) * @phy: The sas phy object * @hard_reset: * - * Only support sas_host direct attached phys. * Returns 0 for success, non-zero for failure. */ static int _transport_phy_reset(struct sas_phy *phy, int hard_reset) { struct MPT2SAS_ADAPTER *ioc = phy_to_ioc(phy); - struct _sas_phy *mpt2sas_phy; Mpi2SasIoUnitControlReply_t mpi_reply; Mpi2SasIoUnitControlRequest_t mpi_request; - int i; + unsigned long flags; - for (i = 0, mpt2sas_phy = NULL; i < ioc->sas_hba.num_phys && - !mpt2sas_phy; i++) { - if (ioc->sas_hba.phy[i].phy != phy) - continue; - mpt2sas_phy = &ioc->sas_hba.phy[i]; + spin_lock_irqsave(&ioc->sas_node_lock, flags); + if (_transport_sas_node_find_by_sas_address(ioc, + phy->identify.sas_address) == NULL) { + spin_unlock_irqrestore(&ioc->sas_node_lock, flags); + return -EINVAL; } + spin_unlock_irqrestore(&ioc->sas_node_lock, flags); - if (!mpt2sas_phy) /* this phy not on sas_host */ - return -EINVAL; + /* handle expander phys */ + if (phy->identify.sas_address != ioc->sas_hba.sas_address) + return _transport_expander_phy_control(ioc, phy, + (hard_reset == 1) ? SMP_PHY_CONTROL_HARD_RESET : + SMP_PHY_CONTROL_LINK_RESET); + /* handle hba phys */ memset(&mpi_request, 0, sizeof(Mpi2SasIoUnitControlReply_t)); mpi_request.Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL; mpi_request.Operation = hard_reset ? MPI2_SAS_OP_PHY_HARD_RESET : MPI2_SAS_OP_PHY_LINK_RESET; - mpi_request.PhyNum = mpt2sas_phy->phy_id; + mpi_request.PhyNum = phy->number; if ((mpt2sas_base_sas_iounit_control(ioc, &mpi_reply, &mpi_request))) { printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", @@ -998,14 +1626,265 @@ _transport_phy_reset(struct sas_phy *phy, int hard_reset) if (mpi_reply.IOCStatus || mpi_reply.IOCLogInfo) printk(MPT2SAS_INFO_FMT "phy(%d), ioc_status" "(0x%04x), loginfo(0x%08x)\n", ioc->name, - mpt2sas_phy->phy_id, - le16_to_cpu(mpi_reply.IOCStatus), + phy->number, le16_to_cpu(mpi_reply.IOCStatus), le32_to_cpu(mpi_reply.IOCLogInfo)); return 0; } /** + * _transport_phy_enable - enable/disable phys + * @phy: The sas phy object + * @enable: enable phy when true + * + * Only support sas_host direct attached phys. + * Returns 0 for success, non-zero for failure. + */ +static int +_transport_phy_enable(struct sas_phy *phy, int enable) +{ + struct MPT2SAS_ADAPTER *ioc = phy_to_ioc(phy); + Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL; + Mpi2SasIOUnitPage0_t *sas_iounit_pg0 = NULL; + Mpi2ConfigReply_t mpi_reply; + u16 ioc_status; + u16 sz; + int rc = 0; + unsigned long flags; + int i, discovery_active; + + spin_lock_irqsave(&ioc->sas_node_lock, flags); + if (_transport_sas_node_find_by_sas_address(ioc, + phy->identify.sas_address) == NULL) { + spin_unlock_irqrestore(&ioc->sas_node_lock, flags); + return -EINVAL; + } + spin_unlock_irqrestore(&ioc->sas_node_lock, flags); + + /* handle expander phys */ + if (phy->identify.sas_address != ioc->sas_hba.sas_address) + return _transport_expander_phy_control(ioc, phy, + (enable == 1) ? SMP_PHY_CONTROL_LINK_RESET : + SMP_PHY_CONTROL_DISABLE); + + /* handle hba phys */ + + /* read sas_iounit page 0 */ + sz = offsetof(Mpi2SasIOUnitPage0_t, PhyData) + (ioc->sas_hba.num_phys * + sizeof(Mpi2SasIOUnit0PhyData_t)); + sas_iounit_pg0 = kzalloc(sz, GFP_KERNEL); + if (!sas_iounit_pg0) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + rc = -ENOMEM; + goto out; + } + if ((mpt2sas_config_get_sas_iounit_pg0(ioc, &mpi_reply, + sas_iounit_pg0, sz))) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + rc = -ENXIO; + goto out; + } + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + rc = -EIO; + goto out; + } + + /* unable to enable/disable phys when when discovery is active */ + for (i = 0, discovery_active = 0; i < ioc->sas_hba.num_phys ; i++) { + if (sas_iounit_pg0->PhyData[i].PortFlags & + MPI2_SASIOUNIT0_PORTFLAGS_DISCOVERY_IN_PROGRESS) { + printk(MPT2SAS_ERR_FMT "discovery is active on " + "port = %d, phy = %d: unable to enable/disable " + "phys, try again later!\n", ioc->name, + sas_iounit_pg0->PhyData[i].Port, i); + discovery_active = 1; + } + } + + if (discovery_active) { + rc = -EAGAIN; + goto out; + } + + /* read sas_iounit page 1 */ + sz = offsetof(Mpi2SasIOUnitPage1_t, PhyData) + (ioc->sas_hba.num_phys * + sizeof(Mpi2SasIOUnit1PhyData_t)); + sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL); + if (!sas_iounit_pg1) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + rc = -ENOMEM; + goto out; + } + if ((mpt2sas_config_get_sas_iounit_pg1(ioc, &mpi_reply, + sas_iounit_pg1, sz))) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + rc = -ENXIO; + goto out; + } + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + rc = -EIO; + goto out; + } + /* copy Port/PortFlags/PhyFlags from page 0 */ + for (i = 0; i < ioc->sas_hba.num_phys ; i++) { + sas_iounit_pg1->PhyData[i].Port = + sas_iounit_pg0->PhyData[i].Port; + sas_iounit_pg1->PhyData[i].PortFlags = + (sas_iounit_pg0->PhyData[i].PortFlags & + MPI2_SASIOUNIT0_PORTFLAGS_AUTO_PORT_CONFIG); + sas_iounit_pg1->PhyData[i].PhyFlags = + (sas_iounit_pg0->PhyData[i].PhyFlags & + (MPI2_SASIOUNIT0_PHYFLAGS_ZONING_ENABLED + + MPI2_SASIOUNIT0_PHYFLAGS_PHY_DISABLED)); + } + if (enable) + sas_iounit_pg1->PhyData[phy->number].PhyFlags + &= ~MPI2_SASIOUNIT1_PHYFLAGS_PHY_DISABLE; + else + sas_iounit_pg1->PhyData[phy->number].PhyFlags + |= MPI2_SASIOUNIT1_PHYFLAGS_PHY_DISABLE; + + mpt2sas_config_set_sas_iounit_pg1(ioc, &mpi_reply, sas_iounit_pg1, sz); + + /* link reset */ + if (enable) + _transport_phy_reset(phy, 0); + + out: + kfree(sas_iounit_pg1); + kfree(sas_iounit_pg0); + return rc; +} + +/** + * _transport_phy_speed - set phy min/max link rates + * @phy: The sas phy object + * @rates: rates defined in sas_phy_linkrates + * + * Only support sas_host direct attached phys. + * Returns 0 for success, non-zero for failure. + */ +static int +_transport_phy_speed(struct sas_phy *phy, struct sas_phy_linkrates *rates) +{ + struct MPT2SAS_ADAPTER *ioc = phy_to_ioc(phy); + Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL; + Mpi2SasPhyPage0_t phy_pg0; + Mpi2ConfigReply_t mpi_reply; + u16 ioc_status; + u16 sz; + int i; + int rc = 0; + unsigned long flags; + + spin_lock_irqsave(&ioc->sas_node_lock, flags); + if (_transport_sas_node_find_by_sas_address(ioc, + phy->identify.sas_address) == NULL) { + spin_unlock_irqrestore(&ioc->sas_node_lock, flags); + return -EINVAL; + } + spin_unlock_irqrestore(&ioc->sas_node_lock, flags); + + if (!rates->minimum_linkrate) + rates->minimum_linkrate = phy->minimum_linkrate; + else if (rates->minimum_linkrate < phy->minimum_linkrate_hw) + rates->minimum_linkrate = phy->minimum_linkrate_hw; + + if (!rates->maximum_linkrate) + rates->maximum_linkrate = phy->maximum_linkrate; + else if (rates->maximum_linkrate > phy->maximum_linkrate_hw) + rates->maximum_linkrate = phy->maximum_linkrate_hw; + + /* handle expander phys */ + if (phy->identify.sas_address != ioc->sas_hba.sas_address) { + phy->minimum_linkrate = rates->minimum_linkrate; + phy->maximum_linkrate = rates->maximum_linkrate; + return _transport_expander_phy_control(ioc, phy, + SMP_PHY_CONTROL_LINK_RESET); + } + + /* handle hba phys */ + + /* sas_iounit page 1 */ + sz = offsetof(Mpi2SasIOUnitPage1_t, PhyData) + (ioc->sas_hba.num_phys * + sizeof(Mpi2SasIOUnit1PhyData_t)); + sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL); + if (!sas_iounit_pg1) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + rc = -ENOMEM; + goto out; + } + if ((mpt2sas_config_get_sas_iounit_pg1(ioc, &mpi_reply, + sas_iounit_pg1, sz))) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + rc = -ENXIO; + goto out; + } + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + rc = -EIO; + goto out; + } + + for (i = 0; i < ioc->sas_hba.num_phys; i++) { + if (phy->number != i) { + sas_iounit_pg1->PhyData[i].MaxMinLinkRate = + (ioc->sas_hba.phy[i].phy->minimum_linkrate + + (ioc->sas_hba.phy[i].phy->maximum_linkrate << 4)); + } else { + sas_iounit_pg1->PhyData[i].MaxMinLinkRate = + (rates->minimum_linkrate + + (rates->maximum_linkrate << 4)); + } + } + + if (mpt2sas_config_set_sas_iounit_pg1(ioc, &mpi_reply, sas_iounit_pg1, + sz)) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + rc = -ENXIO; + goto out; + } + + /* link reset */ + _transport_phy_reset(phy, 0); + + /* read phy page 0, then update the rates in the sas transport phy */ + if (!mpt2sas_config_get_phy_pg0(ioc, &mpi_reply, &phy_pg0, + phy->number)) { + phy->minimum_linkrate = _transport_convert_phy_link_rate( + phy_pg0.ProgrammedLinkRate & MPI2_SAS_PRATE_MIN_RATE_MASK); + phy->maximum_linkrate = _transport_convert_phy_link_rate( + phy_pg0.ProgrammedLinkRate >> 4); + phy->negotiated_linkrate = _transport_convert_phy_link_rate( + phy_pg0.NegotiatedLinkRate & + MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL); + } + + out: + kfree(sas_iounit_pg1); + return rc; +} + + +/** * _transport_smp_handler - transport portal for smp passthru * @shost: shost object * @rphy: sas transport rphy object @@ -1031,24 +1910,21 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, u8 issue_reset = 0; dma_addr_t dma_addr_in = 0; dma_addr_t dma_addr_out = 0; + dma_addr_t pci_dma_in = 0; + dma_addr_t pci_dma_out = 0; + void *pci_addr_in = NULL; + void *pci_addr_out = NULL; u16 wait_state_count; struct request *rsp = req->next_rq; + struct bio_vec bvec; + struct bvec_iter iter; if (!rsp) { printk(MPT2SAS_ERR_FMT "%s: the smp response space is " "missing\n", ioc->name, __func__); return -EINVAL; } - - /* do we need to support multiple segments? */ - if (req->bio->bi_vcnt > 1 || rsp->bio->bi_vcnt > 1) { - printk(MPT2SAS_ERR_FMT "%s: multiple segments req %u %u, " - "rsp %u %u\n", ioc->name, __func__, req->bio->bi_vcnt, - blk_rq_bytes(req), rsp->bio->bi_vcnt, blk_rq_bytes(rsp)); - return -EINVAL; - } - - if (ioc->shost_recovery) { + if (ioc->shost_recovery || ioc->pci_error_recovery) { printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n", __func__, ioc->name); return -EFAULT; @@ -1066,6 +1942,59 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, } ioc->transport_cmds.status = MPT2_CMD_PENDING; + /* Check if the request is split across multiple segments */ + if (bio_multiple_segments(req->bio)) { + u32 offset = 0; + + /* Allocate memory and copy the request */ + pci_addr_out = pci_alloc_consistent(ioc->pdev, + blk_rq_bytes(req), &pci_dma_out); + if (!pci_addr_out) { + printk(MPT2SAS_INFO_FMT "%s(): PCI Addr out = NULL\n", + ioc->name, __func__); + rc = -ENOMEM; + goto out; + } + + bio_for_each_segment(bvec, req->bio, iter) { + memcpy(pci_addr_out + offset, + page_address(bvec.bv_page) + bvec.bv_offset, + bvec.bv_len); + offset += bvec.bv_len; + } + } else { + dma_addr_out = pci_map_single(ioc->pdev, bio_data(req->bio), + blk_rq_bytes(req), PCI_DMA_BIDIRECTIONAL); + if (!dma_addr_out) { + printk(MPT2SAS_INFO_FMT "%s(): DMA Addr out = NULL\n", + ioc->name, __func__); + rc = -ENOMEM; + goto free_pci; + } + } + + /* Check if the response needs to be populated across + * multiple segments */ + if (bio_multiple_segments(rsp->bio)) { + pci_addr_in = pci_alloc_consistent(ioc->pdev, blk_rq_bytes(rsp), + &pci_dma_in); + if (!pci_addr_in) { + printk(MPT2SAS_INFO_FMT "%s(): PCI Addr in = NULL\n", + ioc->name, __func__); + rc = -ENOMEM; + goto unmap; + } + } else { + dma_addr_in = pci_map_single(ioc->pdev, bio_data(rsp->bio), + blk_rq_bytes(rsp), PCI_DMA_BIDIRECTIONAL); + if (!dma_addr_in) { + printk(MPT2SAS_INFO_FMT "%s(): DMA Addr in = NULL\n", + ioc->name, __func__); + rc = -ENOMEM; + goto unmap; + } + } + wait_state_count = 0; ioc_state = mpt2sas_base_get_iocstate(ioc, 1); while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { @@ -1074,7 +2003,7 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, "%s: failed due to ioc not operational\n", ioc->name, __func__); rc = -EFAULT; - goto out; + goto unmap; } ssleep(1); ioc_state = mpt2sas_base_get_iocstate(ioc, 1); @@ -1091,7 +2020,7 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", ioc->name, __func__); rc = -EAGAIN; - goto out; + goto unmap; } rc = 0; @@ -1103,7 +2032,7 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, mpi_request->PhysicalPort = 0xFF; mpi_request->VF_ID = 0; /* TODO */ mpi_request->VP_ID = 0; - *((u64 *)&mpi_request->SASAddress) = (rphy) ? + mpi_request->SASAddress = (rphy) ? cpu_to_le64(rphy->identify.sas_address) : cpu_to_le64(ioc->sas_hba.sas_address); mpi_request->RequestDataLength = cpu_to_le16(blk_rq_bytes(req) - 4); @@ -1113,16 +2042,14 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_HOST_TO_IOC); sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; - dma_addr_out = pci_map_single(ioc->pdev, bio_data(req->bio), - blk_rq_bytes(req), PCI_DMA_BIDIRECTIONAL); - if (!dma_addr_out) { - mpt2sas_base_free_smid(ioc, smid); - goto unmap; + if (bio_multiple_segments(req->bio)) { + ioc->base_add_sg_single(psge, sgl_flags | + (blk_rq_bytes(req) - 4), pci_dma_out); + } else { + ioc->base_add_sg_single(psge, sgl_flags | + (blk_rq_bytes(req) - 4), dma_addr_out); } - ioc->base_add_sg_single(psge, sgl_flags | (blk_rq_bytes(req) - 4), - dma_addr_out); - /* incr sgel */ psge += ioc->sge_size; @@ -1131,21 +2058,19 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_END_OF_LIST); sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; - dma_addr_in = pci_map_single(ioc->pdev, bio_data(rsp->bio), - blk_rq_bytes(rsp), PCI_DMA_BIDIRECTIONAL); - if (!dma_addr_in) { - mpt2sas_base_free_smid(ioc, smid); - goto unmap; + if (bio_multiple_segments(rsp->bio)) { + ioc->base_add_sg_single(psge, sgl_flags | + (blk_rq_bytes(rsp) + 4), pci_dma_in); + } else { + ioc->base_add_sg_single(psge, sgl_flags | + (blk_rq_bytes(rsp) + 4), dma_addr_in); } - ioc->base_add_sg_single(psge, sgl_flags | (blk_rq_bytes(rsp) + 4), - dma_addr_in); - - dtransportprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s - " + dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT "%s - " "sending smp request\n", ioc->name, __func__)); - mpt2sas_base_put_smid_default(ioc, smid); init_completion(&ioc->transport_cmds.done); + mpt2sas_base_put_smid_default(ioc, smid); timeleft = wait_for_completion_timeout(&ioc->transport_cmds.done, 10*HZ); @@ -1159,14 +2084,14 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, goto issue_host_reset; } - dtransportprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s - " + dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT "%s - " "complete\n", ioc->name, __func__)); if (ioc->transport_cmds.status & MPT2_CMD_REPLY_VALID) { mpi_reply = ioc->transport_cmds.reply; - dtransportprintk(ioc, printk(MPT2SAS_DEBUG_FMT + dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT "%s - reply data transfer size(%d)\n", ioc->name, __func__, le16_to_cpu(mpi_reply->ResponseDataLength))); @@ -1174,9 +2099,31 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, memcpy(req->sense, mpi_reply, sizeof(*mpi_reply)); req->sense_len = sizeof(*mpi_reply); req->resid_len = 0; - rsp->resid_len -= mpi_reply->ResponseDataLength; + rsp->resid_len -= + le16_to_cpu(mpi_reply->ResponseDataLength); + /* check if the resp needs to be copied from the allocated + * pci mem */ + if (bio_multiple_segments(rsp->bio)) { + u32 offset = 0; + u32 bytes_to_copy = + le16_to_cpu(mpi_reply->ResponseDataLength); + bio_for_each_segment(bvec, rsp->bio, iter) { + if (bytes_to_copy <= bvec.bv_len) { + memcpy(page_address(bvec.bv_page) + + bvec.bv_offset, pci_addr_in + + offset, bytes_to_copy); + break; + } else { + memcpy(page_address(bvec.bv_page) + + bvec.bv_offset, pci_addr_in + + offset, bvec.bv_len); + bytes_to_copy -= bvec.bv_len; + } + offset += bvec.bv_len; + } + } } else { - dtransportprintk(ioc, printk(MPT2SAS_DEBUG_FMT + dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT "%s - no reply\n", ioc->name, __func__)); rc = -ENXIO; } @@ -1196,6 +2143,15 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, pci_unmap_single(ioc->pdev, dma_addr_in, blk_rq_bytes(rsp), PCI_DMA_BIDIRECTIONAL); + free_pci: + if (pci_addr_out) + pci_free_consistent(ioc->pdev, blk_rq_bytes(req), pci_addr_out, + pci_dma_out); + + if (pci_addr_in) + pci_free_consistent(ioc->pdev, blk_rq_bytes(rsp), pci_addr_in, + pci_dma_in); + out: ioc->transport_cmds.status = MPT2_CMD_NOT_USED; mutex_unlock(&ioc->transport_cmds.mutex); @@ -1207,6 +2163,8 @@ struct sas_function_template mpt2sas_transport_functions = { .get_enclosure_identifier = _transport_get_enclosure_identifier, .get_bay_identifier = _transport_get_bay_identifier, .phy_reset = _transport_phy_reset, + .phy_enable = _transport_phy_enable, + .set_phy_speed = _transport_phy_speed, .smp_handler = _transport_smp_handler, }; |
