From f9d979ce10c98dfd6d8d2a26217c3c4885ef97f6 Mon Sep 17 00:00:00 2001 From: "nagalakshmi.nandigama@lsi.com" Date: Wed, 19 Oct 2011 15:36:05 +0530 Subject: [SCSI] mpt2sas: MPI next revision header update 1)Added ProxyVF_ID field to Configuration Request message. 2)Added IO Unit Page 8, IO Unit Page 9,and IO Unit Page 10. 3)Added SASNotifyPrimitiveMasks field to IOC Page 7. 4)Added SAS NOTIFY Primitive event. 5)Added Temperature Threshold Event. 6)Added Host Message Event. 7)Added Send Host Message request and reply. Signed-off-by: Nagalakshmi Nandigama Signed-off-by: James Bottomley --- drivers/scsi/mpt2sas/mpi/mpi2.h | 11 ++- drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h | 153 +++++++++++++++++++++++++++++++---- drivers/scsi/mpt2sas/mpi/mpi2_ioc.h | 113 +++++++++++++++++++++++++- 3 files changed, 256 insertions(+), 21 deletions(-) diff --git a/drivers/scsi/mpt2sas/mpi/mpi2.h b/drivers/scsi/mpt2sas/mpi/mpi2.h index 3105d5e8d908..8dc1b32918dd 100644 --- a/drivers/scsi/mpt2sas/mpi/mpi2.h +++ b/drivers/scsi/mpt2sas/mpi/mpi2.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2010 LSI Corporation. + * Copyright (c) 2000-2011 LSI Corporation. * * * Name: mpi2.h @@ -8,7 +8,7 @@ * scatter/gather formats. * Creation Date: June 21, 2006 * - * mpi2.h Version: 02.00.18 + * mpi2.h Version: 02.00.20 * * Version History * --------------- @@ -66,6 +66,9 @@ * 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. * -------------------------------------------------------------------------- */ @@ -91,7 +94,7 @@ #define MPI2_VERSION_02_00 (0x0200) /* versioning for this MPI header set */ -#define MPI2_HEADER_VERSION_UNIT (0x12) +#define MPI2_HEADER_VERSION_UNIT (0x14) #define MPI2_HEADER_VERSION_DEV (0x00) #define MPI2_HEADER_VERSION_UNIT_MASK (0xFF00) #define MPI2_HEADER_VERSION_UNIT_SHIFT (8) @@ -515,6 +518,8 @@ typedef union _MPI2_REPLY_DESCRIPTORS_UNION #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 */ diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h b/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h index 61475a6480e3..cfd95b4e3004 100644 --- a/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h +++ b/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h @@ -1,12 +1,12 @@ /* - * Copyright (c) 2000-2010 LSI Corporation. + * Copyright (c) 2000-2011 LSI Corporation. * * * Name: mpi2_cnfg.h * Title: MPI Configuration messages and pages * Creation Date: November 10, 2006 * - * mpi2_cnfg.h Version: 02.00.17 + * mpi2_cnfg.h Version: 02.00.19 * * Version History * --------------- @@ -134,6 +134,12 @@ * 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). * -------------------------------------------------------------------------- */ @@ -329,7 +335,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 */ @@ -915,6 +923,120 @@ typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_7 { #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) + /**************************************************************************** @@ -1022,12 +1144,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 */ @@ -2070,16 +2192,16 @@ typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_8 { #define MPI2_SASIOUNITPAGE8_PAGEVERSION (0x00) /* defines for PowerManagementCapabilities field */ -#define MPI2_SASIOUNIT8_PM_HOST_PORT_WIDTH_MOD (0x000001000) -#define MPI2_SASIOUNIT8_PM_HOST_SAS_SLUMBER_MODE (0x000000800) -#define MPI2_SASIOUNIT8_PM_HOST_SAS_PARTIAL_MODE (0x000000400) -#define MPI2_SASIOUNIT8_PM_HOST_SATA_SLUMBER_MODE (0x000000200) -#define MPI2_SASIOUNIT8_PM_HOST_SATA_PARTIAL_MODE (0x000000100) -#define MPI2_SASIOUNIT8_PM_IOUNIT_PORT_WIDTH_MOD (0x000000010) -#define MPI2_SASIOUNIT8_PM_IOUNIT_SAS_SLUMBER_MODE (0x000000008) -#define MPI2_SASIOUNIT8_PM_IOUNIT_SAS_PARTIAL_MODE (0x000000004) -#define MPI2_SASIOUNIT8_PM_IOUNIT_SATA_SLUMBER_MODE (0x000000002) -#define MPI2_SASIOUNIT8_PM_IOUNIT_SATA_PARTIAL_MODE (0x000000001) +#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) @@ -2266,6 +2388,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) diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h b/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h index 1f0c190d336e..93d9b6956d05 100644 --- a/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h +++ b/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h @@ -1,12 +1,12 @@ /* - * Copyright (c) 2000-2010 LSI Corporation. + * Copyright (c) 2000-2011 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.16 + * mpi2_ioc.h Version: 02.00.17 * * Version History * --------------- @@ -104,6 +104,12 @@ * 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. * -------------------------------------------------------------------------- */ @@ -421,7 +427,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, @@ -476,6 +482,9 @@ typedef struct _MPI2_EVENT_NOTIFICATION_REPLY #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) /* Log Entry Added Event data */ @@ -507,6 +516,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 @@ -749,6 +791,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 */ @@ -1000,6 +1060,53 @@ typedef struct _MPI2_EVENT_ACK_REPLY Mpi2EventAckReply_t, MPI2_POINTER pMpi2EventAckReply_t; +/**************************************************************************** +* 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 ****************************************************************************/ -- cgit v1.2.3 From 921cd8024b908f8f49f772c8d3a02381b4db2ed2 Mon Sep 17 00:00:00 2001 From: "nagalakshmi.nandigama@lsi.com" Date: Wed, 19 Oct 2011 15:36:26 +0530 Subject: [SCSI] mpt2sas: New feature - Fast Load Support New feature Fast Load Support. (1)Asynchronous SCSI scanning: This will allow the drivers to scan for devices in parallel while other device drivers are loading at the same time. This will improve the amount of time it takes for the OS to load. (2) Reporting Devices while port enable is active: This feature will allow devices to be reported to OS immediately while port enable is active. The previous implementation waits for port enable to complete, and then report devices. This feature is only enabled on IT firmware configurations when there are no boot device configured in BIOS Configuration Utility, else the driver will wait till port enable completes reporting devices. For IR firmware, this feature is turned off. This feature is to address large SAS topologies (>100 drives) when the boot OS is using onboard SATA device, in other words, the boot devices is not connected to our controller. (3) Scanning for devices after diagnostic reset completes: A new routine _scsih_scan_start is added. This will scan the expander pages, IR pages, and sas device pages, then reporting new devices to SCSI Mid layer. It seems the driver is not supporting adding devices while diagnostic reset is active. Apparently this is due to the sanity checks on ioc->shost_recovery flag throughout the context of kernel work thread FIFO, and the mpt2sas_fw_work. Signed-off-by: Nagalakshmi Nandigama Signed-off-by: James Bottomley --- drivers/scsi/mpt2sas/mpt2sas_base.c | 247 ++++++++++++++++---- drivers/scsi/mpt2sas/mpt2sas_base.h | 26 ++- drivers/scsi/mpt2sas/mpt2sas_ctl.c | 9 +- drivers/scsi/mpt2sas/mpt2sas_scsih.c | 423 ++++++++++++++++++++++++++++------- 4 files changed, 580 insertions(+), 125 deletions(-) diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.c b/drivers/scsi/mpt2sas/mpt2sas_base.c index 81209ca87274..beda04a8404b 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_base.c +++ b/drivers/scsi/mpt2sas/mpt2sas_base.c @@ -81,6 +81,15 @@ 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"); +static int mpt2sas_fwfault_debug; +MODULE_PARM_DESC(mpt2sas_fwfault_debug, " enable detection of firmware fault " + "and halt firmware - (default=0)"); + +static int disable_discovery = -1; +module_param(disable_discovery, int, 0); +MODULE_PARM_DESC(disable_discovery, " disable discovery "); + + /* diag_buffer_enable is bitwise * bit 0 set = TRACE * bit 1 set = SNAPSHOT @@ -93,14 +102,6 @@ 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 mpt2sas_fwfault_debug; -MODULE_PARM_DESC(mpt2sas_fwfault_debug, " enable detection of firmware fault " - "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. * @@ -691,6 +692,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; } @@ -3469,6 +3471,58 @@ _base_send_ioc_init(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) return 0; } +/** + * 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 @@ -3480,66 +3534,150 @@ 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_INFO_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 @@ -3962,6 +4100,7 @@ _base_make_ioc_operational(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) skip_init_reply_post_host_index: _base_unmask_interrupts(ioc); + r = _base_event_notification(ioc, sleep_flag); if (r) return r; @@ -3969,7 +4108,18 @@ _base_make_ioc_operational(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) if (sleep_flag == CAN_SLEEP) _base_static_config_pages(ioc); - if (ioc->wait_for_port_enable_to_complete && ioc->is_warpdrive) { + + if (ioc->is_driver_loading) { + + + + ioc->wait_for_discovery_to_complete = + _base_determine_wait_on_discovery(ioc); + return r; /* scan_start and scan_finished support */ + } + + + if (ioc->wait_for_discovery_to_complete && ioc->is_warpdrive) { if (ioc->manu_pg10.OEMIdentifier == 0x80) { hide_flag = (u8) (ioc->manu_pg10.OEMSpecificFlags0 & MFG_PAGE10_HIDE_SSDS_MASK); @@ -3978,13 +4128,6 @@ _base_make_ioc_operational(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) } } - if (ioc->wait_for_port_enable_to_complete) { - if (diag_buffer_enable != 0) - mpt2sas_enable_diag_buffer(ioc, diag_buffer_enable); - if (disable_discovery > 0) - return r; - } - r = _base_send_port_enable(ioc, sleep_flag); if (r) return r; @@ -4121,6 +4264,10 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc) 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; @@ -4162,8 +4309,6 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc) goto out_free_resources; } - init_completion(&ioc->shost_recovery_done); - for (i = 0; i < MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++) ioc->event_masks[i] = -1; @@ -4186,7 +4331,6 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc) _base_update_missing_delay(ioc, missing_delay[0], missing_delay[1]); - mpt2sas_base_start_watchdog(ioc); return 0; out_free_resources: @@ -4204,6 +4348,7 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc) 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); @@ -4243,6 +4388,7 @@ mpt2sas_base_detach(struct MPT2SAS_ADAPTER *ioc) 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); @@ -4284,6 +4430,20 @@ _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); @@ -4349,7 +4509,6 @@ mpt2sas_base_hard_reset_handler(struct MPT2SAS_ADAPTER *ioc, int sleep_flag, { int r; unsigned long flags; - u8 pe_complete = ioc->wait_for_port_enable_to_complete; dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter\n", ioc->name, __func__)); @@ -4396,7 +4555,8 @@ mpt2sas_base_hard_reset_handler(struct MPT2SAS_ADAPTER *ioc, int sleep_flag, /* If this hard reset is called while port enable is active, then * there is no reason to call make_ioc_operational */ - if (pe_complete) { + if (ioc->is_driver_loading && ioc->port_enable_failed) { + ioc->remove_host = 1; r = -EFAULT; goto out; } @@ -4410,7 +4570,6 @@ mpt2sas_base_hard_reset_handler(struct MPT2SAS_ADAPTER *ioc, int sleep_flag, spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); ioc->ioc_reset_in_progress_status = r; ioc->shost_recovery = 0; - complete(&ioc->shost_recovery_done); spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); mutex_unlock(&ioc->reset_in_progress_mutex); diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.h b/drivers/scsi/mpt2sas/mpt2sas_base.h index 59354dba68c0..ce2bd9bf40f7 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_base.h +++ b/drivers/scsi/mpt2sas/mpt2sas_base.h @@ -655,7 +655,12 @@ enum mutex_type { * @ignore_loginfos: ignore loginfos during task management * @remove_host: flag for when driver unloads, to avoid sending dev resets * @pci_error_recovery: flag to prevent ioc access until slot reset completes - * @wait_for_port_enable_to_complete: + * @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 * @cpu_msix_table: table for mapping cpus to msix index @@ -790,15 +795,20 @@ struct MPT2SAS_ADAPTER { u8 shost_recovery; struct mutex reset_in_progress_mutex; - struct completion shost_recovery_done; spinlock_t ioc_reset_in_progress_lock; u8 ioc_link_reset_in_progress; - int ioc_reset_in_progress_status; + u8 ioc_reset_in_progress_status; u8 ignore_loginfos; u8 remove_host; u8 pci_error_recovery; - u8 wait_for_port_enable_to_complete; + 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; @@ -814,11 +824,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; @@ -1001,6 +1013,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); @@ -1015,6 +1029,8 @@ void mpt2sas_base_validate_event_type(struct MPT2SAS_ADAPTER *ioc, u32 *event_ty void mpt2sas_halt_firmware(struct MPT2SAS_ADAPTER *ioc); +int mpt2sas_port_enable(struct MPT2SAS_ADAPTER *ioc); + /* scsih shared API */ u8 mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, u32 reply); @@ -1032,6 +1048,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 */ diff --git a/drivers/scsi/mpt2sas/mpt2sas_ctl.c b/drivers/scsi/mpt2sas/mpt2sas_ctl.c index 9adb0133d6fb..aabcb911706e 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_ctl.c +++ b/drivers/scsi/mpt2sas/mpt2sas_ctl.c @@ -1207,6 +1207,9 @@ _ctl_do_reset(void __user *arg) if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc) return -ENODEV; + 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__)); @@ -2178,7 +2181,8 @@ _ctl_ioctl_main(struct file *file, unsigned int cmd, void __user *arg) !ioc) return -ENODEV; - if (ioc->shost_recovery || ioc->pci_error_recovery) + if (ioc->shost_recovery || ioc->pci_error_recovery || + ioc->is_driver_loading) return -EAGAIN; if (_IOC_SIZE(cmd) == sizeof(struct mpt2_ioctl_command)) { @@ -2297,7 +2301,8 @@ _ctl_compat_mpt_command(struct file *file, unsigned cmd, unsigned long arg) if (_ctl_verify_adapter(karg32.hdr.ioc_number, &ioc) == -1 || !ioc) return -ENODEV; - if (ioc->shost_recovery || ioc->pci_error_recovery) + if (ioc->shost_recovery || ioc->pci_error_recovery || + ioc->is_driver_loading) return -EAGAIN; memset(&karg, 0, sizeof(struct mpt2_ioctl_command)); diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c index 1da1aa1a11e2..7a3865d9c959 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c +++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c @@ -71,6 +71,9 @@ 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); @@ -79,6 +82,7 @@ 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; @@ -103,6 +107,18 @@ 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)"); + /** * struct sense_info - common structure for obtaining sense keys * @skey: sense key @@ -117,8 +133,8 @@ struct sense_info { #define MPT2SAS_TURN_ON_FAULT_LED (0xFFFC) -#define MPT2SAS_RESCAN_AFTER_HOST_RESET (0xFFFF) - +#define MPT2SAS_PORT_ENABLE_COMPLETE (0xFFFD) +#define MPT2SAS_REMOVE_UNRESPONDING_DEVICES (0xFFFF) /** * struct fw_event_work - firmware event struct * @list: link list framework @@ -424,7 +440,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) { @@ -2714,22 +2734,38 @@ _scsih_fw_event_free(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work /** - * _scsih_queue_rescan - queue a topology rescan from user context + * _scsih_error_recovery_delete_devices - remove devices not responding * @ioc: per adapter object * * Return nothing. */ static void -_scsih_queue_rescan(struct MPT2SAS_ADAPTER *ioc) +_scsih_error_recovery_delete_devices(struct MPT2SAS_ADAPTER *ioc) { struct fw_event_work *fw_event; - if (ioc->wait_for_port_enable_to_complete) + if (ioc->is_driver_loading) 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_RESCAN_AFTER_HOST_RESET; + fw_event->event = MPT2SAS_PORT_ENABLE_COMPLETE; fw_event->ioc = ioc; _scsih_fw_event_add(ioc, fw_event); } @@ -5099,7 +5135,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); @@ -5622,7 +5658,7 @@ broadcast_aen_retry: termination_count = 0; query_count = 0; for (smid = 1; smid <= ioc->scsiio_depth; smid++) { - if (ioc->ioc_reset_in_progress_status) + if (ioc->shost_recovery) goto out; scmd = _scsih_scsi_lookup_get(ioc, smid); if (!scmd) @@ -5644,7 +5680,7 @@ broadcast_aen_retry: lun = sas_device_priv_data->lun; query_count++; - if (ioc->ioc_reset_in_progress_status) + if (ioc->shost_recovery) goto out; spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); @@ -5686,7 +5722,7 @@ broadcast_aen_retry: goto broadcast_aen_retry; } - if (ioc->ioc_reset_in_progress_status) + if (ioc->shost_recovery) goto out_no_lock; r = mpt2sas_scsih_issue_tm(ioc, handle, sdev->channel, sdev->id, @@ -5725,7 +5761,7 @@ broadcast_aen_retry: ioc->name, __func__, query_count, termination_count)); ioc->broadcast_aen_busy = 0; - if (!ioc->ioc_reset_in_progress_status) + if (!ioc->shost_recovery) _scsih_ublock_io_all_device(ioc); mutex_unlock(&ioc->tm_cmds.mutex); } @@ -5845,7 +5881,7 @@ _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) @@ -6127,6 +6163,10 @@ _scsih_sas_ir_config_change_event(struct MPT2SAS_ADAPTER *ioc, _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; @@ -6185,6 +6225,9 @@ _scsih_sas_ir_volume_event(struct MPT2SAS_ADAPTER *ioc, int rc; 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; @@ -6267,6 +6310,9 @@ _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; @@ -6510,10 +6556,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, @@ -6532,6 +6578,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); } /** @@ -6607,10 +6656,14 @@ _scsih_search_responding_raid_devices(struct MPT2SAS_ADAPTER *ioc) u16 handle; u8 phys_disk_num; - printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__); + if (!ioc->ir_firmware) + return; + + 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, @@ -6649,6 +6702,9 @@ _scsih_search_responding_raid_devices(struct MPT2SAS_ADAPTER *ioc) set_bit(handle, ioc->pd_handles); } } +out: + printk(MPT2SAS_INFO_FMT "search for responding raid volumes: " + "complete\n", ioc->name); } /** @@ -6708,10 +6764,10 @@ _scsih_search_responding_expanders(struct MPT2SAS_ADAPTER *ioc) 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, @@ -6730,6 +6786,8 @@ _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); } /** @@ -6745,6 +6803,8 @@ _scsih_remove_unresponding_sas_devices(struct MPT2SAS_ADAPTER *ioc) struct _sas_node *sas_expander; struct _raid_device *raid_device, *raid_device_next; + printk(MPT2SAS_INFO_FMT "removing unresponding devices: start\n", + ioc->name); list_for_each_entry_safe(sas_device, sas_device_next, &ioc->sas_device_list, list) { @@ -6764,6 +6824,9 @@ _scsih_remove_unresponding_sas_devices(struct MPT2SAS_ADAPTER *ioc) _scsih_remove_device(ioc, sas_device); } + if (!ioc->ir_firmware) + goto retry_expander_search; + list_for_each_entry_safe(raid_device, raid_device_next, &ioc->raid_device_list, list) { if (raid_device->responding) { @@ -6790,52 +6853,170 @@ _scsih_remove_unresponding_sas_devices(struct MPT2SAS_ADAPTER *ioc) mpt2sas_expander_remove(ioc, sas_expander->sas_address); goto retry_expander_search; } + 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_hide_unhide_sas_devices - add/remove device to/from OS + * _scsih_scan_for_devices_after_reset - scan for devices after host reset * @ioc: per adapter object * * Return nothing. */ static void -_scsih_hide_unhide_sas_devices(struct MPT2SAS_ADAPTER *ioc) +_scsih_scan_for_devices_after_reset(struct MPT2SAS_ADAPTER *ioc) { - struct _sas_device *sas_device, *sas_device_next; + 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; - if (!ioc->is_warpdrive || ioc->mfg_pg10_hide_flag != - MFG_PAGE10_HIDE_IF_VOL_PRESENT) - return; + printk(MPT2SAS_INFO_FMT "scan devices: start\n", ioc->name); - if (ioc->hide_drives) { - if (_scsih_get_num_volumes(ioc)) - return; - ioc->hide_drives = 0; - list_for_each_entry_safe(sas_device, sas_device_next, - &ioc->sas_device_list, list) { - 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_device->sas_address, - sas_device->sas_address_parent); - _scsih_sas_device_remove(ioc, sas_device); - } + _scsih_sas_host_refresh(ioc); + + /* 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_CONFIG_INVALID_PAGE) + break; + handle = le16_to_cpu(expander_pg0.DevHandle); + expander_device = mpt2sas_scsih_expander_find_by_sas_address( + ioc, le64_to_cpu(expander_pg0.SASAddress)); + if (expander_device) + _scsih_refresh_expander_links(ioc, expander_device, + handle); + else + _scsih_expander_add(ioc, handle); + } + + if (!ioc->ir_firmware) + goto skip_to_sas; + + /* 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_CONFIG_INVALID_PAGE) + break; + phys_disk_num = pd_pg0.PhysDiskNum; + handle = le16_to_cpu(pd_pg0.DevHandle); + sas_device = _scsih_sas_device_find_by_handle(ioc, handle); + 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; + parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle); + if (!_scsih_get_sas_address(ioc, parent_handle, + &sas_address)) { + 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); + _scsih_add_device(ioc, handle, 0, 1); } - } else { - if (!_scsih_get_num_volumes(ioc)) - return; - ioc->hide_drives = 1; - list_for_each_entry_safe(sas_device, sas_device_next, - &ioc->sas_device_list, list) { - mpt2sas_transport_port_remove(ioc, - sas_device->sas_address, - sas_device->sas_address_parent); + } + + /* 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_CONFIG_INVALID_PAGE) + break; + handle = le16_to_cpu(volume_pg1.DevHandle); + raid_device = _scsih_raid_device_find_by_wwid(ioc, + le64_to_cpu(volume_pg1.WWID)); + 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; + 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; + _scsih_sas_volume_add(ioc, &element); + } + } + + skip_to_sas: + + /* 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_CONFIG_INVALID_PAGE) + break; + handle = le16_to_cpu(sas_device_pg0.DevHandle); + if (!(_scsih_is_end_device( + le32_to_cpu(sas_device_pg0.DeviceInfo)))) + continue; + sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, + le64_to_cpu(sas_device_pg0.SASAddress)); + if (sas_device) + continue; + parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle); + if (!_scsih_get_sas_address(ioc, parent_handle, &sas_address)) { + mpt2sas_transport_update_links(ioc, sas_address, handle, + sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5); + _scsih_add_device(ioc, handle, 0, 0); } } + + printk(MPT2SAS_INFO_FMT "scan devices: complete\n", ioc->name); } + /** * mpt2sas_scsih_reset_handler - reset callback handler (for scsih) * @ioc: per adapter object @@ -6871,7 +7052,6 @@ mpt2sas_scsih_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase) } _scsih_fw_event_cleanup_queue(ioc); _scsih_flush_running_cmds(ioc); - _scsih_queue_rescan(ioc); break; case MPT2_IOC_DONE_RESET: dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: " @@ -6881,6 +7061,7 @@ mpt2sas_scsih_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase) _scsih_search_responding_sas_devices(ioc); _scsih_search_responding_raid_devices(ioc); _scsih_search_responding_expanders(ioc); + _scsih_error_recovery_delete_devices(ioc); break; } } @@ -6898,7 +7079,6 @@ _firmware_event_work(struct work_struct *work) { struct fw_event_work *fw_event = container_of(work, struct fw_event_work, delayed_work.work); - unsigned long flags; struct MPT2SAS_ADAPTER *ioc = fw_event->ioc; /* the queue is being flushed so ignore this event */ @@ -6908,23 +7088,31 @@ _firmware_event_work(struct work_struct *work) return; } - if (fw_event->event == MPT2SAS_RESCAN_AFTER_HOST_RESET) { - _scsih_fw_event_free(ioc, fw_event); - spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); - if (ioc->shost_recovery) { - init_completion(&ioc->shost_recovery_done); - spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, - flags); - wait_for_completion(&ioc->shost_recovery_done); - } else - spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, - flags); + switch (fw_event->event) { + case MPT2SAS_REMOVE_UNRESPONDING_DEVICES: + while (scsi_host_in_recovery(ioc->shost)) + ssleep(1); _scsih_remove_unresponding_sas_devices(ioc); - _scsih_hide_unhide_sas_devices(ioc); - return; - } + _scsih_scan_for_devices_after_reset(ioc); + break; + case MPT2SAS_PORT_ENABLE_COMPLETE: + if (!ioc->is_driver_loading && ioc->shost_recovery) { + _scsih_prep_device_scan(ioc); + _scsih_search_responding_sas_devices(ioc); + _scsih_search_responding_raid_devices(ioc); + _scsih_search_responding_expanders(ioc); + } - switch (fw_event->event) { + if (ioc->start_scan) + ioc->start_scan = 0; + else + complete(&ioc->port_enable_done); + + + + 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; @@ -7121,6 +7309,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, @@ -7381,7 +7571,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; @@ -7490,9 +7685,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 */ @@ -7500,18 +7693,93 @@ _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); + + 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 (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 @@ -7548,6 +7816,7 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) 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; @@ -7620,14 +7889,14 @@ _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; + scsi_scan_host(shost); if (ioc->is_warpdrive) { if (ioc->mfg_pg10_hide_flag == MFG_PAGE10_EXPOSE_ALL_DISKS) ioc->hide_drives = 0; @@ -7650,6 +7919,7 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) out_thread_fail: list_del(&ioc->list); scsi_remove_host(shost); + scsi_host_put(shost); out_add_shost_fail: return -ENODEV; } @@ -7896,6 +8166,8 @@ _scsih_init(void) /* 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( @@ -7950,6 +8222,7 @@ _scsih_exit(void) 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); -- cgit v1.2.3 From 0167ac67ff6f35bf2364f7672c8012b0cd40277f Mon Sep 17 00:00:00 2001 From: "nagalakshmi.nandigama@lsi.com" Date: Fri, 21 Oct 2011 10:06:33 +0530 Subject: [SCSI] mpt2sas: Fix for system hang when discovery in progress Fix for issue : While discovery is in progress, hot unplug and hot plug of enclosure connected to the controller card is causing system to hang. When a device is in the process of being detected at driver load time then if it is removed, the device that is no longer present will not be added to the list. So the code in _scsih_probe_sas() is rearranged as such so the devices that failed to be detected are not added to the list. Signed-off-by: Nagalakshmi Nandigama Cc: stable@kernel.org Signed-off-by: James Bottomley --- drivers/scsi/mpt2sas/mpt2sas_scsih.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c index 7a3865d9c959..c13efc3268d8 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c +++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c @@ -7657,22 +7657,27 @@ _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); + 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); } } -- cgit v1.2.3 From 24f09b598dc455be84991e69ab9e6a339fd66bcf Mon Sep 17 00:00:00 2001 From: "nagalakshmi.nandigama@lsi.com" Date: Wed, 19 Oct 2011 15:36:47 +0530 Subject: [SCSI] mpt2sas: Fix failure message displayed during diag reset The fix is to inhibit the warning message in _scsih_get_sas_address when the MPI2_IOCSTATUS_CONFIG_INVALID_PAGE ioc status is returned. Signed-off-by: Nagalakshmi Nandigama Signed-off-by: James Bottomley --- drivers/scsi/mpt2sas/mpt2sas_scsih.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c index c13efc3268d8..8a9c70f21ecd 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c +++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c @@ -388,31 +388,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; } /** -- cgit v1.2.3 From f881ceadd4d6afafb227bcf8165c1b63ba90065b Mon Sep 17 00:00:00 2001 From: "nagalakshmi.nandigama@lsi.com" Date: Wed, 19 Oct 2011 15:37:00 +0530 Subject: [SCSI] mpt2sas: Fix drives not getting properly deleted if sas cable is removed while host reset is active The fix is in the driver-firmware handshake device removal code. We need to read the controller ioc_state to see if controller is OPERATIONAL prior to sending target reset and OP_REMOVE. Previously it was checking the flag ioc->shost_recovery flag, which is always set when host reset is active, thus preventing drives from getting properly deleted. Signed-off-by: Nagalakshmi Nandigama Signed-off-by: James Bottomley --- drivers/scsi/mpt2sas/mpt2sas_scsih.c | 38 ++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c index 8a9c70f21ecd..cd89f42440fe 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c +++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c @@ -3019,11 +3019,23 @@ _scsih_tm_tr_send(struct MPT2SAS_ADAPTER *ioc, u16 handle) struct MPT2SAS_TARGET *sas_target_priv_data; unsigned long flags; struct _tr_list *delayed_tr; + u32 ioc_state; - 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)); + 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; } @@ -3224,11 +3236,21 @@ _scsih_tm_tr_complete(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, mpt2sas_base_get_reply_virt_addr(ioc, reply); Mpi2SasIoUnitControlRequest_t *mpi_request; u16 smid_sas_ctrl; + u32 ioc_state; - 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)); + 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; + } + 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; } -- cgit v1.2.3 From f3db032f1af6dd3280037ea526fee7cddcc36c41 Mon Sep 17 00:00:00 2001 From: "nagalakshmi.nandigama@lsi.com" Date: Wed, 19 Oct 2011 15:37:14 +0530 Subject: [SCSI] mpt2sas: Fix for dead lock occurring between host_lock and sas_device_lock Fix for dead lock occurring between host_lock and sas_device_lock. The deadlock is between two spin locks, between the shost->host_lock and driver ioc->sas_device_lock. The fix is to rearrange the code in the FW/Driver device removal handshake so the ioc->sas_device_lock is not occurring when the shost->host_lock is taken. [jejb: zero initialise sas_address to fix spurious compiler warning] Signed-off-by: Nagalakshmi Nandigama Signed-off-by: James Bottomley --- drivers/scsi/mpt2sas/mpt2sas_scsih.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c index cd89f42440fe..f9ce31950314 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c +++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c @@ -3016,7 +3016,8 @@ _scsih_tm_tr_send(struct MPT2SAS_ADAPTER *ioc, u16 handle) Mpi2SCSITaskManagementRequest_t *mpi_request; u16 smid; struct _sas_device *sas_device; - struct MPT2SAS_TARGET *sas_target_priv_data; + struct MPT2SAS_TARGET *sas_target_priv_data = NULL; + u64 sas_address = 0; unsigned long flags; struct _tr_list *delayed_tr; u32 ioc_state; @@ -3049,13 +3050,17 @@ _scsih_tm_tr_send(struct MPT2SAS_ADAPTER *ioc, u16 handle) sas_device->starget->hostdata) { sas_target_priv_data = sas_device->starget->hostdata; sas_target_priv_data->deleted = 1; - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT - "setting delete flag: handle(0x%04x), " - "sas_addr(0x%016llx)\n", ioc->name, handle, - (unsigned long long) sas_device->sas_address)); + sas_address = sas_device->sas_address; } spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + 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, handle); + } + smid = mpt2sas_base_get_smid_hpr(ioc, ioc->tm_tr_cb_idx); if (!smid) { delayed_tr = kzalloc(sizeof(*delayed_tr), GFP_ATOMIC); -- cgit v1.2.3 From 918134efe9893629407af04adf242ee3095bea4a Mon Sep 17 00:00:00 2001 From: "nagalakshmi.nandigama@lsi.com" Date: Wed, 19 Oct 2011 15:37:24 +0530 Subject: [SCSI] mpt2sas: Fix for deadlock between hot plug worker threads and host reset context This is due to driver reporting a device missing to the OS then the OS sending a SYNC_CACHE request to driver while the IO queues are locked due to host reset. To fix the issue, the driver will be waking up the port enable context immediately when the driver receives the reply message, instead of waiting on the hot plug worker threads. Signed-off-by: Nagalakshmi Nandigama Signed-off-by: James Bottomley --- drivers/scsi/mpt2sas/mpt2sas_scsih.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c index f9ce31950314..1f289882a298 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c +++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c @@ -3059,6 +3059,7 @@ _scsih_tm_tr_send(struct MPT2SAS_ADAPTER *ioc, u16 handle) "handle(0x%04x), sas_addr(0x%016llx)\n", ioc->name, handle, (unsigned long long)sas_address)); _scsih_ublock_io_device(ioc, handle); + sas_target_priv_data->handle = MPT2SAS_INVALID_DEVICE_HANDLE; } smid = mpt2sas_base_get_smid_hpr(ioc, ioc->tm_tr_cb_idx); @@ -5201,6 +5202,9 @@ _scsih_remove_device(struct MPT2SAS_ADAPTER *ioc, if (sas_device_backup.starget && sas_device_backup.starget->hostdata) { sas_target_priv_data = sas_device_backup.starget->hostdata; sas_target_priv_data->deleted = 1; + _scsih_ublock_io_device(ioc, sas_device_backup.handle); + sas_target_priv_data->handle = + MPT2SAS_INVALID_DEVICE_HANDLE; } _scsih_ublock_io_device(ioc, sas_device_backup.handle); @@ -5354,7 +5358,7 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc, _scsih_sas_topology_change_event_debug(ioc, event_data); #endif - if (ioc->shost_recovery || ioc->remove_host || ioc->pci_error_recovery) + if (ioc->remove_host || ioc->pci_error_recovery) return; if (!ioc->sas_hba.num_phys) @@ -5415,6 +5419,9 @@ _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; @@ -5428,6 +5435,9 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc, 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); @@ -7091,7 +7101,13 @@ mpt2sas_scsih_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase) _scsih_search_responding_sas_devices(ioc); _scsih_search_responding_raid_devices(ioc); _scsih_search_responding_expanders(ioc); + if (!ioc->is_driver_loading) { + _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; } } @@ -7126,17 +7142,7 @@ _firmware_event_work(struct work_struct *work) _scsih_scan_for_devices_after_reset(ioc); break; case MPT2SAS_PORT_ENABLE_COMPLETE: - if (!ioc->is_driver_loading && ioc->shost_recovery) { - _scsih_prep_device_scan(ioc); - _scsih_search_responding_sas_devices(ioc); - _scsih_search_responding_raid_devices(ioc); - _scsih_search_responding_expanders(ioc); - } - - if (ioc->start_scan) - ioc->start_scan = 0; - else - complete(&ioc->port_enable_done); + ioc->start_scan = 0; -- cgit v1.2.3 From 6faace2a0e418b45728bcea6d3626922cf16b14b Mon Sep 17 00:00:00 2001 From: "nagalakshmi.nandigama@lsi.com" Date: Wed, 19 Oct 2011 15:37:37 +0530 Subject: [SCSI] mpt2sas: Fix for issue Port Reset taking long time(around 5 mins) to complete while issued during creating a volume This is due to the slave_configuration routine is getting called when host reset is active, and config page reads are failing, and driver attempts to added device with stale config data. To fix the issue, added error checking in slave_configure to check for configuration pages failing, and return "1" so the device is not configured. The config pages are failing if raid volume is configured while issuing a host reset, thus driver is reading stale data and proceeding to attempt to add. The fix is to return error so the volume is not configured. Signed-off-by: Nagalakshmi Nandigama Signed-off-by: James Bottomley --- drivers/scsi/mpt2sas/mpt2sas_scsih.c | 62 ++++++++++++++++++++++++++---------- 1 file changed, 45 insertions(+), 17 deletions(-) diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c index 1f289882a298..a8cb57723ff4 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c +++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c @@ -1621,8 +1621,10 @@ _scsih_set_level(struct scsi_device *sdev, struct _raid_device *raid_device) * _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) { @@ -1635,9 +1637,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; @@ -1645,17 +1648,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; @@ -1675,6 +1680,7 @@ _scsih_get_volume_capabilities(struct MPT2SAS_ADAPTER *ioc, } kfree(vol_pg0); + return 0; } /** * _scsih_disable_ddio - Disable direct I/O for all the volumes @@ -1945,13 +1951,20 @@ _scsih_slave_configure(struct scsi_device *sdev) sas_target_priv_data->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 */ @@ -2025,11 +2038,21 @@ _scsih_slave_configure(struct scsi_device *sdev) 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, + if (mpt2sas_config_get_volume_handle(ioc, + sas_device->handle, &sas_device->volume_handle)) { + dfailprintk(ioc, printk(MPT2SAS_WARN_FMT + "failure at %s:%d/%s()!\n", ioc->name, + __FILE__, __LINE__, __func__)); + return 1; + } + if (mpt2sas_config_get_volume_wwid(ioc, sas_device->volume_handle, - &sas_device->volume_wwid); + &sas_device->volume_wwid)) { + dfailprintk(ioc, printk(MPT2SAS_WARN_FMT + "failure at %s:%d/%s()!\n", ioc->name, + __FILE__, __LINE__, __func__)); + return 1; + } } if (sas_device->device_info & MPI2_SAS_DEVICE_INFO_SSP_TARGET) { qdepth = MPT2SAS_SAS_QUEUE_DEPTH; @@ -2058,6 +2081,11 @@ _scsih_slave_configure(struct scsi_device *sdev) if (!ssp_target) _scsih_display_sata_capabilities(ioc, sas_device, sdev); + } else { + dfailprintk(ioc, printk(MPT2SAS_WARN_FMT + "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, + __func__)); + return 1; } _scsih_change_queue_depth(sdev, qdepth, SCSI_QDEPTH_DEFAULT); -- cgit v1.2.3 From 35116db95c42937061bfca93998291f6562e9e92 Mon Sep 17 00:00:00 2001 From: "nagalakshmi.nandigama@lsi.com" Date: Fri, 21 Oct 2011 10:08:07 +0530 Subject: [SCSI] mpt2sas: Fix for Panic when inactive volume is tried deleting The driver was setting the action to MPI2_CONFIG_ACTION_PAGE_READ_CURRENT, which only returns active volumes. In order to get info on inactive volumes, the driver needs to change the action to MPI2_RAID_PGAD_FORM_GET_NEXT_CONFIGNUM, and traverse each config till the iocstatus is MPI2_IOCSTATUS_CONFIG_INVALID_PAGE returned. Added a change in the driver to remove the instance of sas_device object when the driver returns "1" from the slave_configure callback. Also fixed code to report the hot spares to the operating system with a /dev/sg assigned. Signed-off-by: Nagalakshmi Nandigama Cc: stable@kernel.org Signed-off-by: James Bottomley --- drivers/scsi/mpt2sas/mpt2sas_config.c | 67 +++++++++++++++++++++++------------ drivers/scsi/mpt2sas/mpt2sas_scsih.c | 46 +++++++++++++++++++----- 2 files changed, 82 insertions(+), 31 deletions(-) diff --git a/drivers/scsi/mpt2sas/mpt2sas_config.c b/drivers/scsi/mpt2sas/mpt2sas_config.c index 2b1101076cfe..36ea0b2d8020 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_config.c +++ b/drivers/scsi/mpt2sas/mpt2sas_config.c @@ -1356,6 +1356,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)); @@ -1371,35 +1374,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) - goto out; - r = _config_request(ioc, &mpi_request, &mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, - config_page_sz); - if (r) + if (!config_page) { + r = -1; 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 ((le16_to_cpu(config_page->ConfigElement[i].ElementFlags) & - MPI2_RAIDCONFIG0_EFLAGS_MASK_ELEMENT_TYPE) != - MPI2_RAIDCONFIG0_EFLAGS_VOL_PHYS_DISK_ELEMENT) - continue; - if (le16_to_cpu(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_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c index a8cb57723ff4..8889b1babcac 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c +++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c @@ -610,8 +610,15 @@ _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) { + 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); + } } /** @@ -1423,6 +1430,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; @@ -1430,6 +1441,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_device->starget = NULL; + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + } + kfree(sdev->hostdata); sdev->hostdata = NULL; } @@ -2045,7 +2069,8 @@ _scsih_slave_configure(struct scsi_device *sdev) __FILE__, __LINE__, __func__)); return 1; } - if (mpt2sas_config_get_volume_wwid(ioc, + if (sas_device->volume_handle && + mpt2sas_config_get_volume_wwid(ioc, sas_device->volume_handle, &sas_device->volume_wwid)) { dfailprintk(ioc, printk(MPT2SAS_WARN_FMT @@ -5893,8 +5918,11 @@ _scsih_reprobe_lun(struct scsi_device *sdev, void *no_uld_attach) static void _scsih_reprobe_target(struct scsi_target *starget, int no_uld_attach) { - struct MPT2SAS_TARGET *sas_target_priv_data = starget->hostdata; + struct MPT2SAS_TARGET *sas_target_priv_data; + if (starget == NULL) + return; + sas_target_priv_data = starget->hostdata; if (no_uld_attach) sas_target_priv_data->flags |= MPT_TARGET_FLAGS_RAID_COMPONENT; else @@ -7676,8 +7704,9 @@ _scsih_probe_boot_devices(struct MPT2SAS_ADAPTER *ioc) 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); + if (!ioc->is_driver_loading) + mpt2sas_transport_port_remove(ioc, sas_address, + sas_address_parent); _scsih_sas_device_remove(ioc, sas_device); } } @@ -7731,9 +7760,10 @@ _scsih_probe_sas(struct MPT2SAS_ADAPTER *ioc) kfree(sas_device); continue; } else if (!sas_device->starget) { - mpt2sas_transport_port_remove(ioc, - sas_device->sas_address, - sas_device->sas_address_parent); + 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; -- cgit v1.2.3 From 6e88020025ccb6a6a0a54098acf1e187d2c9368c Mon Sep 17 00:00:00 2001 From: "nagalakshmi.nandigama@lsi.com" Date: Wed, 19 Oct 2011 15:37:54 +0530 Subject: [SCSI] mpt2sas: Bump driver version to 10.100.00.00 Bump driver vesion to 10.100.00.00 Signed-off-by: Nagalakshmi Nandigama Signed-off-by: James Bottomley --- drivers/scsi/mpt2sas/mpt2sas_base.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.h b/drivers/scsi/mpt2sas/mpt2sas_base.h index ce2bd9bf40f7..3c3babc7d260 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_base.h +++ b/drivers/scsi/mpt2sas/mpt2sas_base.h @@ -69,11 +69,11 @@ #define MPT2SAS_DRIVER_NAME "mpt2sas" #define MPT2SAS_AUTHOR "LSI Corporation " #define MPT2SAS_DESCRIPTION "LSI MPT Fusion SAS 2.0 Device Driver" -#define MPT2SAS_DRIVER_VERSION "09.100.00.01" -#define MPT2SAS_MAJOR_VERSION 09 +#define MPT2SAS_DRIVER_VERSION "10.100.00.00" +#define MPT2SAS_MAJOR_VERSION 10 #define MPT2SAS_MINOR_VERSION 100 #define MPT2SAS_BUILD_VERSION 00 -#define MPT2SAS_RELEASE_VERSION 01 +#define MPT2SAS_RELEASE_VERSION 00 /* * Set MPT2SAS_SG_DEPTH value based on user input. -- cgit v1.2.3 From 21208ae5a21fd5f337e987cde11374eaf2fe70b4 Mon Sep 17 00:00:00 2001 From: Dave Kleikamp Date: Wed, 19 Oct 2011 11:49:04 -0500 Subject: [SCSI] sd: remove arbitrary SD_MAX_DISKS namespace limit There is no reason to limit the SCSI disk namespace to sdXXX. Add new error messages to sd_probe() in the unlikely event that either ida_get_new() or sd_format_disk_name() fail. Signed-off-by: Dave Kleikamp Signed-off-by: James Bottomley --- drivers/scsi/sd.c | 12 +++++------- drivers/scsi/sd.h | 6 ------ 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index a7942e5c8be8..fa3a5918009c 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -2590,18 +2590,16 @@ static int sd_probe(struct device *dev) spin_unlock(&sd_index_lock); } while (error == -EAGAIN); - if (error) + if (error) { + sdev_printk(KERN_WARNING, sdp, "sd_probe: memory exhausted.\n"); goto out_put; - - if (index >= SD_MAX_DISKS) { - error = -ENODEV; - sdev_printk(KERN_WARNING, sdp, "SCSI disk (sd) name space exhausted.\n"); - goto out_free_index; } error = sd_format_disk_name("sd", index, gd->disk_name, DISK_NAME_LEN); - if (error) + if (error) { + sdev_printk(KERN_WARNING, sdp, "SCSI disk (sd) name length exceeded.\n"); goto out_free_index; + } sdkp->device = sdp; sdkp->driver = &sd_template; diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h index 6ad798bfd52a..4163f2910e3d 100644 --- a/drivers/scsi/sd.h +++ b/drivers/scsi/sd.h @@ -8,12 +8,6 @@ */ #define SD_MAJORS 16 -/* - * This is limited by the naming scheme enforced in sd_probe, - * add another character to it if you really need more disks. - */ -#define SD_MAX_DISKS (((26 * 26) + 26 + 1) * 26) - /* * Time out in seconds for disks and Magneto-opticals (which are slower). */ -- cgit v1.2.3 From 3308511c93e6ad0d3c58984ecd6e5e57f96b12c8 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 23 Sep 2011 19:48:18 +0200 Subject: [SCSI] Make scsi_free_queue() kill pending SCSI commands Make sure that SCSI device removal via scsi_remove_host() does finish all pending SCSI commands. Currently that's not the case and hence removal of a SCSI host during I/O can cause a deadlock. See also "blkdev_issue_discard() hangs forever if underlying storage device is removed" (http://bugzilla.kernel.org/show_bug.cgi?id=40472). See also http://lkml.org/lkml/2011/8/27/6. Signed-off-by: Bart Van Assche Cc: Signed-off-by: James Bottomley --- drivers/scsi/hosts.c | 9 ++++++--- drivers/scsi/scsi_lib.c | 9 +++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 4f7a5829ea4c..351dc0b86fab 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -286,6 +286,7 @@ static void scsi_host_dev_release(struct device *dev) { struct Scsi_Host *shost = dev_to_shost(dev); struct device *parent = dev->parent; + struct request_queue *q; scsi_proc_hostdir_rm(shost->hostt); @@ -293,9 +294,11 @@ static void scsi_host_dev_release(struct device *dev) kthread_stop(shost->ehandler); if (shost->work_q) destroy_workqueue(shost->work_q); - if (shost->uspace_req_q) { - kfree(shost->uspace_req_q->queuedata); - scsi_free_queue(shost->uspace_req_q); + q = shost->uspace_req_q; + if (q) { + kfree(q->queuedata); + q->queuedata = NULL; + scsi_free_queue(q); } scsi_destroy_command_freelist(shost); diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index fc3f168decb4..b4d43ae76132 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -1698,6 +1698,15 @@ struct request_queue *scsi_alloc_queue(struct scsi_device *sdev) void scsi_free_queue(struct request_queue *q) { + unsigned long flags; + + WARN_ON(q->queuedata); + + /* cause scsi_request_fn() to kill all non-finished requests */ + spin_lock_irqsave(q->queue_lock, flags); + q->request_fn(q); + spin_unlock_irqrestore(q->queue_lock, flags); + blk_cleanup_queue(q); } -- cgit v1.2.3 From c68bf8eeaa57c852e74adcf597237be149eef830 Mon Sep 17 00:00:00 2001 From: Petr Uzel Date: Fri, 21 Oct 2011 13:31:09 +0200 Subject: [SCSI] st: fix race in st_scsi_execute_end MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The call to complete() in st_scsi_execute_end() wakes up sleeping thread in write_behind_check(), which frees the st_request, thus invalidating the pointer to the associated bio structure, which is then passed to the blk_rq_unmap_user(). Fix by storing pointer to bio structure into temporary local variable. This bug is present since at least linux-2.6.32. CC: stable@kernel.org Signed-off-by: Petr Uzel Reported-by: Juergen Groß Reviewed-by: Jan Kara Acked-by: Kai Mäkisara Signed-off-by: James Bottomley --- drivers/scsi/st.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index 1871b8ae83ae..9b28f39bac26 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -462,14 +462,16 @@ static void st_scsi_execute_end(struct request *req, int uptodate) { struct st_request *SRpnt = req->end_io_data; struct scsi_tape *STp = SRpnt->stp; + struct bio *tmp; STp->buffer->cmdstat.midlevel_result = SRpnt->result = req->errors; STp->buffer->cmdstat.residual = req->resid_len; + tmp = SRpnt->bio; if (SRpnt->waiting) complete(SRpnt->waiting); - blk_rq_unmap_user(SRpnt->bio); + blk_rq_unmap_user(tmp); __blk_put_request(req->q, req); } -- cgit v1.2.3 From 99cc600cdd6f938633394523447378f7a43f4340 Mon Sep 17 00:00:00 2001 From: Bhanu Prakash Gollapudi Date: Sun, 23 Oct 2011 23:23:56 -0700 Subject: [SCSI] bnx2fc: Handle ABTS timeout during ulp timeout If the IO and the corresponding ABTS are not responded by a target, cleanup the IO and issue explicit logout when ulp timer expires while waiting for ABTS to complete. Wait for the session to be ready before returning to the SCSI layer. If the session is not ready let the SCSI-ml escalate the error recovery. Signed-off-by: Bhanu Prakash Gollapudi Signed-off-by: James Bottomley --- drivers/scsi/bnx2fc/bnx2fc.h | 3 +++ drivers/scsi/bnx2fc/bnx2fc_io.c | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/drivers/scsi/bnx2fc/bnx2fc.h b/drivers/scsi/bnx2fc/bnx2fc.h index 63de1c7cd0cb..b843d710688c 100644 --- a/drivers/scsi/bnx2fc/bnx2fc.h +++ b/drivers/scsi/bnx2fc/bnx2fc.h @@ -145,6 +145,9 @@ #define REC_RETRY_COUNT 1 #define BNX2FC_NUM_ERR_BITS 63 +#define BNX2FC_RELOGIN_WAIT_TIME 200 +#define BNX2FC_RELOGIN_WAIT_CNT 10 + /* bnx2fc driver uses only one instance of fcoe_percpu_s */ extern struct fcoe_percpu_s bnx2fc_global; diff --git a/drivers/scsi/bnx2fc/bnx2fc_io.c b/drivers/scsi/bnx2fc/bnx2fc_io.c index 0c64d184d731..84a78af83f90 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_io.c +++ b/drivers/scsi/bnx2fc/bnx2fc_io.c @@ -1103,7 +1103,10 @@ int bnx2fc_eh_abort(struct scsi_cmnd *sc_cmd) struct fc_rport_libfc_priv *rp = rport->dd_data; struct bnx2fc_cmd *io_req; struct fc_lport *lport; + struct fc_rport_priv *rdata; struct bnx2fc_rport *tgt; + int logo_issued; + int wait_cnt = 0; int rc = FAILED; @@ -1192,8 +1195,40 @@ int bnx2fc_eh_abort(struct scsi_cmnd *sc_cmd) } else { printk(KERN_ERR PFX "eh_abort: io_req (xid = 0x%x) " "already in abts processing\n", io_req->xid); + if (cancel_delayed_work(&io_req->timeout_work)) + kref_put(&io_req->refcount, + bnx2fc_cmd_release); /* drop timer hold */ + bnx2fc_initiate_cleanup(io_req); + + spin_unlock_bh(&tgt->tgt_lock); + + wait_for_completion(&io_req->tm_done); + + spin_lock_bh(&tgt->tgt_lock); + io_req->wait_for_comp = 0; + rdata = io_req->tgt->rdata; + logo_issued = test_and_set_bit(BNX2FC_FLAG_EXPL_LOGO, + &tgt->flags); kref_put(&io_req->refcount, bnx2fc_cmd_release); spin_unlock_bh(&tgt->tgt_lock); + + if (!logo_issued) { + BNX2FC_IO_DBG(io_req, "Expl logo - tgt flags = 0x%lx\n", + tgt->flags); + mutex_lock(&lport->disc.disc_mutex); + lport->tt.rport_logoff(rdata); + mutex_unlock(&lport->disc.disc_mutex); + do { + msleep(BNX2FC_RELOGIN_WAIT_TIME); + /* + * If session not recovered, let SCSI-ml + * escalate error recovery. + */ + if (wait_cnt++ > BNX2FC_RELOGIN_WAIT_CNT) + return FAILED; + } while (!test_bit(BNX2FC_FLAG_SESSION_READY, + &tgt->flags)); + } return SUCCESS; } if (rc == FAILED) { @@ -1275,6 +1310,8 @@ void bnx2fc_process_cleanup_compl(struct bnx2fc_cmd *io_req, io_req->refcount.refcount.counter, io_req->cmd_type); bnx2fc_scsi_done(io_req, DID_ERROR); kref_put(&io_req->refcount, bnx2fc_cmd_release); + if (io_req->wait_for_comp) + complete(&io_req->tm_done); } void bnx2fc_process_abts_compl(struct bnx2fc_cmd *io_req, -- cgit v1.2.3 From 32c30454507b4f5f00661ac12ddbcc150983b9ff Mon Sep 17 00:00:00 2001 From: Bhanu Prakash Gollapudi Date: Sun, 23 Oct 2011 23:23:57 -0700 Subject: [SCSI] bnx2fc: Handle SRR LS_ACC drop scenario When SRR LS_ACC is dropped, the driver was not issuing ABTS for SRR when it times out. Since the target received SRR, it was able to send the XFER_RDY and the the original IO request completed successfully. In this condition ABTS was not sent during bnx2fc_srr_compl(). Fix this by first checking for ELS timeout and issue ABTS before checking if original IO request is complete. Signed-off-by: Bhanu Prakash Gollapudi Signed-off-by: James Bottomley --- drivers/scsi/bnx2fc/bnx2fc_els.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/scsi/bnx2fc/bnx2fc_els.c b/drivers/scsi/bnx2fc/bnx2fc_els.c index fd382fe33f6e..ce0ce3e32f33 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_els.c +++ b/drivers/scsi/bnx2fc/bnx2fc_els.c @@ -268,17 +268,6 @@ void bnx2fc_srr_compl(struct bnx2fc_els_cb_arg *cb_arg) orig_io_req = cb_arg->aborted_io_req; srr_req = cb_arg->io_req; - if (test_bit(BNX2FC_FLAG_IO_COMPL, &orig_io_req->req_flags)) { - BNX2FC_IO_DBG(srr_req, "srr_compl: xid - 0x%x completed", - orig_io_req->xid); - goto srr_compl_done; - } - if (test_bit(BNX2FC_FLAG_ISSUE_ABTS, &orig_io_req->req_flags)) { - BNX2FC_IO_DBG(srr_req, "rec abts in prog " - "orig_io - 0x%x\n", - orig_io_req->xid); - goto srr_compl_done; - } if (test_and_clear_bit(BNX2FC_FLAG_ELS_TIMEOUT, &srr_req->req_flags)) { /* SRR timedout */ BNX2FC_IO_DBG(srr_req, "srr timed out, abort " @@ -290,6 +279,12 @@ void bnx2fc_srr_compl(struct bnx2fc_els_cb_arg *cb_arg) "failed. issue cleanup\n"); bnx2fc_initiate_cleanup(srr_req); } + if (test_bit(BNX2FC_FLAG_IO_COMPL, &orig_io_req->req_flags) || + test_bit(BNX2FC_FLAG_ISSUE_ABTS, &orig_io_req->req_flags)) { + BNX2FC_IO_DBG(srr_req, "srr_compl:xid 0x%x flags = %lx", + orig_io_req->xid, orig_io_req->req_flags); + goto srr_compl_done; + } orig_io_req->srr_retry++; if (orig_io_req->srr_retry <= SRR_RETRY_COUNT) { struct bnx2fc_rport *tgt = orig_io_req->tgt; @@ -311,6 +306,12 @@ void bnx2fc_srr_compl(struct bnx2fc_els_cb_arg *cb_arg) } goto srr_compl_done; } + if (test_bit(BNX2FC_FLAG_IO_COMPL, &orig_io_req->req_flags) || + test_bit(BNX2FC_FLAG_ISSUE_ABTS, &orig_io_req->req_flags)) { + BNX2FC_IO_DBG(srr_req, "srr_compl:xid - 0x%x flags = %lx", + orig_io_req->xid, orig_io_req->req_flags); + goto srr_compl_done; + } mp_req = &(srr_req->mp_req); fc_hdr = &(mp_req->resp_fc_hdr); resp_len = mp_req->resp_len; -- cgit v1.2.3 From fd2541893da50cbc1e547a9aaebf104bed859915 Mon Sep 17 00:00:00 2001 From: Bhanu Prakash Gollapudi Date: Sun, 23 Oct 2011 23:23:58 -0700 Subject: [SCSI] bnx2fc: Bumped version to 1.0.9 Signed-off-by: Bhanu Prakash Gollapudi Signed-off-by: James Bottomley --- drivers/scsi/bnx2fc/bnx2fc.h | 2 +- drivers/scsi/bnx2fc/bnx2fc_fcoe.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/bnx2fc/bnx2fc.h b/drivers/scsi/bnx2fc/bnx2fc.h index b843d710688c..049ea907e04a 100644 --- a/drivers/scsi/bnx2fc/bnx2fc.h +++ b/drivers/scsi/bnx2fc/bnx2fc.h @@ -62,7 +62,7 @@ #include "bnx2fc_constants.h" #define BNX2FC_NAME "bnx2fc" -#define BNX2FC_VERSION "1.0.8" +#define BNX2FC_VERSION "1.0.9" #define PFX "bnx2fc: " diff --git a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c index 85bcc4b55965..8c6156a10d90 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c +++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c @@ -22,7 +22,7 @@ DEFINE_PER_CPU(struct bnx2fc_percpu_s, bnx2fc_percpu); #define DRV_MODULE_NAME "bnx2fc" #define DRV_MODULE_VERSION BNX2FC_VERSION -#define DRV_MODULE_RELDATE "Oct 02, 2011" +#define DRV_MODULE_RELDATE "Oct 21, 2011" static char version[] __devinitdata = -- cgit v1.2.3 From a63ec37629415848b5704eda5110fe8e750032ca Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Mon, 24 Oct 2011 18:10:26 -0400 Subject: [SCSI] pmcraid: pmcraid_chr_ioctl uses incorrect argument order to kmalloc() Size is 1st arg, not second. Signed-off-by: Dave Jones Signed-off-by: James Bottomley --- drivers/scsi/pmcraid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/pmcraid.c b/drivers/scsi/pmcraid.c index b86db84d6f32..5163edb925cb 100644 --- a/drivers/scsi/pmcraid.c +++ b/drivers/scsi/pmcraid.c @@ -4102,7 +4102,7 @@ static long pmcraid_chr_ioctl( struct pmcraid_ioctl_header *hdr = NULL; int retval = -ENOTTY; - hdr = kmalloc(GFP_KERNEL, sizeof(struct pmcraid_ioctl_header)); + hdr = kmalloc(sizeof(struct pmcraid_ioctl_header), GFP_KERNEL); if (!hdr) { pmcraid_err("faile to allocate memory for ioctl header\n"); -- cgit v1.2.3 From d424754cbe97fe581985dca4400347cb275d7eb2 Mon Sep 17 00:00:00 2001 From: Andrew Vasquez Date: Wed, 26 Oct 2011 14:20:13 -0700 Subject: [SCSI] qla2xxx: Correct inadvertent clearing of RISC_INTR status. During heavy I/O (CPU-affinity mode enabled) and CLI/Agent interactions, the driver would report periodic mailbox command timeout statuses. Within the CPU-affinity ISR handler, the driver should check the 'disable-msix-handshake' flag in deciding whether or not to clear HCCRX_CLR_RISC_INT. The mode is not specific to a dedicated queue, instead, applies to the current 'ha' context. Signed-off-by: Andrew Vasquez Signed-off-by: Chad Dupuis Signed-off-by: James Bottomley --- drivers/scsi/qla2xxx/qla_isr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index 3474e86e98ab..2516adf1aeea 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -2279,7 +2279,7 @@ qla25xx_msix_rsp_q(int irq, void *dev_id) ha = rsp->hw; /* Clear the interrupt, if enabled, for this response queue */ - if (rsp->options & ~BIT_6) { + if (!ha->flags.disable_msix_handshake) { reg = &ha->iobase->isp24; spin_lock_irqsave(&ha->hardware_lock, flags); WRT_REG_DWORD(®->hccr, HCCRX_CLR_RISC_INT); -- cgit v1.2.3 From c0d6a4d17b3848750b0285861b7a807811a0cfa6 Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Wed, 26 Oct 2011 16:20:53 -0500 Subject: [SCSI] hpsa: set max sectors instead of taking the default Set the max hardware sectors in the SCSI host template to 8192 to allow for larger i/o's (8192 is the same limit the cciss driver currently has.) Signed-off-by: Stephen M. Cameron Signed-off-by: James Bottomley --- drivers/scsi/hpsa.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index 9825ecf34957..43a882bc751d 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -484,6 +484,7 @@ static struct scsi_host_template hpsa_driver_template = { #endif .sdev_attrs = hpsa_sdev_attrs, .shost_attrs = hpsa_shost_attrs, + .max_sectors = 8192, }; -- cgit v1.2.3 From 03ab31f4c14f259bfa160543c83dbfd93d6fb3e2 Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Wed, 26 Oct 2011 16:20:58 -0500 Subject: [SCSI] hpsa: remove unused busy_initializing and busy_scanning Signed-off-by: Stephen M. Cameron Signed-off-by: James Bottomley --- drivers/scsi/hpsa.c | 3 --- drivers/scsi/hpsa.h | 2 -- 2 files changed, 5 deletions(-) diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index 43a882bc751d..c89bed12cb55 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -4121,7 +4121,6 @@ reinit_after_soft_reset: return -ENOMEM; h->pdev = pdev; - h->busy_initializing = 1; h->intr_mode = hpsa_simple_mode ? SIMPLE_MODE_INT : PERF_MODE_INT; INIT_LIST_HEAD(&h->cmpQ); INIT_LIST_HEAD(&h->reqQ); @@ -4230,7 +4229,6 @@ reinit_after_soft_reset: hpsa_hba_inquiry(h); hpsa_register_scsi(h); /* hook ourselves into SCSI subsystem */ - h->busy_initializing = 0; return 1; clean4: @@ -4239,7 +4237,6 @@ clean4: free_irq(h->intr[h->intr_mode], h); clean2: clean1: - h->busy_initializing = 0; kfree(h); return rc; } diff --git a/drivers/scsi/hpsa.h b/drivers/scsi/hpsa.h index 7f53ceaa7239..111b79e32b2b 100644 --- a/drivers/scsi/hpsa.h +++ b/drivers/scsi/hpsa.h @@ -95,8 +95,6 @@ struct ctlr_info { unsigned long *cmd_pool_bits; int nr_allocs; int nr_frees; - int busy_initializing; - int busy_scanning; int scan_finished; spinlock_t scan_lock; wait_queue_head_t scan_wait_queue; -- cgit v1.2.3 From cfe5badcab2e993e71ebebbc07c21c270e5580c0 Mon Sep 17 00:00:00 2001 From: Scott Teel Date: Wed, 26 Oct 2011 16:21:07 -0500 Subject: [SCSI] hpsa: rename HPSA_MAX_SCSI_DEVS_PER_HBA Rename HPSA_MAX_SCSI_DEVS_PER_HBA to HPSA_MAX_DEVICES Signed-off-by: Scott Teel Signed-off-by: Stephen M. Cameron Signed-off-by: James Bottomley --- drivers/scsi/hpsa.c | 23 ++++++++++------------- drivers/scsi/hpsa.h | 4 ++-- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index c89bed12cb55..f661ad1e500c 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -567,16 +567,16 @@ static int hpsa_find_target_lun(struct ctlr_info *h, * assumes h->devlock is held */ int i, found = 0; - DECLARE_BITMAP(lun_taken, HPSA_MAX_SCSI_DEVS_PER_HBA); + DECLARE_BITMAP(lun_taken, HPSA_MAX_DEVICES); - memset(&lun_taken[0], 0, HPSA_MAX_SCSI_DEVS_PER_HBA >> 3); + memset(&lun_taken[0], 0, HPSA_MAX_DEVICES >> 3); for (i = 0; i < h->ndevices; i++) { if (h->dev[i]->bus == bus && h->dev[i]->target != -1) set_bit(h->dev[i]->target, lun_taken); } - for (i = 0; i < HPSA_MAX_SCSI_DEVS_PER_HBA; i++) { + for (i = 0; i < HPSA_MAX_DEVICES; i++) { if (!test_bit(i, lun_taken)) { /* *bus = 1; */ *target = i; @@ -599,7 +599,7 @@ static int hpsa_scsi_add_entry(struct ctlr_info *h, int hostno, unsigned char addr1[8], addr2[8]; struct hpsa_scsi_dev_t *sd; - if (n >= HPSA_MAX_SCSI_DEVS_PER_HBA) { + if (n >= HPSA_MAX_DEVICES) { dev_err(&h->pdev->dev, "too many devices, some will be " "inaccessible.\n"); return -1; @@ -674,7 +674,7 @@ static void hpsa_scsi_replace_entry(struct ctlr_info *h, int hostno, struct hpsa_scsi_dev_t *removed[], int *nremoved) { /* assumes h->devlock is held */ - BUG_ON(entry < 0 || entry >= HPSA_MAX_SCSI_DEVS_PER_HBA); + BUG_ON(entry < 0 || entry >= HPSA_MAX_DEVICES); removed[*nremoved] = h->dev[entry]; (*nremoved)++; @@ -703,7 +703,7 @@ static void hpsa_scsi_remove_entry(struct ctlr_info *h, int hostno, int entry, int i; struct hpsa_scsi_dev_t *sd; - BUG_ON(entry < 0 || entry >= HPSA_MAX_SCSI_DEVS_PER_HBA); + BUG_ON(entry < 0 || entry >= HPSA_MAX_DEVICES); sd = h->dev[entry]; removed[*nremoved] = h->dev[entry]; @@ -815,10 +815,8 @@ static void adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno, int nadded, nremoved; struct Scsi_Host *sh = NULL; - added = kzalloc(sizeof(*added) * HPSA_MAX_SCSI_DEVS_PER_HBA, - GFP_KERNEL); - removed = kzalloc(sizeof(*removed) * HPSA_MAX_SCSI_DEVS_PER_HBA, - GFP_KERNEL); + added = kzalloc(sizeof(*added) * HPSA_MAX_DEVICES, GFP_KERNEL); + removed = kzalloc(sizeof(*removed) * HPSA_MAX_DEVICES, GFP_KERNEL); if (!added || !removed) { dev_warn(&h->pdev->dev, "out of memory in " @@ -1847,8 +1845,7 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno) int raid_ctlr_position; DECLARE_BITMAP(lunzerobits, HPSA_MAX_TARGETS_PER_CTLR); - currentsd = kzalloc(sizeof(*currentsd) * HPSA_MAX_SCSI_DEVS_PER_HBA, - GFP_KERNEL); + currentsd = kzalloc(sizeof(*currentsd) * HPSA_MAX_DEVICES, GFP_KERNEL); physdev_list = kzalloc(reportlunsize, GFP_KERNEL); logdev_list = kzalloc(reportlunsize, GFP_KERNEL); tmpdevice = kzalloc(sizeof(*tmpdevice), GFP_KERNEL); @@ -1957,7 +1954,7 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno) default: break; } - if (ncurrent >= HPSA_MAX_SCSI_DEVS_PER_HBA) + if (ncurrent >= HPSA_MAX_DEVICES) break; } adjust_hpsa_scsi_table(h, hostno, currentsd, ncurrent); diff --git a/drivers/scsi/hpsa.h b/drivers/scsi/hpsa.h index 111b79e32b2b..4de9f71d8bff 100644 --- a/drivers/scsi/hpsa.h +++ b/drivers/scsi/hpsa.h @@ -102,8 +102,8 @@ struct ctlr_info { struct Scsi_Host *scsi_host; spinlock_t devlock; /* to protect hba[ctlr]->dev[]; */ int ndevices; /* number of used elements in .dev[] array. */ -#define HPSA_MAX_SCSI_DEVS_PER_HBA 256 - struct hpsa_scsi_dev_t *dev[HPSA_MAX_SCSI_DEVS_PER_HBA]; +#define HPSA_MAX_DEVICES 256 + struct hpsa_scsi_dev_t *dev[HPSA_MAX_DEVICES]; /* * Performant mode tables. */ -- cgit v1.2.3 From b7ec021fe6fe979dbd4e62604a4942f964b12864 Mon Sep 17 00:00:00 2001 From: Scott Teel Date: Wed, 26 Oct 2011 16:21:12 -0500 Subject: [SCSI] hpsa: fix potential array overflow in hpsa_update_scsi_devices The currentsd[] array in hpsa_update_scsi_devices had room for 256 devices. The code was iterating over however many physical and logical devices plus an additional number of possible external MSA2XXX controllers, which together could potentially exceed 256. We increased the size of the currentsd array to 1024 + 1024 + 32 + 1 elements to reflect a reasonable maximum possible number of devices which might be encountered. We also don't just walk off the end of the array if the array controller reports more devices than we are prepared to handle, we just ignore the excessive devices. Signed-off-by: Scott Teel Signed-off-by: Stephen M. Cameron Signed-off-by: James Bottomley --- drivers/scsi/hpsa.c | 8 +++++++- drivers/scsi/hpsa.h | 1 - drivers/scsi/hpsa_cmd.h | 5 ++++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index f661ad1e500c..f3fd9f1711f7 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -1734,7 +1734,6 @@ static int add_msa2xxx_enclosure_device(struct ctlr_info *h, if (is_scsi_rev_5(h)) return 0; /* p1210m doesn't need to do this. */ -#define MAX_MSA2XXX_ENCLOSURES 32 if (*nmsa2xxx_enclosures >= MAX_MSA2XXX_ENCLOSURES) { dev_warn(&h->pdev->dev, "Maximum number of MSA2XXX " "enclosures exceeded. Check your hardware " @@ -1868,6 +1867,13 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno) /* Allocate the per device structures */ for (i = 0; i < ndevs_to_allocate; i++) { + if (i >= HPSA_MAX_DEVICES) { + dev_warn(&h->pdev->dev, "maximum devices (%d) exceeded." + " %d devices ignored.\n", HPSA_MAX_DEVICES, + ndevs_to_allocate - HPSA_MAX_DEVICES); + break; + } + currentsd[i] = kzalloc(sizeof(*currentsd[i]), GFP_KERNEL); if (!currentsd[i]) { dev_warn(&h->pdev->dev, "out of memory at %s:%d\n", diff --git a/drivers/scsi/hpsa.h b/drivers/scsi/hpsa.h index 4de9f71d8bff..73858bc22e57 100644 --- a/drivers/scsi/hpsa.h +++ b/drivers/scsi/hpsa.h @@ -102,7 +102,6 @@ struct ctlr_info { struct Scsi_Host *scsi_host; spinlock_t devlock; /* to protect hba[ctlr]->dev[]; */ int ndevices; /* number of used elements in .dev[] array. */ -#define HPSA_MAX_DEVICES 256 struct hpsa_scsi_dev_t *dev[HPSA_MAX_DEVICES]; /* * Performant mode tables. diff --git a/drivers/scsi/hpsa_cmd.h b/drivers/scsi/hpsa_cmd.h index 55d741b019db..3fd4715935c2 100644 --- a/drivers/scsi/hpsa_cmd.h +++ b/drivers/scsi/hpsa_cmd.h @@ -123,8 +123,11 @@ union u64bit { /* FIXME this is a per controller value (barf!) */ #define HPSA_MAX_TARGETS_PER_CTLR 16 -#define HPSA_MAX_LUN 256 +#define HPSA_MAX_LUN 1024 #define HPSA_MAX_PHYS_LUN 1024 +#define MAX_MSA2XXX_ENCLOSURES 32 +#define HPSA_MAX_DEVICES (HPSA_MAX_PHYS_LUN + HPSA_MAX_LUN + \ + MAX_MSA2XXX_ENCLOSURES + 1) /* + 1 is for the controller itself */ /* SCSI-3 Commands */ #pragma pack(1) -- cgit v1.2.3 From bb158eabda984851d7964d968b9859383f98a701 Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Wed, 26 Oct 2011 16:21:17 -0500 Subject: [SCSI] hpsa: fix flush cache transfer length We weren't filling in the transfer length of the flush cache command (it transfers 4 bytes of zeroes). Firmware didn't seem to be bothered by this, but it should be fixed. Signed-off-by: Stephen M. Cameron Signed-off-by: James Bottomley --- drivers/scsi/hpsa.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index f3fd9f1711f7..57ed00f7050a 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -2876,6 +2876,8 @@ static void fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h, c->Request.Timeout = 0; c->Request.CDB[0] = BMIC_WRITE; c->Request.CDB[6] = BMIC_CACHE_FLUSH; + c->Request.CDB[7] = (size >> 8) & 0xFF; + c->Request.CDB[8] = size & 0xFF; break; case TEST_UNIT_READY: c->Request.CDBLen = 6; -- cgit v1.2.3 From a0c124137a40fc22730ae87caf17e821f2dce1ed Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Wed, 26 Oct 2011 16:22:04 -0500 Subject: [SCSI] hpsa: detect controller lockup When controller lockup condition is detected, we should fail all outstanding commands and disable the controller. This will enable multipath solutions to recover gracefully. Signed-off-by: Stephen M. Cameron Signed-off-by: James Bottomley --- drivers/scsi/hpsa.c | 184 ++++++++++++++++++++++++++++++++++++++++++++++++++-- drivers/scsi/hpsa.h | 5 ++ 2 files changed, 185 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index 57ed00f7050a..e0119377ffe3 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -48,6 +48,7 @@ #include #include #include +#include #include "hpsa_cmd.h" #include "hpsa.h" @@ -127,6 +128,10 @@ static struct board_type products[] = { static int number_of_controllers; +static struct list_head hpsa_ctlr_list = LIST_HEAD_INIT(hpsa_ctlr_list); +static spinlock_t lockup_detector_lock; +static struct task_struct *hpsa_lockup_detector; + static irqreturn_t do_hpsa_intr_intx(int irq, void *dev_id); static irqreturn_t do_hpsa_intr_msi(int irq, void *dev_id); static int hpsa_ioctl(struct scsi_device *dev, int cmd, void *arg); @@ -1337,6 +1342,22 @@ static inline void hpsa_scsi_do_simple_cmd_core(struct ctlr_info *h, wait_for_completion(&wait); } +static void hpsa_scsi_do_simple_cmd_core_if_no_lockup(struct ctlr_info *h, + struct CommandList *c) +{ + unsigned long flags; + + /* If controller lockup detected, fake a hardware error. */ + spin_lock_irqsave(&h->lock, flags); + if (unlikely(h->lockup_detected)) { + spin_unlock_irqrestore(&h->lock, flags); + c->err_info->CommandStatus = CMD_HARDWARE_ERR; + } else { + spin_unlock_irqrestore(&h->lock, flags); + hpsa_scsi_do_simple_cmd_core(h, c); + } +} + static void hpsa_scsi_do_simple_cmd_with_retry(struct ctlr_info *h, struct CommandList *c, int data_direction) { @@ -2052,8 +2073,14 @@ static int hpsa_scsi_queue_command_lck(struct scsi_cmnd *cmd, } memcpy(scsi3addr, dev->scsi3addr, sizeof(scsi3addr)); - /* Need a lock as this is being allocated from the pool */ spin_lock_irqsave(&h->lock, flags); + if (unlikely(h->lockup_detected)) { + spin_unlock_irqrestore(&h->lock, flags); + cmd->result = DID_ERROR << 16; + done(cmd); + return 0; + } + /* Need a lock as this is being allocated from the pool */ c = cmd_alloc(h); spin_unlock_irqrestore(&h->lock, flags); if (c == NULL) { /* trouble... */ @@ -2605,7 +2632,7 @@ static int hpsa_passthru_ioctl(struct ctlr_info *h, void __user *argp) c->SG[0].Len = iocommand.buf_size; c->SG[0].Ext = 0; /* we are not chaining*/ } - hpsa_scsi_do_simple_cmd_core(h, c); + hpsa_scsi_do_simple_cmd_core_if_no_lockup(h, c); if (iocommand.buf_size > 0) hpsa_pci_unmap(h->pdev, c, 1, PCI_DMA_BIDIRECTIONAL); check_ioctl_unit_attention(h, c); @@ -2728,7 +2755,7 @@ static int hpsa_big_passthru_ioctl(struct ctlr_info *h, void __user *argp) c->SG[i].Ext = 0; } } - hpsa_scsi_do_simple_cmd_core(h, c); + hpsa_scsi_do_simple_cmd_core_if_no_lockup(h, c); if (sg_used) hpsa_pci_unmap(h->pdev, c, sg_used, PCI_DMA_BIDIRECTIONAL); check_ioctl_unit_attention(h, c); @@ -3097,6 +3124,7 @@ static irqreturn_t hpsa_intx_discard_completions(int irq, void *dev_id) if (interrupt_not_for_us(h)) return IRQ_NONE; spin_lock_irqsave(&h->lock, flags); + h->last_intr_timestamp = get_jiffies_64(); while (interrupt_pending(h)) { raw_tag = get_next_completion(h); while (raw_tag != FIFO_EMPTY) @@ -3116,6 +3144,7 @@ static irqreturn_t hpsa_msix_discard_completions(int irq, void *dev_id) return IRQ_NONE; spin_lock_irqsave(&h->lock, flags); + h->last_intr_timestamp = get_jiffies_64(); raw_tag = get_next_completion(h); while (raw_tag != FIFO_EMPTY) raw_tag = next_command(h); @@ -3132,6 +3161,7 @@ static irqreturn_t do_hpsa_intr_intx(int irq, void *dev_id) if (interrupt_not_for_us(h)) return IRQ_NONE; spin_lock_irqsave(&h->lock, flags); + h->last_intr_timestamp = get_jiffies_64(); while (interrupt_pending(h)) { raw_tag = get_next_completion(h); while (raw_tag != FIFO_EMPTY) { @@ -3152,6 +3182,7 @@ static irqreturn_t do_hpsa_intr_msi(int irq, void *dev_id) u32 raw_tag; spin_lock_irqsave(&h->lock, flags); + h->last_intr_timestamp = get_jiffies_64(); raw_tag = get_next_completion(h); while (raw_tag != FIFO_EMPTY) { if (hpsa_tag_contains_index(raw_tag)) @@ -4089,6 +4120,149 @@ static void hpsa_undo_allocations_after_kdump_soft_reset(struct ctlr_info *h) kfree(h); } +static void remove_ctlr_from_lockup_detector_list(struct ctlr_info *h) +{ + assert_spin_locked(&lockup_detector_lock); + if (!hpsa_lockup_detector) + return; + if (h->lockup_detected) + return; /* already stopped the lockup detector */ + list_del(&h->lockup_list); +} + +/* Called when controller lockup detected. */ +static void fail_all_cmds_on_list(struct ctlr_info *h, struct list_head *list) +{ + struct CommandList *c = NULL; + + assert_spin_locked(&h->lock); + /* Mark all outstanding commands as failed and complete them. */ + while (!list_empty(list)) { + c = list_entry(list->next, struct CommandList, list); + c->err_info->CommandStatus = CMD_HARDWARE_ERR; + finish_cmd(c, c->Header.Tag.lower); + } +} + +static void controller_lockup_detected(struct ctlr_info *h) +{ + unsigned long flags; + + assert_spin_locked(&lockup_detector_lock); + remove_ctlr_from_lockup_detector_list(h); + h->access.set_intr_mask(h, HPSA_INTR_OFF); + spin_lock_irqsave(&h->lock, flags); + h->lockup_detected = readl(h->vaddr + SA5_SCRATCHPAD_OFFSET); + spin_unlock_irqrestore(&h->lock, flags); + dev_warn(&h->pdev->dev, "Controller lockup detected: 0x%08x\n", + h->lockup_detected); + pci_disable_device(h->pdev); + spin_lock_irqsave(&h->lock, flags); + fail_all_cmds_on_list(h, &h->cmpQ); + fail_all_cmds_on_list(h, &h->reqQ); + spin_unlock_irqrestore(&h->lock, flags); +} + +#define HEARTBEAT_SAMPLE_INTERVAL (10 * HZ) +#define HEARTBEAT_CHECK_MINIMUM_INTERVAL (HEARTBEAT_SAMPLE_INTERVAL / 2) + +static void detect_controller_lockup(struct ctlr_info *h) +{ + u64 now; + u32 heartbeat; + unsigned long flags; + + assert_spin_locked(&lockup_detector_lock); + now = get_jiffies_64(); + /* If we've received an interrupt recently, we're ok. */ + if (time_after64(h->last_intr_timestamp + + (HEARTBEAT_CHECK_MINIMUM_INTERVAL), now)) + return; + + /* + * If we've already checked the heartbeat recently, we're ok. + * This could happen if someone sends us a signal. We + * otherwise don't care about signals in this thread. + */ + if (time_after64(h->last_heartbeat_timestamp + + (HEARTBEAT_CHECK_MINIMUM_INTERVAL), now)) + return; + + /* If heartbeat has not changed since we last looked, we're not ok. */ + spin_lock_irqsave(&h->lock, flags); + heartbeat = readl(&h->cfgtable->HeartBeat); + spin_unlock_irqrestore(&h->lock, flags); + if (h->last_heartbeat == heartbeat) { + controller_lockup_detected(h); + return; + } + + /* We're ok. */ + h->last_heartbeat = heartbeat; + h->last_heartbeat_timestamp = now; +} + +static int detect_controller_lockup_thread(void *notused) +{ + struct ctlr_info *h; + unsigned long flags; + + while (1) { + struct list_head *this, *tmp; + + schedule_timeout_interruptible(HEARTBEAT_SAMPLE_INTERVAL); + if (kthread_should_stop()) + break; + spin_lock_irqsave(&lockup_detector_lock, flags); + list_for_each_safe(this, tmp, &hpsa_ctlr_list) { + h = list_entry(this, struct ctlr_info, lockup_list); + detect_controller_lockup(h); + } + spin_unlock_irqrestore(&lockup_detector_lock, flags); + } + return 0; +} + +static void add_ctlr_to_lockup_detector_list(struct ctlr_info *h) +{ + unsigned long flags; + + spin_lock_irqsave(&lockup_detector_lock, flags); + list_add_tail(&h->lockup_list, &hpsa_ctlr_list); + spin_unlock_irqrestore(&lockup_detector_lock, flags); +} + +static void start_controller_lockup_detector(struct ctlr_info *h) +{ + /* Start the lockup detector thread if not already started */ + if (!hpsa_lockup_detector) { + spin_lock_init(&lockup_detector_lock); + hpsa_lockup_detector = + kthread_run(detect_controller_lockup_thread, + NULL, "hpsa"); + } + if (!hpsa_lockup_detector) { + dev_warn(&h->pdev->dev, + "Could not start lockup detector thread\n"); + return; + } + add_ctlr_to_lockup_detector_list(h); +} + +static void stop_controller_lockup_detector(struct ctlr_info *h) +{ + unsigned long flags; + + spin_lock_irqsave(&lockup_detector_lock, flags); + remove_ctlr_from_lockup_detector_list(h); + /* If the list of ctlr's to monitor is empty, stop the thread */ + if (list_empty(&hpsa_ctlr_list)) { + kthread_stop(hpsa_lockup_detector); + hpsa_lockup_detector = NULL; + } + spin_unlock_irqrestore(&lockup_detector_lock, flags); +} + static int __devinit hpsa_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -4234,6 +4408,7 @@ reinit_after_soft_reset: hpsa_hba_inquiry(h); hpsa_register_scsi(h); /* hook ourselves into SCSI subsystem */ + start_controller_lockup_detector(h); return 1; clean4: @@ -4296,10 +4471,11 @@ static void __devexit hpsa_remove_one(struct pci_dev *pdev) struct ctlr_info *h; if (pci_get_drvdata(pdev) == NULL) { - dev_err(&pdev->dev, "unable to remove device \n"); + dev_err(&pdev->dev, "unable to remove device\n"); return; } h = pci_get_drvdata(pdev); + stop_controller_lockup_detector(h); hpsa_unregister_scsi(h); /* unhook from SCSI subsystem */ hpsa_shutdown(pdev); iounmap(h->vaddr); diff --git a/drivers/scsi/hpsa.h b/drivers/scsi/hpsa.h index 73858bc22e57..91edafb8c7e6 100644 --- a/drivers/scsi/hpsa.h +++ b/drivers/scsi/hpsa.h @@ -121,6 +121,11 @@ struct ctlr_info { unsigned char reply_pool_wraparound; u32 *blockFetchTable; unsigned char *hba_inquiry_data; + u64 last_intr_timestamp; + u32 last_heartbeat; + u64 last_heartbeat_timestamp; + u32 lockup_detected; + struct list_head lockup_list; }; #define HPSA_ABORT_MSG 0 #define HPSA_DEVICE_RESET_MSG 1 -- cgit v1.2.3 From a18a920c70d48a8e4a2b750d8a183b3c1a4be514 Mon Sep 17 00:00:00 2001 From: "Moger, Babu" Date: Wed, 26 Oct 2011 14:29:38 -0400 Subject: [SCSI] scsi_dh: check queuedata pointer before proceeding further This patch validates sdev pointer in scsi_dh_activate before proceeding further. Without this check we might see the panic as below. I have seen this panic multiple times.. Call trace: #0 [ffff88007d647b50] machine_kexec at ffffffff81020902 #1 [ffff88007d647ba0] crash_kexec at ffffffff810875b0 #2 [ffff88007d647c70] oops_end at ffffffff8139c650 #3 [ffff88007d647c90] __bad_area_nosemaphore at ffffffff8102dd15 #4 [ffff88007d647d50] page_fault at ffffffff8139b8cf [exception RIP: scsi_dh_activate+0x82] RIP: ffffffffa0041922 RSP: ffff88007d647e00 RFLAGS: 00010046 RAX: 0000000000000000 RBX: 0000000000000000 RCX: 00000000000093c5 RDX: 00000000000093c5 RSI: ffffffffa02e6640 RDI: ffff88007cc88988 RBP: 000000000000000f R8: ffff88007d646000 R9: 0000000000000000 R10: ffff880082293790 R11: 00000000ffffffff R12: ffff88007cc88988 R13: 0000000000000000 R14: 0000000000000286 R15: ffff880037b845e0 ORIG_RAX: ffffffffffffffff CS: 0010 SS: 0000 #5 [ffff88007d647e38] run_workqueue at ffffffff81060268 #6 [ffff88007d647e78] worker_thread at ffffffff81060386 #7 [ffff88007d647ee8] kthread at ffffffff81064436 #8 [ffff88007d647f48] kernel_thread at ffffffff81003fba Signed-off-by: Babu Moger Cc: stable@kernel.org Signed-off-by: James Bottomley --- drivers/scsi/device_handler/scsi_dh.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/device_handler/scsi_dh.c b/drivers/scsi/device_handler/scsi_dh.c index 7c05fd9dccfd..339ea23a8675 100644 --- a/drivers/scsi/device_handler/scsi_dh.c +++ b/drivers/scsi/device_handler/scsi_dh.c @@ -441,7 +441,15 @@ int scsi_dh_activate(struct request_queue *q, activate_complete fn, void *data) spin_lock_irqsave(q->queue_lock, flags); sdev = q->queuedata; - if (sdev && sdev->scsi_dh_data) + if (!sdev) { + spin_unlock_irqrestore(q->queue_lock, flags); + err = SCSI_DH_NOSYS; + if (fn) + fn(data, err); + return err; + } + + if (sdev->scsi_dh_data) scsi_dh = sdev->scsi_dh_data->scsi_dh; dev = get_device(&sdev->sdev_gendev); if (!scsi_dh || !dev || -- cgit v1.2.3 From 5a918353ec97bbce2af83a950eb38e2781bfe9e7 Mon Sep 17 00:00:00 2001 From: Wayne Boyer Date: Thu, 27 Oct 2011 11:58:21 -0700 Subject: [SCSI] ipr: add definitions for additional adapter Add the appropriate definition and table entry for an additional adapter. Signed-off-by: Wayne Boyer Acked-by: Brian King Signed-off-by: James Bottomley --- drivers/scsi/ipr.c | 2 ++ drivers/scsi/ipr.h | 1 + 2 files changed, 3 insertions(+) diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index 73e24b48dced..fd860d952b28 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -9122,6 +9122,8 @@ static struct pci_device_id ipr_pci_table[] __devinitdata = { PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_574D, 0, 0, 0 }, { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROC_FPGA_E2, PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57B2, 0, 0, 0 }, + { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROC_FPGA_E2, + PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57C3, 0, 0, 0 }, { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROC_FPGA_E2, PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57C4, 0, 0, 0 }, { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROC_ASIC_E2, diff --git a/drivers/scsi/ipr.h b/drivers/scsi/ipr.h index 6d257e0dd6a5..ac84736c1b9c 100644 --- a/drivers/scsi/ipr.h +++ b/drivers/scsi/ipr.h @@ -82,6 +82,7 @@ #define IPR_SUBS_DEV_ID_57B4 0x033B #define IPR_SUBS_DEV_ID_57B2 0x035F +#define IPR_SUBS_DEV_ID_57C3 0x0353 #define IPR_SUBS_DEV_ID_57C4 0x0354 #define IPR_SUBS_DEV_ID_57C6 0x0357 #define IPR_SUBS_DEV_ID_57CC 0x035C -- cgit v1.2.3 From 0e2e27990e2dcd415f7974e8460a2f05accdddfb Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 27 Oct 2011 15:04:50 -0700 Subject: [SCSI] isci: Lookup device references through requests in completions. The LLDD needs to obtain a reference to the device through the request itself and not through the domain_device, because the domain_device.lldd_dev is set to NULL early in the lldd_dev_gone call. This relies on the fact that the isci_remote_device object is keeping a seperate reference count of outstanding requests. TODO: unify the request count tracking with the isci_remote_device kref. The failure signature of this condition looks like the following log, where the important bits are the call to lldd_dev_gone followed by a crash in isci_terminate_request_core: [ 229.151541] isci 0000:0b:00.0: isci_remote_device_gone: domain_device = ffff8801492d4800, isci_device = ffff880143c657d0, isci_port = ffff880143c63658 [ 229.166007] isci 0000:0b:00.0: isci_remote_device_stop: isci_device = ffff880143c657d0 [ 229.175317] isci 0000:0b:00.0: isci_terminate_pending_requests: idev=ffff880143c657d0 request=ffff88014741f000; task=ffff8801470f46c0 old_state=2 [ 229.189702] isci 0000:0b:00.0: isci_terminate_request_core: device = ffff880143c657d0; request = ffff88014741f000 [ 229.201339] isci 0000:0b:00.0: isci_terminate_request_core: before completion wait (ffff88014741f000/ffff880149715ad0) [ 229.213414] isci 0000:0b:00.0: sci_controller_process_completions: completion queue entry:0x8000a0e9 [ 229.214401] BUG: unable to handle kernel NULL pointer dereference at 0000000000000228 [ 229.214401] IP:jdskirvi-testlbo [] sci_request_completed_state_enter+0x50/0xafb [isci] [ 229.214401] PGD 13d19e067 PUD 13d104067 PMD 0 [ 229.214401] Oops: 0000 [#1] SMP [ 229.214401] CPU 0 x kernel: [ 226 [ 229.214401] Modules linked in: ipv6 dm_multipath uinput nouveau snd_hda_codec_realtek snd_hda_intel ttm drm_kms_helper drm snd_hda_codec snd_hwdep snd_pcm snd_timer i2c_algo_bit isci snd libsas ioatdma mxm_wmi iTCO_wdt soundcore snd_page_alloc scsi_transport_sas iTCO_vendor_support wmi dca video i2c_i801 i2c_core [last unloaded: speedstep_lib] [ 229.214401] [ 229.214401] Pid: 5, comm: kworker/u:0 Not tainted 3.0.0-isci-11.7.29+ #30.353196] Buffer Intel Corporation Stoakley/Pearlcity Workstation [ 229.214401] RIP: 0010:[] I/O error on dev [] sci_request_completed_state_enter+0x50/0xafb [isci] [ 229.214401] RSP: 0018:ffff88014fc03d20 EFLAGS: 00010046 [ 229.214401] RAX: 0000000000000000 RBX: ffff88014741f000 RCX: 0000000000000000 [ 229.214401] RDX: ffffffffa00b2c90 RSI: 0000000000000017 RDI: ffff88014741f0a0 [ 229.214401] RBP: ffff88014fc03d90 R08: 0000000000000018 R09: 0000000000000000 [ 229.214401] R10: 0000000000000000 R11: ffffffff81a17d98 R12: 000000000000001d [ 229.214401] R13: ffff8801470f46c0 R14: 0000000000000000 R15: 0000000000008000 [ 229.214401] FS: 0000000000000000(0000) GS:ffff88014fc00000(0000) knlGS:0000000000000000 [ 229.214401] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b [ 229.214401] CR2: 0000000000000228 CR3: 000000013ceaa000 CR4: 00000000000406f0 [ 229.214401] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 229.214401] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 [ 229.214401] Process kworker/u:0 (pid: 5, threadinfo ffff880149714000, task ffff880149718000) [ 229.214401] Call Trace: [ 229.214401] [ 229.214401] [] sci_change_state+0x4a/0x4f [isci] [ 229.214401] [] sci_io_request_tc_completion+0x79c/0x7a0 [isci] [ 229.214401] [] sci_controller_process_completions+0x14f/0x396 [isci] [ 229.214401] [] ? spin_lock_irq+0xe/0x10 [isci] [ 229.214401] [] isci_host_completion_routine+0x71/0x2be [isci] [ 229.214401] [] ? mark_held_locks+0x52/0x70 [ 229.214401] [] tasklet_action+0x90/0xf1 [ 229.214401] [] __do_softirq+0xe5/0x1bf [ 229.214401] [] ? hrtimer_interrupt+0x129/0x1bb [ 229.214401] [] call_softirq+0x1c/0x30 [ 229.214401] [] do_softirq+0x4b/0xa3 [ 229.214401] [] irq_exit+0x53/0xb4 [ 229.214401] [] smp_apic_timer_interrupt+0x83/0x91 [ 229.214401] [] apic_timer_interrupt+0x13/0x20 [ 229.214401] [ 229.214401] [] ? retint_restore_args+0x13/0x13 [ 229.214401] [] ? trace_hardirqs_off+0xd/0xf [ 229.214401] [] ? vprintk+0x40b/0x452 [ 229.214401] [] printk+0x41/0x47 [ 229.214401] [] __dev_printk+0x78/0x7a [ 229.214401] [] dev_printk+0x45/0x47 [ 229.214401] [] isci_terminate_request_core+0x15d/0x317 [isci] [ 229.214401] [] isci_terminate_pending_requests+0x1a4/0x204 [isci] [ 229.214401] [] ? sas_phye_oob_error+0xc3/0xc3 [libsas] [ 229.214401] [] isci_remote_device_nuke_requests+0xa6/0xff [isci] [ 229.214401] [] isci_remote_device_stop+0x7c/0x166 [isci] [ 229.214401] [] ? sas_phye_oob_error+0xc3/0xc3 [libsas] [ 229.214401] [] isci_remote_device_gone+0x76/0x7e [isci] [ 229.214401] [] sas_notify_lldd_dev_gone+0x34/0x36 [libsas] [ 229.214401] [] sas_unregister_dev+0x57/0x9c [libsas] [ 229.214401] [] sas_unregister_domain_devices+0x36/0x65 [libsas] [ 229.214401] [] sas_deform_port+0x72/0x1ac [libsas] [ 229.214401] [] ? sas_phye_oob_error+0xc3/0xc3 [libsas] [ 229.214401] [] sas_phye_loss_of_signal+0x3e/0x42 [libsas] Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/isci/request.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/isci/request.c b/drivers/scsi/isci/request.c index 565a9f0a9bc2..bfc7379727b1 100644 --- a/drivers/scsi/isci/request.c +++ b/drivers/scsi/isci/request.c @@ -2728,9 +2728,9 @@ static void isci_request_io_request_complete(struct isci_host *ihost, struct sas_task *task = isci_request_access_task(request); struct ssp_response_iu *resp_iu; unsigned long task_flags; - struct isci_remote_device *idev = isci_lookup_device(task->dev); - enum service_response response = SAS_TASK_UNDELIVERED; - enum exec_status status = SAS_ABORTED_TASK; + struct isci_remote_device *idev = request->target_device; + enum service_response response = SAS_TASK_UNDELIVERED; + enum exec_status status = SAS_ABORTED_TASK; enum isci_request_status request_status; enum isci_completion_selection complete_to_host = isci_perform_normal_io_completion; @@ -3061,7 +3061,6 @@ static void isci_request_io_request_complete(struct isci_host *ihost, /* complete the io request to the core. */ sci_controller_complete_io(ihost, request->target_device, request); - isci_put_device(idev); /* set terminated handle so it cannot be completed or * terminated again, and to cause any calls into abort -- cgit v1.2.3 From c2cb8a5fd7d5d8729a4fc25937c4d6564f9a7aa3 Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 27 Oct 2011 15:04:56 -0700 Subject: [SCSI] isci: Immediately fail I/O to removed devices. In the case where an I/O fails to start in isci_request_execute, only allow retries if the device is not already gone. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/isci/task.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index e2d9418683ce..e8cf17b024a4 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c @@ -212,16 +212,27 @@ int isci_task_execute_task(struct sas_task *task, int num, gfp_t gfp_flags) task->task_state_flags &= ~SAS_TASK_AT_INITIATOR; spin_unlock_irqrestore(&task->task_state_lock, flags); - /* Indicate QUEUE_FULL so that the scsi - * midlayer retries. if the request - * failed for remote device reasons, - * it gets returned as - * SAS_TASK_UNDELIVERED next time - * through. - */ - isci_task_refuse(ihost, task, - SAS_TASK_COMPLETE, - SAS_QUEUE_FULL); + if (test_bit(IDEV_GONE, &idev->flags)) { + + /* Indicate that the device + * is gone. + */ + isci_task_refuse(ihost, task, + SAS_TASK_UNDELIVERED, + SAS_DEVICE_UNKNOWN); + } else { + /* Indicate QUEUE_FULL so that + * the scsi midlayer retries. + * If the request failed for + * remote device reasons, it + * gets returned as + * SAS_TASK_UNDELIVERED next + * time through. + */ + isci_task_refuse(ihost, task, + SAS_TASK_COMPLETE, + SAS_QUEUE_FULL); + } } } } -- cgit v1.2.3 From d6891682220c18c01bf6838f30e37342c38fde44 Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 27 Oct 2011 15:05:01 -0700 Subject: [SCSI] isci: Fix tag leak in tasks and terminated requests. Make sure terminated requests and completed task tags are freed. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/isci/task.c | 51 +++++++++++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index e8cf17b024a4..7180b048c34b 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c @@ -552,11 +552,27 @@ static void isci_request_cleanup_completed_loiterer( if (isci_request != NULL) { spin_lock_irqsave(&isci_host->scic_lock, flags); + isci_free_tag(isci_host, isci_request->io_tag); + isci_request_change_state(isci_request, unallocated); list_del_init(&isci_request->dev_node); spin_unlock_irqrestore(&isci_host->scic_lock, flags); } } +static int isci_request_is_dealloc_managed(enum isci_request_status stat) +{ + switch (stat) { + case aborted: + case aborting: + case terminating: + case completed: + case dead: + return true; + default: + return false; + } +} + /** * isci_terminate_request_core() - This function will terminate the given * request, and wait for it to complete. This function must only be called @@ -574,7 +590,6 @@ static void isci_terminate_request_core(struct isci_host *ihost, enum sci_status status = SCI_SUCCESS; bool was_terminated = false; bool needs_cleanup_handling = false; - enum isci_request_status request_status; unsigned long flags; unsigned long termination_completed = 1; struct completion *io_request_completion; @@ -702,23 +717,11 @@ static void isci_terminate_request_core(struct isci_host *ihost, * needs to be detached and freed here. */ spin_lock_irqsave(&isci_request->state_lock, flags); - request_status = isci_request->status; - - if ((isci_request->ttype == io_task) /* TMFs are in their own thread */ - && ((request_status == aborted) - || (request_status == aborting) - || (request_status == terminating) - || (request_status == completed) - || (request_status == dead) - ) - ) { - - /* The completion routine won't free a request in - * the aborted/aborting/etc. states, so we do - * it here. - */ - needs_cleanup_handling = true; - } + + needs_cleanup_handling + = isci_request_is_dealloc_managed( + isci_request->status); + spin_unlock_irqrestore(&isci_request->state_lock, flags); } @@ -1329,8 +1332,16 @@ isci_task_request_complete(struct isci_host *ihost, */ set_bit(IREQ_TERMINATED, &ireq->flags); - isci_request_change_state(ireq, unallocated); - list_del_init(&ireq->dev_node); + /* As soon as something is in the terminate path, deallocation is + * managed there. Note that the final non-managed state of a task + * request is "completed". + */ + if ((ireq->status == completed) || + !isci_request_is_dealloc_managed(ireq->status)) { + isci_request_change_state(ireq, unallocated); + isci_free_tag(ihost, ireq->io_tag); + list_del_init(&ireq->dev_node); + } /* The task management part completes last. */ complete(tmf_complete); -- cgit v1.2.3 From b343dff1a269bcc0eac123ef541c5476b03d52c1 Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 27 Oct 2011 15:05:06 -0700 Subject: [SCSI] isci: Handle task request timeouts correctly. In the case where "task" requests timeout (note that this class of requests can also include SATA/STP soft reset FIS transmissions), handle the case where the task was being managed by some call to terminate the task request by completing both the tmf and the aborting process. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/isci/task.c | 149 ++++++++++++++++++++++++++++++++++------------- drivers/scsi/isci/task.h | 2 + 2 files changed, 109 insertions(+), 42 deletions(-) diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index 7180b048c34b..4175f173868e 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c @@ -338,6 +338,61 @@ static struct isci_request *isci_task_request_build(struct isci_host *ihost, return ireq; } +/** +* isci_request_mark_zombie() - This function must be called with scic_lock held. +*/ +static void isci_request_mark_zombie(struct isci_host *ihost, struct isci_request *ireq) +{ + struct completion *tmf_completion = NULL; + struct completion *req_completion; + + /* Set the request state to "dead". */ + ireq->status = dead; + + req_completion = ireq->io_request_completion; + ireq->io_request_completion = NULL; + + if (ireq->ttype == io_task) { + + /* Break links with the sas_task - the callback is done + * elsewhere. + */ + struct sas_task *task = isci_request_access_task(ireq); + + if (task) + task->lldd_task = NULL; + + ireq->ttype_ptr.io_task_ptr = NULL; + } else { + /* Break links with the TMF request. */ + struct isci_tmf *tmf = isci_request_access_tmf(ireq); + + /* In the case where a task request is dying, + * the thread waiting on the complete will sit and + * timeout unless we wake it now. Since the TMF + * has a default error status, complete it here + * to wake the waiting thread. + */ + if (tmf) { + tmf_completion = tmf->complete; + tmf->complete = NULL; + } + ireq->ttype_ptr.tmf_task_ptr = NULL; + dev_dbg(&ihost->pdev->dev, "%s: tmf_code %d, managed tag %#x\n", + __func__, tmf->tmf_code, tmf->io_tag); + } + + dev_warn(&ihost->pdev->dev, "task context unrecoverable (tag: %#x)\n", + ireq->io_tag); + + /* Don't force waiting threads to timeout. */ + if (req_completion) + complete(req_completion); + + if (tmf_completion != NULL) + complete(tmf_completion); +} + static int isci_task_execute_tmf(struct isci_host *ihost, struct isci_remote_device *idev, struct isci_tmf *tmf, unsigned long timeout_ms) @@ -375,6 +430,7 @@ static int isci_task_execute_tmf(struct isci_host *ihost, /* Assign the pointer to the TMF's completion kernel wait structure. */ tmf->complete = &completion; + tmf->status = SCI_FAILURE_TIMEOUT; ireq = isci_task_request_build(ihost, idev, tag, tmf); if (!ireq) @@ -410,18 +466,35 @@ static int isci_task_execute_tmf(struct isci_host *ihost, msecs_to_jiffies(timeout_ms)); if (timeleft == 0) { + /* The TMF did not complete - this could be because + * of an unplug. Terminate the TMF request now. + */ spin_lock_irqsave(&ihost->scic_lock, flags); if (tmf->cb_state_func != NULL) - tmf->cb_state_func(isci_tmf_timed_out, tmf, tmf->cb_data); + tmf->cb_state_func(isci_tmf_timed_out, tmf, + tmf->cb_data); - sci_controller_terminate_request(ihost, - idev, - ireq); + sci_controller_terminate_request(ihost, idev, ireq); spin_unlock_irqrestore(&ihost->scic_lock, flags); - wait_for_completion(tmf->complete); + timeleft = wait_for_completion_timeout( + &completion, + msecs_to_jiffies(ISCI_TERMINATION_TIMEOUT_MSEC)); + + if (!timeleft) { + /* Strange condition - the termination of the TMF + * request timed-out. + */ + spin_lock_irqsave(&ihost->scic_lock, flags); + + /* If the TMF status has not changed, kill it. */ + if (tmf->status == SCI_FAILURE_TIMEOUT) + isci_request_mark_zombie(ihost, ireq); + + spin_unlock_irqrestore(&ihost->scic_lock, flags); + } } isci_print_tmf(tmf); @@ -645,42 +718,27 @@ static void isci_terminate_request_core(struct isci_host *ihost, __func__, isci_request, io_request_completion); /* Wait here for the request to complete. */ - #define TERMINATION_TIMEOUT_MSEC 500 termination_completed = wait_for_completion_timeout( io_request_completion, - msecs_to_jiffies(TERMINATION_TIMEOUT_MSEC)); + msecs_to_jiffies(ISCI_TERMINATION_TIMEOUT_MSEC)); if (!termination_completed) { /* The request to terminate has timed out. */ - spin_lock_irqsave(&ihost->scic_lock, - flags); + spin_lock_irqsave(&ihost->scic_lock, flags); /* Check for state changes. */ - if (!test_bit(IREQ_TERMINATED, &isci_request->flags)) { + if (!test_bit(IREQ_TERMINATED, + &isci_request->flags)) { /* The best we can do is to have the * request die a silent death if it * ever really completes. - * - * Set the request state to "dead", - * and clear the task pointer so that - * an actual completion event callback - * doesn't do anything. */ - isci_request->status = dead; - isci_request->io_request_completion - = NULL; - - if (isci_request->ttype == io_task) { - - /* Break links with the - * sas_task. - */ - isci_request->ttype_ptr.io_task_ptr - = NULL; - } + isci_request_mark_zombie(ihost, + isci_request); + needs_cleanup_handling = true; } else termination_completed = 1; @@ -1302,7 +1360,8 @@ isci_task_request_complete(struct isci_host *ihost, enum sci_task_status completion_status) { struct isci_tmf *tmf = isci_request_access_tmf(ireq); - struct completion *tmf_complete; + struct completion *tmf_complete = NULL; + struct completion *request_complete = ireq->io_request_completion; dev_dbg(&ihost->pdev->dev, "%s: request = %p, status=%d\n", @@ -1310,22 +1369,23 @@ isci_task_request_complete(struct isci_host *ihost, isci_request_change_state(ireq, completed); - tmf->status = completion_status; set_bit(IREQ_COMPLETE_IN_TARGET, &ireq->flags); - if (tmf->proto == SAS_PROTOCOL_SSP) { - memcpy(&tmf->resp.resp_iu, - &ireq->ssp.rsp, - SSP_RESP_IU_MAX_SIZE); - } else if (tmf->proto == SAS_PROTOCOL_SATA) { - memcpy(&tmf->resp.d2h_fis, - &ireq->stp.rsp, - sizeof(struct dev_to_host_fis)); + if (tmf) { + tmf->status = completion_status; + + if (tmf->proto == SAS_PROTOCOL_SSP) { + memcpy(&tmf->resp.resp_iu, + &ireq->ssp.rsp, + SSP_RESP_IU_MAX_SIZE); + } else if (tmf->proto == SAS_PROTOCOL_SATA) { + memcpy(&tmf->resp.d2h_fis, + &ireq->stp.rsp, + sizeof(struct dev_to_host_fis)); + } + /* PRINT_TMF( ((struct isci_tmf *)request->task)); */ + tmf_complete = tmf->complete; } - - /* PRINT_TMF( ((struct isci_tmf *)request->task)); */ - tmf_complete = tmf->complete; - sci_controller_complete_io(ihost, ireq->target_device, ireq); /* set the 'terminated' flag handle to make sure it cannot be terminated * or completed again. @@ -1343,8 +1403,13 @@ isci_task_request_complete(struct isci_host *ihost, list_del_init(&ireq->dev_node); } + /* "request_complete" is set if the task was being terminated. */ + if (request_complete) + complete(request_complete); + /* The task management part completes last. */ - complete(tmf_complete); + if (tmf_complete) + complete(tmf_complete); } static void isci_smp_task_timedout(unsigned long _task) diff --git a/drivers/scsi/isci/task.h b/drivers/scsi/isci/task.h index 15b18d158993..c9ccd0b5ff53 100644 --- a/drivers/scsi/isci/task.h +++ b/drivers/scsi/isci/task.h @@ -58,6 +58,8 @@ #include #include "host.h" +#define ISCI_TERMINATION_TIMEOUT_MSEC 500 + struct isci_request; /** -- cgit v1.2.3 From db49c2d037d50dfc67b29a4e013d6250ca97c3aa Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 27 Oct 2011 15:05:11 -0700 Subject: [SCSI] isci: No task_done callbacks in error handler paths. libsas will cleanup pending sas_tasks after error handler path functions are called; do not call task_done callbacks. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/isci/task.c | 69 ++++++++++-------------------------------------- 1 file changed, 14 insertions(+), 55 deletions(-) diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index 4175f173868e..c1f439bed717 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c @@ -585,53 +585,6 @@ static enum isci_request_status isci_task_validate_request_to_abort( return old_state; } -/** -* isci_request_cleanup_completed_loiterer() - This function will take care of -* the final cleanup on any request which has been explicitly terminated. -* @isci_host: This parameter specifies the ISCI host object -* @isci_device: This is the device to which the request is pending. -* @isci_request: This parameter specifies the terminated request object. -* @task: This parameter is the libsas I/O request. -*/ -static void isci_request_cleanup_completed_loiterer( - struct isci_host *isci_host, - struct isci_remote_device *isci_device, - struct isci_request *isci_request, - struct sas_task *task) -{ - unsigned long flags; - - dev_dbg(&isci_host->pdev->dev, - "%s: isci_device=%p, request=%p, task=%p\n", - __func__, isci_device, isci_request, task); - - if (task != NULL) { - - spin_lock_irqsave(&task->task_state_lock, flags); - task->lldd_task = NULL; - - task->task_state_flags &= ~SAS_TASK_NEED_DEV_RESET; - - isci_set_task_doneflags(task); - - /* If this task is not in the abort path, call task_done. */ - if (!(task->task_state_flags & SAS_TASK_STATE_ABORTED)) { - - spin_unlock_irqrestore(&task->task_state_lock, flags); - task->task_done(task); - } else - spin_unlock_irqrestore(&task->task_state_lock, flags); - } - - if (isci_request != NULL) { - spin_lock_irqsave(&isci_host->scic_lock, flags); - isci_free_tag(isci_host, isci_request->io_tag); - isci_request_change_state(isci_request, unallocated); - list_del_init(&isci_request->dev_node); - spin_unlock_irqrestore(&isci_host->scic_lock, flags); - } -} - static int isci_request_is_dealloc_managed(enum isci_request_status stat) { switch (stat) { @@ -666,7 +619,6 @@ static void isci_terminate_request_core(struct isci_host *ihost, unsigned long flags; unsigned long termination_completed = 1; struct completion *io_request_completion; - struct sas_task *task; dev_dbg(&ihost->pdev->dev, "%s: device = %p; request = %p\n", @@ -676,10 +628,6 @@ static void isci_terminate_request_core(struct isci_host *ihost, io_request_completion = isci_request->io_request_completion; - task = (isci_request->ttype == io_task) - ? isci_request_access_task(isci_request) - : NULL; - /* Note that we are not going to control * the target to abort the request. */ @@ -783,9 +731,20 @@ static void isci_terminate_request_core(struct isci_host *ihost, spin_unlock_irqrestore(&isci_request->state_lock, flags); } - if (needs_cleanup_handling) - isci_request_cleanup_completed_loiterer( - ihost, idev, isci_request, task); + if (needs_cleanup_handling) { + + dev_dbg(&ihost->pdev->dev, + "%s: cleanup isci_device=%p, request=%p\n", + __func__, idev, isci_request); + + if (isci_request != NULL) { + spin_lock_irqsave(&ihost->scic_lock, flags); + isci_free_tag(ihost, isci_request->io_tag); + isci_request_change_state(isci_request, unallocated); + list_del_init(&isci_request->dev_node); + spin_unlock_irqrestore(&ihost->scic_lock, flags); + } + } } } -- cgit v1.2.3 From 98145cb722b51ccc3ba27166c9803545accba950 Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 27 Oct 2011 15:05:16 -0700 Subject: [SCSI] isci: Fix task management for SMP, SATA and on dev remove. libsas uses the LLDD abort task interface to handle I/O timeouts in the SATA/STP and SMP discovery paths, so this change will terminate STP/SMP requests. Also, if the device is gone, the lldd will prevent libsas from further escalations in the error handler. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/isci/remote_device.c | 47 ----------- drivers/scsi/isci/remote_device.h | 2 - drivers/scsi/isci/task.c | 169 +++++++++++++++----------------------- drivers/scsi/isci/task.h | 33 +------- 4 files changed, 67 insertions(+), 184 deletions(-) diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c index fbf9ce28c3f5..20c77edd4711 100644 --- a/drivers/scsi/isci/remote_device.c +++ b/drivers/scsi/isci/remote_device.c @@ -1438,53 +1438,6 @@ int isci_remote_device_found(struct domain_device *domain_dev) return status == SCI_SUCCESS ? 0 : -ENODEV; } -/** - * isci_device_is_reset_pending() - This function will check if there is any - * pending reset condition on the device. - * @request: This parameter is the isci_device object. - * - * true if there is a reset pending for the device. - */ -bool isci_device_is_reset_pending( - struct isci_host *isci_host, - struct isci_remote_device *isci_device) -{ - struct isci_request *isci_request; - struct isci_request *tmp_req; - bool reset_is_pending = false; - unsigned long flags; - - dev_dbg(&isci_host->pdev->dev, - "%s: isci_device = %p\n", __func__, isci_device); - - spin_lock_irqsave(&isci_host->scic_lock, flags); - - /* Check for reset on all pending requests. */ - list_for_each_entry_safe(isci_request, tmp_req, - &isci_device->reqs_in_process, dev_node) { - dev_dbg(&isci_host->pdev->dev, - "%s: isci_device = %p request = %p\n", - __func__, isci_device, isci_request); - - if (isci_request->ttype == io_task) { - struct sas_task *task = isci_request_access_task( - isci_request); - - spin_lock(&task->task_state_lock); - if (task->task_state_flags & SAS_TASK_NEED_DEV_RESET) - reset_is_pending = true; - spin_unlock(&task->task_state_lock); - } - } - - spin_unlock_irqrestore(&isci_host->scic_lock, flags); - - dev_dbg(&isci_host->pdev->dev, - "%s: isci_device = %p reset_is_pending = %d\n", - __func__, isci_device, reset_is_pending); - - return reset_is_pending; -} /** * isci_device_clear_reset_pending() - This function will clear if any pending diff --git a/drivers/scsi/isci/remote_device.h b/drivers/scsi/isci/remote_device.h index e1747ea0d0ea..bee6dd2d0fe7 100644 --- a/drivers/scsi/isci/remote_device.h +++ b/drivers/scsi/isci/remote_device.h @@ -132,8 +132,6 @@ void isci_remote_device_nuke_requests(struct isci_host *ihost, struct isci_remote_device *idev); void isci_remote_device_gone(struct domain_device *domain_dev); int isci_remote_device_found(struct domain_device *domain_dev); -bool isci_device_is_reset_pending(struct isci_host *ihost, - struct isci_remote_device *idev); void isci_device_clear_reset_pending(struct isci_host *ihost, struct isci_remote_device *idev); /** diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index c1f439bed717..ec85beb1f979 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c @@ -920,22 +920,14 @@ int isci_task_lu_reset(struct domain_device *domain_device, u8 *lun) "%s: domain_device=%p, isci_host=%p; isci_device=%p\n", __func__, domain_device, isci_host, isci_device); - if (isci_device) - set_bit(IDEV_EH, &isci_device->flags); + if (!isci_device) { + /* If the device is gone, stop the escalations. */ + dev_dbg(&isci_host->pdev->dev, "%s: No dev\n", __func__); - /* If there is a device reset pending on any request in the - * device's list, fail this LUN reset request in order to - * escalate to the device reset. - */ - if (!isci_device || - isci_device_is_reset_pending(isci_host, isci_device)) { - dev_dbg(&isci_host->pdev->dev, - "%s: No dev (%p), or " - "RESET PENDING: domain_device=%p\n", - __func__, isci_device, domain_device); - ret = TMF_RESP_FUNC_FAILED; + ret = TMF_RESP_FUNC_COMPLETE; goto out; } + set_bit(IDEV_EH, &isci_device->flags); /* Send the task management part of the reset. */ if (sas_protocol_ata(domain_device->tproto)) { @@ -1044,7 +1036,7 @@ int isci_task_abort_task(struct sas_task *task) struct isci_tmf tmf; int ret = TMF_RESP_FUNC_FAILED; unsigned long flags; - bool any_dev_reset = false; + int perform_termination = 0; /* Get the isci_request reference from the task. Note that * this check does not depend on the pending request list @@ -1066,89 +1058,34 @@ int isci_task_abort_task(struct sas_task *task) spin_unlock_irqrestore(&isci_host->scic_lock, flags); dev_dbg(&isci_host->pdev->dev, - "%s: task = %p\n", __func__, task); - - if (!isci_device || !old_request) - goto out; + "%s: dev = %p, task = %p, old_request == %p\n", + __func__, isci_device, task, old_request); - set_bit(IDEV_EH, &isci_device->flags); - - /* This version of the driver will fail abort requests for - * SATA/STP. Failing the abort request this way will cause the - * SCSI error handler thread to escalate to LUN reset - */ - if (sas_protocol_ata(task->task_proto)) { - dev_dbg(&isci_host->pdev->dev, - " task %p is for a STP/SATA device;" - " returning TMF_RESP_FUNC_FAILED\n" - " to cause a LUN reset...\n", task); - goto out; - } - - dev_dbg(&isci_host->pdev->dev, - "%s: old_request == %p\n", __func__, old_request); - - any_dev_reset = isci_device_is_reset_pending(isci_host, isci_device); - - spin_lock_irqsave(&task->task_state_lock, flags); - - any_dev_reset = any_dev_reset || (task->task_state_flags & SAS_TASK_NEED_DEV_RESET); + if (isci_device) + set_bit(IDEV_EH, &isci_device->flags); - /* If the extraction of the request reference from the task - * failed, then the request has been completed (or if there is a - * pending reset then this abort request function must be failed - * in order to escalate to the target reset). + /* Device reset conditions signalled in task_state_flags are the + * responsbility of libsas to observe at the start of the error + * handler thread. */ - if ((old_request == NULL) || any_dev_reset) { - - /* If the device reset task flag is set, fail the task - * management request. Otherwise, the original request - * has completed. - */ - if (any_dev_reset) { - - /* Turn off the task's DONE to make sure this - * task is escalated to a target reset. - */ - task->task_state_flags &= ~SAS_TASK_STATE_DONE; - - /* Make the reset happen as soon as possible. */ - task->task_state_flags |= SAS_TASK_NEED_DEV_RESET; - - spin_unlock_irqrestore(&task->task_state_lock, flags); - - /* Fail the task management request in order to - * escalate to the target reset. - */ - ret = TMF_RESP_FUNC_FAILED; - - dev_dbg(&isci_host->pdev->dev, - "%s: Failing task abort in order to " - "escalate to target reset because\n" - "SAS_TASK_NEED_DEV_RESET is set for " - "task %p on dev %p\n", - __func__, task, isci_device); - - - } else { - /* The request has already completed and there - * is nothing to do here other than to set the task - * done bit, and indicate that the task abort function - * was sucessful. - */ - isci_set_task_doneflags(task); - - spin_unlock_irqrestore(&task->task_state_lock, flags); + if (!isci_device || !old_request) { + /* The request has already completed and there + * is nothing to do here other than to set the task + * done bit, and indicate that the task abort function + * was sucessful. + */ + spin_lock_irqsave(&task->task_state_lock, flags); + task->task_state_flags |= SAS_TASK_STATE_DONE; + task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR | + SAS_TASK_STATE_PENDING); + spin_unlock_irqrestore(&task->task_state_lock, flags); - ret = TMF_RESP_FUNC_COMPLETE; + ret = TMF_RESP_FUNC_COMPLETE; - dev_dbg(&isci_host->pdev->dev, - "%s: abort task not needed for %p\n", - __func__, task); - } + dev_dbg(&isci_host->pdev->dev, + "%s: abort task not needed for %p\n", + __func__, task); goto out; - } else { - spin_unlock_irqrestore(&task->task_state_lock, flags); } spin_lock_irqsave(&isci_host->scic_lock, flags); @@ -1177,24 +1114,44 @@ int isci_task_abort_task(struct sas_task *task) goto out; } if (task->task_proto == SAS_PROTOCOL_SMP || + sas_protocol_ata(task->task_proto) || test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags)) { spin_unlock_irqrestore(&isci_host->scic_lock, flags); dev_dbg(&isci_host->pdev->dev, - "%s: SMP request (%d)" + "%s: %s request" " or complete_in_target (%d), thus no TMF\n", - __func__, (task->task_proto == SAS_PROTOCOL_SMP), + __func__, + ((task->task_proto == SAS_PROTOCOL_SMP) + ? "SMP" + : (sas_protocol_ata(task->task_proto) + ? "SATA/STP" + : "") + ), test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags)); - /* Set the state on the task. */ - isci_task_all_done(task); - - ret = TMF_RESP_FUNC_COMPLETE; + if (test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags)) { + spin_lock_irqsave(&task->task_state_lock, flags); + task->task_state_flags |= SAS_TASK_STATE_DONE; + task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR | + SAS_TASK_STATE_PENDING); + spin_unlock_irqrestore(&task->task_state_lock, flags); + ret = TMF_RESP_FUNC_COMPLETE; + } else { + spin_lock_irqsave(&task->task_state_lock, flags); + task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR | + SAS_TASK_STATE_PENDING); + spin_unlock_irqrestore(&task->task_state_lock, flags); + } - /* Stopping and SMP devices are not sent a TMF, and are not - * reset, but the outstanding I/O request is terminated below. + /* STP and SMP devices are not sent a TMF, but the + * outstanding I/O request is terminated below. This is + * because SATA/STP and SMP discovery path timeouts directly + * call the abort task interface for cleanup. */ + perform_termination = 1; + } else { /* Fill in the tmf stucture */ isci_task_build_abort_task_tmf(&tmf, isci_tmf_ssp_task_abort, @@ -1203,22 +1160,24 @@ int isci_task_abort_task(struct sas_task *task) spin_unlock_irqrestore(&isci_host->scic_lock, flags); - #define ISCI_ABORT_TASK_TIMEOUT_MS 500 /* half second timeout. */ + #define ISCI_ABORT_TASK_TIMEOUT_MS 500 /* 1/2 second timeout */ ret = isci_task_execute_tmf(isci_host, isci_device, &tmf, ISCI_ABORT_TASK_TIMEOUT_MS); - if (ret != TMF_RESP_FUNC_COMPLETE) + if (ret == TMF_RESP_FUNC_COMPLETE) + perform_termination = 1; + else dev_dbg(&isci_host->pdev->dev, - "%s: isci_task_send_tmf failed\n", - __func__); + "%s: isci_task_send_tmf failed\n", __func__); } - if (ret == TMF_RESP_FUNC_COMPLETE) { + if (perform_termination) { set_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags); /* Clean up the request on our side, and wait for the aborted * I/O to complete. */ - isci_terminate_request_core(isci_host, isci_device, old_request); + isci_terminate_request_core(isci_host, isci_device, + old_request); } /* Make sure we do not leave a reference to aborted_io_completion */ diff --git a/drivers/scsi/isci/task.h b/drivers/scsi/isci/task.h index c9ccd0b5ff53..bc78c0a41d5c 100644 --- a/drivers/scsi/isci/task.h +++ b/drivers/scsi/isci/task.h @@ -226,35 +226,6 @@ enum isci_completion_selection { isci_perform_error_io_completion /* Use sas_task_abort */ }; -static inline void isci_set_task_doneflags( - struct sas_task *task) -{ - /* Since no futher action will be taken on this task, - * make sure to mark it complete from the lldd perspective. - */ - task->task_state_flags |= SAS_TASK_STATE_DONE; - task->task_state_flags &= ~SAS_TASK_AT_INITIATOR; - task->task_state_flags &= ~SAS_TASK_STATE_PENDING; -} -/** - * isci_task_all_done() - This function clears the task bits to indicate the - * LLDD is done with the task. - * - * - */ -static inline void isci_task_all_done( - struct sas_task *task) -{ - unsigned long flags; - - /* Since no futher action will be taken on this task, - * make sure to mark it complete from the lldd perspective. - */ - spin_lock_irqsave(&task->task_state_lock, flags); - isci_set_task_doneflags(task); - spin_unlock_irqrestore(&task->task_state_lock, flags); -} - /** * isci_task_set_completion_status() - This function sets the completion status * for the request. @@ -336,7 +307,9 @@ isci_task_set_completion_status( /* Fall through to the normal case... */ case isci_perform_normal_io_completion: /* Normal notification (task_done) */ - isci_set_task_doneflags(task); + task->task_state_flags |= SAS_TASK_STATE_DONE; + task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR | + SAS_TASK_STATE_PENDING); break; default: WARN_ONCE(1, "unknown task_notification_selection: %d\n", -- cgit v1.2.3 From 3b34c169f8197e02529fa3ec703703c2ce418c57 Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 27 Oct 2011 15:05:22 -0700 Subject: [SCSI] isci: Remove redundant isci_request.ttype field. Use the existing IREQ_TMF flag as a request type indicator. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/isci/remote_device.c | 11 ++++------ drivers/scsi/isci/request.c | 45 +++++++++++---------------------------- drivers/scsi/isci/request.h | 6 ------ drivers/scsi/isci/task.c | 29 +++++++++++++------------ 4 files changed, 31 insertions(+), 60 deletions(-) diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c index 20c77edd4711..9d9e33d2ed51 100644 --- a/drivers/scsi/isci/remote_device.c +++ b/drivers/scsi/isci/remote_device.c @@ -1463,15 +1463,12 @@ void isci_device_clear_reset_pending(struct isci_host *ihost, struct isci_remote dev_dbg(&ihost->pdev->dev, "%s: idev = %p request = %p\n", __func__, idev, isci_request); - if (isci_request->ttype == io_task) { + if (!test_bit(IREQ_TMF, &isci_request->flags)) { + struct sas_task *task = isci_request_access_task(isci_request); - unsigned long flags2; - struct sas_task *task = isci_request_access_task( - isci_request); - - spin_lock_irqsave(&task->task_state_lock, flags2); + spin_lock(&task->task_state_lock); task->task_state_flags &= ~SAS_TASK_NEED_DEV_RESET; - spin_unlock_irqrestore(&task->task_state_lock, flags2); + spin_unlock(&task->task_state_lock); } } spin_unlock_irqrestore(&ihost->scic_lock, flags); diff --git a/drivers/scsi/isci/request.c b/drivers/scsi/isci/request.c index bfc7379727b1..192cb48d849a 100644 --- a/drivers/scsi/isci/request.c +++ b/drivers/scsi/isci/request.c @@ -191,7 +191,7 @@ static void sci_task_request_build_ssp_task_iu(struct isci_request *ireq) task_iu->task_func = isci_tmf->tmf_code; task_iu->task_tag = - (ireq->ttype == tmf_task) ? + (test_bit(IREQ_TMF, &ireq->flags)) ? isci_tmf->io_tag : SCI_CONTROLLER_INVALID_IO_TAG; } @@ -516,7 +516,7 @@ sci_io_request_construct_sata(struct isci_request *ireq, struct domain_device *dev = ireq->target_device->domain_dev; /* check for management protocols */ - if (ireq->ttype == tmf_task) { + if (test_bit(IREQ_TMF, &ireq->flags)) { struct isci_tmf *tmf = isci_request_access_tmf(ireq); if (tmf->tmf_code == isci_tmf_sata_srst_high || @@ -632,7 +632,7 @@ enum sci_status sci_task_request_construct_sata(struct isci_request *ireq) enum sci_status status = SCI_SUCCESS; /* check for management protocols */ - if (ireq->ttype == tmf_task) { + if (test_bit(IREQ_TMF, &ireq->flags)) { struct isci_tmf *tmf = isci_request_access_tmf(ireq); if (tmf->tmf_code == isci_tmf_sata_srst_high || @@ -2630,14 +2630,8 @@ static void isci_task_save_for_upper_layer_completion( switch (task_notification_selection) { case isci_perform_normal_io_completion: - /* Normal notification (task_done) */ - dev_dbg(&host->pdev->dev, - "%s: Normal - task = %p, response=%d (%d), status=%d (%d)\n", - __func__, - task, - task->task_status.resp, response, - task->task_status.stat, status); + /* Add to the completed list. */ list_add(&request->completed_node, &host->requests_to_complete); @@ -2650,13 +2644,6 @@ static void isci_task_save_for_upper_layer_completion( /* No notification to libsas because this request is * already in the abort path. */ - dev_dbg(&host->pdev->dev, - "%s: Aborted - task = %p, response=%d (%d), status=%d (%d)\n", - __func__, - task, - task->task_status.resp, response, - task->task_status.stat, status); - /* Wake up whatever process was waiting for this * request to complete. */ @@ -2673,30 +2660,22 @@ static void isci_task_save_for_upper_layer_completion( case isci_perform_error_io_completion: /* Use sas_task_abort */ - dev_dbg(&host->pdev->dev, - "%s: Error - task = %p, response=%d (%d), status=%d (%d)\n", - __func__, - task, - task->task_status.resp, response, - task->task_status.stat, status); /* Add to the aborted list. */ list_add(&request->completed_node, &host->requests_to_errorback); break; default: - dev_dbg(&host->pdev->dev, - "%s: Unknown - task = %p, response=%d (%d), status=%d (%d)\n", - __func__, - task, - task->task_status.resp, response, - task->task_status.stat, status); - /* Add to the error to libsas list. */ list_add(&request->completed_node, &host->requests_to_errorback); break; } + dev_dbg(&host->pdev->dev, + "%s: %d - task = %p, response=%d (%d), status=%d (%d)\n", + __func__, task_notification_selection, task, + (task) ? task->task_status.resp : 0, response, + (task) ? task->task_status.stat : 0, status); } static void isci_process_stp_response(struct sas_task *task, struct dev_to_host_fis *fis) @@ -3079,7 +3058,7 @@ static void sci_request_started_state_enter(struct sci_base_state_machine *sm) /* XXX as hch said always creating an internal sas_task for tmf * requests would simplify the driver */ - task = ireq->ttype == io_task ? isci_request_access_task(ireq) : NULL; + task = (test_bit(IREQ_TMF, &ireq->flags)) ? NULL : isci_request_access_task(ireq); /* all unaccelerated request types (non ssp or ncq) handled with * substates @@ -3563,7 +3542,7 @@ static struct isci_request *isci_io_request_from_tag(struct isci_host *ihost, ireq = isci_request_from_tag(ihost, tag); ireq->ttype_ptr.io_task_ptr = task; - ireq->ttype = io_task; + clear_bit(IREQ_TMF, &ireq->flags); task->lldd_task = ireq; return ireq; @@ -3577,7 +3556,7 @@ struct isci_request *isci_tmf_request_from_tag(struct isci_host *ihost, ireq = isci_request_from_tag(ihost, tag); ireq->ttype_ptr.tmf_task_ptr = isci_tmf; - ireq->ttype = tmf_task; + set_bit(IREQ_TMF, &ireq->flags); return ireq; } diff --git a/drivers/scsi/isci/request.h b/drivers/scsi/isci/request.h index f720b97b7bb5..be38933dd6df 100644 --- a/drivers/scsi/isci/request.h +++ b/drivers/scsi/isci/request.h @@ -77,11 +77,6 @@ enum isci_request_status { dead = 0x07 }; -enum task_type { - io_task = 0, - tmf_task = 1 -}; - enum sci_request_protocol { SCIC_NO_PROTOCOL, SCIC_SMP_PROTOCOL, @@ -116,7 +111,6 @@ struct isci_request { #define IREQ_ACTIVE 3 unsigned long flags; /* XXX kill ttype and ttype_ptr, allocate full sas_task */ - enum task_type ttype; union ttype_ptr_union { struct sas_task *io_task_ptr; /* When ttype==io_task */ struct isci_tmf *tmf_task_ptr; /* When ttype==tmf_task */ diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index ec85beb1f979..80e1a69ac96f 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c @@ -254,7 +254,7 @@ static enum sci_status isci_sata_management_task_request_build(struct isci_reque struct isci_tmf *isci_tmf; enum sci_status status; - if (tmf_task != ireq->ttype) + if (!test_bit(IREQ_TMF, &ireq->flags)) return SCI_FAILURE; isci_tmf = isci_request_access_tmf(ireq); @@ -352,18 +352,7 @@ static void isci_request_mark_zombie(struct isci_host *ihost, struct isci_reques req_completion = ireq->io_request_completion; ireq->io_request_completion = NULL; - if (ireq->ttype == io_task) { - - /* Break links with the sas_task - the callback is done - * elsewhere. - */ - struct sas_task *task = isci_request_access_task(ireq); - - if (task) - task->lldd_task = NULL; - - ireq->ttype_ptr.io_task_ptr = NULL; - } else { + if (test_bit(IREQ_TMF, &ireq->flags)) { /* Break links with the TMF request. */ struct isci_tmf *tmf = isci_request_access_tmf(ireq); @@ -380,6 +369,16 @@ static void isci_request_mark_zombie(struct isci_host *ihost, struct isci_reques ireq->ttype_ptr.tmf_task_ptr = NULL; dev_dbg(&ihost->pdev->dev, "%s: tmf_code %d, managed tag %#x\n", __func__, tmf->tmf_code, tmf->io_tag); + } else { + /* Break links with the sas_task - the callback is done + * elsewhere. + */ + struct sas_task *task = isci_request_access_task(ireq); + + if (task) + task->lldd_task = NULL; + + ireq->ttype_ptr.io_task_ptr = NULL; } dev_warn(&ihost->pdev->dev, "task context unrecoverable (tag: %#x)\n", @@ -803,7 +802,9 @@ void isci_terminate_pending_requests(struct isci_host *ihost, dev_dbg(&ihost->pdev->dev, "%s: idev=%p request=%p; task=%p old_state=%d\n", __func__, idev, ireq, - ireq->ttype == io_task ? isci_request_access_task(ireq) : NULL, + (!test_bit(IREQ_TMF, &ireq->flags) + ? isci_request_access_task(ireq) + : NULL), old_state); /* If the old_state is started: -- cgit v1.2.3 From 5412e25c55fc0b08041a451d8bee6f2b291099c2 Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 27 Oct 2011 15:05:27 -0700 Subject: [SCSI] isci: No need to manage the pending reset bit on pending requests. The lldd does not need to look at or manage the pending device reset bit in pending sas_tasks. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/isci/remote_device.c | 35 ----------------------------------- drivers/scsi/isci/remote_device.h | 3 +-- drivers/scsi/isci/task.c | 3 --- 3 files changed, 1 insertion(+), 40 deletions(-) diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c index 9d9e33d2ed51..b207cd3b15a0 100644 --- a/drivers/scsi/isci/remote_device.c +++ b/drivers/scsi/isci/remote_device.c @@ -1438,38 +1438,3 @@ int isci_remote_device_found(struct domain_device *domain_dev) return status == SCI_SUCCESS ? 0 : -ENODEV; } - -/** - * isci_device_clear_reset_pending() - This function will clear if any pending - * reset condition flags on the device. - * @request: This parameter is the isci_device object. - * - * true if there is a reset pending for the device. - */ -void isci_device_clear_reset_pending(struct isci_host *ihost, struct isci_remote_device *idev) -{ - struct isci_request *isci_request; - struct isci_request *tmp_req; - unsigned long flags = 0; - - dev_dbg(&ihost->pdev->dev, "%s: idev=%p, ihost=%p\n", - __func__, idev, ihost); - - spin_lock_irqsave(&ihost->scic_lock, flags); - - /* Clear reset pending on all pending requests. */ - list_for_each_entry_safe(isci_request, tmp_req, - &idev->reqs_in_process, dev_node) { - dev_dbg(&ihost->pdev->dev, "%s: idev = %p request = %p\n", - __func__, idev, isci_request); - - if (!test_bit(IREQ_TMF, &isci_request->flags)) { - struct sas_task *task = isci_request_access_task(isci_request); - - spin_lock(&task->task_state_lock); - task->task_state_flags &= ~SAS_TASK_NEED_DEV_RESET; - spin_unlock(&task->task_state_lock); - } - } - spin_unlock_irqrestore(&ihost->scic_lock, flags); -} diff --git a/drivers/scsi/isci/remote_device.h b/drivers/scsi/isci/remote_device.h index bee6dd2d0fe7..483ee50152f3 100644 --- a/drivers/scsi/isci/remote_device.h +++ b/drivers/scsi/isci/remote_device.h @@ -132,8 +132,7 @@ void isci_remote_device_nuke_requests(struct isci_host *ihost, struct isci_remote_device *idev); void isci_remote_device_gone(struct domain_device *domain_dev); int isci_remote_device_found(struct domain_device *domain_dev); -void isci_device_clear_reset_pending(struct isci_host *ihost, - struct isci_remote_device *idev); + /** * sci_remote_device_stop() - This method will stop both transmission and * reception of link activity for the supplied remote device. This method diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index 80e1a69ac96f..6d8ff15a03d1 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c @@ -1570,9 +1570,6 @@ static int isci_reset_device(struct isci_host *ihost, } spin_unlock_irqrestore(&ihost->scic_lock, flags); - /* Make sure all pending requests are able to be fully terminated. */ - isci_device_clear_reset_pending(ihost, idev); - /* If this is a device on an expander, disable BCN processing. */ if (!scsi_is_sas_phy_local(phy)) set_bit(IPORT_BCN_BLOCKED, &iport->flags); -- cgit v1.2.3 From 8e35a1398c5db981cd1a2d7635de9c15dd648527 Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 27 Oct 2011 15:05:32 -0700 Subject: [SCSI] isci: Fix hard reset timeout conditions. A hard reset can timeout before or after the last phy in the port goes away. If after, then notify the OS that the last phy has failed. The recovery for the failed hard reset has been removed. This recovery code was unecessary in that the link would recover from the failure normally by a new link reset sequence or hotplug of the remote device. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/isci/port.c | 101 +++++++++++++++++++++++++++-------------------- drivers/scsi/isci/port.h | 1 + 2 files changed, 60 insertions(+), 42 deletions(-) diff --git a/drivers/scsi/isci/port.c b/drivers/scsi/isci/port.c index 8e59c8865dcd..bfeb87905aaf 100644 --- a/drivers/scsi/isci/port.c +++ b/drivers/scsi/isci/port.c @@ -350,6 +350,34 @@ static void isci_port_stop_complete(struct isci_host *ihost, dev_dbg(&ihost->pdev->dev, "Port stop complete\n"); } + +static bool is_port_ready_state(enum sci_port_states state) +{ + switch (state) { + case SCI_PORT_READY: + case SCI_PORT_SUB_WAITING: + case SCI_PORT_SUB_OPERATIONAL: + case SCI_PORT_SUB_CONFIGURING: + return true; + default: + return false; + } +} + +/* flag dummy rnc hanling when exiting a ready state */ +static void port_state_machine_change(struct isci_port *iport, + enum sci_port_states state) +{ + struct sci_base_state_machine *sm = &iport->sm; + enum sci_port_states old_state = sm->current_state_id; + + if (is_port_ready_state(old_state) && !is_port_ready_state(state)) + iport->ready_exit = true; + + sci_change_state(sm, state); + iport->ready_exit = false; +} + /** * isci_port_hard_reset_complete() - This function is called by the sci core * when the hard reset complete notification has been received. @@ -368,6 +396,26 @@ static void isci_port_hard_reset_complete(struct isci_port *isci_port, /* Save the status of the hard reset from the port. */ isci_port->hard_reset_status = completion_status; + if (completion_status != SCI_SUCCESS) { + + /* The reset failed. The port state is now SCI_PORT_FAILED. */ + if (isci_port->active_phy_mask == 0) { + + /* Generate the link down now to the host, since it + * was intercepted by the hard reset state machine when + * it really happened. + */ + isci_port_link_down(isci_port->isci_host, + &isci_port->isci_host->phys[ + isci_port->last_active_phy], + isci_port); + } + /* Advance the port state so that link state changes will be + * noticed. + */ + port_state_machine_change(isci_port, SCI_PORT_SUB_WAITING); + + } complete_all(&isci_port->hard_reset_complete); } @@ -657,6 +705,8 @@ void sci_port_deactivate_phy(struct isci_port *iport, struct isci_phy *iphy, struct isci_host *ihost = iport->owning_controller; iport->active_phy_mask &= ~(1 << iphy->phy_index); + if (!iport->active_phy_mask) + iport->last_active_phy = iphy->phy_index; iphy->max_negotiated_speed = SAS_LINK_RATE_UNKNOWN; @@ -683,33 +733,6 @@ static void sci_port_invalid_link_up(struct isci_port *iport, struct isci_phy *i } } -static bool is_port_ready_state(enum sci_port_states state) -{ - switch (state) { - case SCI_PORT_READY: - case SCI_PORT_SUB_WAITING: - case SCI_PORT_SUB_OPERATIONAL: - case SCI_PORT_SUB_CONFIGURING: - return true; - default: - return false; - } -} - -/* flag dummy rnc hanling when exiting a ready state */ -static void port_state_machine_change(struct isci_port *iport, - enum sci_port_states state) -{ - struct sci_base_state_machine *sm = &iport->sm; - enum sci_port_states old_state = sm->current_state_id; - - if (is_port_ready_state(old_state) && !is_port_ready_state(state)) - iport->ready_exit = true; - - sci_change_state(sm, state); - iport->ready_exit = false; -} - /** * sci_port_general_link_up_handler - phy can be assigned to port? * @sci_port: sci_port object for which has a phy that has gone link up. @@ -1622,7 +1645,8 @@ void sci_port_construct(struct isci_port *iport, u8 index, iport->logical_port_index = SCIC_SDS_DUMMY_PORT; iport->physical_port_index = index; iport->active_phy_mask = 0; - iport->ready_exit = false; + iport->last_active_phy = 0; + iport->ready_exit = false; iport->owning_controller = ihost; @@ -1676,7 +1700,7 @@ int isci_port_perform_hard_reset(struct isci_host *ihost, struct isci_port *ipor { unsigned long flags; enum sci_status status; - int idx, ret = TMF_RESP_FUNC_COMPLETE; + int ret = TMF_RESP_FUNC_COMPLETE; dev_dbg(&ihost->pdev->dev, "%s: iport = %p\n", __func__, iport); @@ -1697,8 +1721,13 @@ int isci_port_perform_hard_reset(struct isci_host *ihost, struct isci_port *ipor "%s: iport = %p; hard reset completion\n", __func__, iport); - if (iport->hard_reset_status != SCI_SUCCESS) + if (iport->hard_reset_status != SCI_SUCCESS) { ret = TMF_RESP_FUNC_FAILED; + + dev_err(&ihost->pdev->dev, + "%s: iport = %p; hard reset failed (0x%x)\n", + __func__, iport, iport->hard_reset_status); + } } else { ret = TMF_RESP_FUNC_FAILED; @@ -1718,18 +1747,6 @@ int isci_port_perform_hard_reset(struct isci_host *ihost, struct isci_port *ipor "%s: iport = %p; hard reset failed " "(0x%x) - driving explicit link fail for all phys\n", __func__, iport, iport->hard_reset_status); - - /* Down all phys in the port. */ - spin_lock_irqsave(&ihost->scic_lock, flags); - for (idx = 0; idx < SCI_MAX_PHYS; ++idx) { - struct isci_phy *iphy = iport->phy_table[idx]; - - if (!iphy) - continue; - sci_phy_stop(iphy); - sci_phy_start(iphy); - } - spin_unlock_irqrestore(&ihost->scic_lock, flags); } return ret; } diff --git a/drivers/scsi/isci/port.h b/drivers/scsi/isci/port.h index b50ecd4e8f9c..e84d22a309e3 100644 --- a/drivers/scsi/isci/port.h +++ b/drivers/scsi/isci/port.h @@ -109,6 +109,7 @@ struct isci_port { u8 logical_port_index; u8 physical_port_index; u8 active_phy_mask; + u8 last_active_phy; u16 reserved_rni; u16 reserved_tag; u32 started_request_count; -- cgit v1.2.3 From 52d74634335dfc0984ed955ed3c6ad6488495f96 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 27 Oct 2011 15:05:37 -0700 Subject: [SCSI] isci: revert bcn filtering The initial bcn filtering implementation was validated on a kernel baseline that predated the switch to new libata error handling. Also, prior to that conversion we borrowed the mvsas MVS_DEV_EH approach to prevent the unwanted extra ap->ops->phy_reset(ap) that occurred in the ata_bus_probe() path. After the conversion to new libata eh resets at discovery are more frequent and get filtered prematurely by IDEV_EH. The result is that our bcn filtering has been blocked from running and at discovery and it appears to stall discovery completion to the point of triggering hung task timeouts. So, revert the implementation for now. When it returns it will go into libsas proper. The domain rediscovery that takes place due to ->lldd_I_T_nexus_reset() events should now be properly waited for by the ata_port_wait_eh() call in ata_port_probe(). So the hard coded delay in the isci ->lldd_I_T_nexus_reset() and other libsas drivers should help debounce the libsas thread from seeing temporary device removals. Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/isci/port.c | 45 +-------- drivers/scsi/isci/port.h | 5 - drivers/scsi/isci/task.c | 235 ----------------------------------------------- 3 files changed, 4 insertions(+), 281 deletions(-) diff --git a/drivers/scsi/isci/port.c b/drivers/scsi/isci/port.c index bfeb87905aaf..ac7f27749f97 100644 --- a/drivers/scsi/isci/port.c +++ b/drivers/scsi/isci/port.c @@ -145,48 +145,15 @@ static void sci_port_bcn_enable(struct isci_port *iport) } } -/* called under sci_lock to stabilize phy:port associations */ -void isci_port_bcn_enable(struct isci_host *ihost, struct isci_port *iport) -{ - int i; - - clear_bit(IPORT_BCN_BLOCKED, &iport->flags); - wake_up(&ihost->eventq); - - if (!test_and_clear_bit(IPORT_BCN_PENDING, &iport->flags)) - return; - - for (i = 0; i < ARRAY_SIZE(iport->phy_table); i++) { - struct isci_phy *iphy = iport->phy_table[i]; - - if (!iphy) - continue; - - ihost->sas_ha.notify_port_event(&iphy->sas_phy, - PORTE_BROADCAST_RCVD); - break; - } -} - static void isci_port_bc_change_received(struct isci_host *ihost, struct isci_port *iport, struct isci_phy *iphy) { - if (iport && test_bit(IPORT_BCN_BLOCKED, &iport->flags)) { - dev_dbg(&ihost->pdev->dev, - "%s: disabled BCN; isci_phy = %p, sas_phy = %p\n", - __func__, iphy, &iphy->sas_phy); - set_bit(IPORT_BCN_PENDING, &iport->flags); - atomic_inc(&iport->event); - wake_up(&ihost->eventq); - } else { - dev_dbg(&ihost->pdev->dev, - "%s: isci_phy = %p, sas_phy = %p\n", - __func__, iphy, &iphy->sas_phy); + dev_dbg(&ihost->pdev->dev, + "%s: isci_phy = %p, sas_phy = %p\n", + __func__, iphy, &iphy->sas_phy); - ihost->sas_ha.notify_port_event(&iphy->sas_phy, - PORTE_BROADCAST_RCVD); - } + ihost->sas_ha.notify_port_event(&iphy->sas_phy, PORTE_BROADCAST_RCVD); sci_port_bcn_enable(iport); } @@ -278,9 +245,6 @@ static void isci_port_link_down(struct isci_host *isci_host, /* check to see if this is the last phy on this port. */ if (isci_phy->sas_phy.port && isci_phy->sas_phy.port->num_phys == 1) { - atomic_inc(&isci_port->event); - isci_port_bcn_enable(isci_host, isci_port); - /* change the state for all devices on this port. The * next task sent to this device will be returned as * SAS_TASK_UNDELIVERED, and the scsi mid layer will @@ -1672,7 +1636,6 @@ void isci_port_init(struct isci_port *iport, struct isci_host *ihost, int index) init_completion(&iport->start_complete); iport->isci_host = ihost; isci_port_change_state(iport, isci_freed); - atomic_set(&iport->event, 0); } /** diff --git a/drivers/scsi/isci/port.h b/drivers/scsi/isci/port.h index e84d22a309e3..cb5ffbc38603 100644 --- a/drivers/scsi/isci/port.h +++ b/drivers/scsi/isci/port.h @@ -77,7 +77,6 @@ enum isci_status { /** * struct isci_port - isci direct attached sas port object - * @event: counts bcns and port stop events (for bcn filtering) * @ready_exit: several states constitute 'ready'. When exiting ready we * need to take extra port-teardown actions that are * skipped when exiting to another 'ready' state. @@ -92,10 +91,6 @@ enum isci_status { */ struct isci_port { enum isci_status status; - #define IPORT_BCN_BLOCKED 0 - #define IPORT_BCN_PENDING 1 - unsigned long flags; - atomic_t event; struct isci_host *isci_host; struct asd_sas_port sas_port; struct list_head remote_dev_list; diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index 6d8ff15a03d1..66ad3dc89498 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c @@ -1331,226 +1331,10 @@ isci_task_request_complete(struct isci_host *ihost, complete(tmf_complete); } -static void isci_smp_task_timedout(unsigned long _task) -{ - struct sas_task *task = (void *) _task; - unsigned long flags; - - spin_lock_irqsave(&task->task_state_lock, flags); - if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) - task->task_state_flags |= SAS_TASK_STATE_ABORTED; - spin_unlock_irqrestore(&task->task_state_lock, flags); - - complete(&task->completion); -} - -static void isci_smp_task_done(struct sas_task *task) -{ - if (!del_timer(&task->timer)) - return; - complete(&task->completion); -} - -static int isci_smp_execute_task(struct isci_host *ihost, - struct domain_device *dev, void *req, - int req_size, void *resp, int resp_size) -{ - int res, retry; - struct sas_task *task = NULL; - - for (retry = 0; retry < 3; retry++) { - task = sas_alloc_task(GFP_KERNEL); - if (!task) - return -ENOMEM; - - task->dev = dev; - task->task_proto = dev->tproto; - sg_init_one(&task->smp_task.smp_req, req, req_size); - sg_init_one(&task->smp_task.smp_resp, resp, resp_size); - - task->task_done = isci_smp_task_done; - - task->timer.data = (unsigned long) task; - task->timer.function = isci_smp_task_timedout; - task->timer.expires = jiffies + 10*HZ; - add_timer(&task->timer); - - res = isci_task_execute_task(task, 1, GFP_KERNEL); - - if (res) { - del_timer(&task->timer); - dev_dbg(&ihost->pdev->dev, - "%s: executing SMP task failed:%d\n", - __func__, res); - goto ex_err; - } - - wait_for_completion(&task->completion); - res = -ECOMM; - if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) { - dev_dbg(&ihost->pdev->dev, - "%s: smp task timed out or aborted\n", - __func__); - isci_task_abort_task(task); - if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) { - dev_dbg(&ihost->pdev->dev, - "%s: SMP task aborted and not done\n", - __func__); - goto ex_err; - } - } - if (task->task_status.resp == SAS_TASK_COMPLETE && - task->task_status.stat == SAM_STAT_GOOD) { - res = 0; - break; - } - if (task->task_status.resp == SAS_TASK_COMPLETE && - task->task_status.stat == SAS_DATA_UNDERRUN) { - /* no error, but return the number of bytes of - * underrun */ - res = task->task_status.residual; - break; - } - if (task->task_status.resp == SAS_TASK_COMPLETE && - task->task_status.stat == SAS_DATA_OVERRUN) { - res = -EMSGSIZE; - break; - } else { - dev_dbg(&ihost->pdev->dev, - "%s: task to dev %016llx response: 0x%x " - "status 0x%x\n", __func__, - SAS_ADDR(dev->sas_addr), - task->task_status.resp, - task->task_status.stat); - sas_free_task(task); - task = NULL; - } - } -ex_err: - BUG_ON(retry == 3 && task != NULL); - sas_free_task(task); - return res; -} - -#define DISCOVER_REQ_SIZE 16 -#define DISCOVER_RESP_SIZE 56 - -int isci_smp_get_phy_attached_dev_type(struct isci_host *ihost, - struct domain_device *dev, - int phy_id, int *adt) -{ - struct smp_resp *disc_resp; - u8 *disc_req; - int res; - - disc_resp = kzalloc(DISCOVER_RESP_SIZE, GFP_KERNEL); - if (!disc_resp) - return -ENOMEM; - - disc_req = kzalloc(DISCOVER_REQ_SIZE, GFP_KERNEL); - if (disc_req) { - disc_req[0] = SMP_REQUEST; - disc_req[1] = SMP_DISCOVER; - disc_req[9] = phy_id; - } else { - kfree(disc_resp); - return -ENOMEM; - } - res = isci_smp_execute_task(ihost, dev, disc_req, DISCOVER_REQ_SIZE, - disc_resp, DISCOVER_RESP_SIZE); - if (!res) { - if (disc_resp->result != SMP_RESP_FUNC_ACC) - res = disc_resp->result; - else - *adt = disc_resp->disc.attached_dev_type; - } - kfree(disc_req); - kfree(disc_resp); - - return res; -} - -static void isci_wait_for_smp_phy_reset(struct isci_remote_device *idev, int phy_num) -{ - struct domain_device *dev = idev->domain_dev; - struct isci_port *iport = idev->isci_port; - struct isci_host *ihost = iport->isci_host; - int res, iteration = 0, attached_device_type; - #define STP_WAIT_MSECS 25000 - unsigned long tmo = msecs_to_jiffies(STP_WAIT_MSECS); - unsigned long deadline = jiffies + tmo; - enum { - SMP_PHYWAIT_PHYDOWN, - SMP_PHYWAIT_PHYUP, - SMP_PHYWAIT_DONE - } phy_state = SMP_PHYWAIT_PHYDOWN; - - /* While there is time, wait for the phy to go away and come back */ - while (time_is_after_jiffies(deadline) && phy_state != SMP_PHYWAIT_DONE) { - int event = atomic_read(&iport->event); - - ++iteration; - - tmo = wait_event_timeout(ihost->eventq, - event != atomic_read(&iport->event) || - !test_bit(IPORT_BCN_BLOCKED, &iport->flags), - tmo); - /* link down, stop polling */ - if (!test_bit(IPORT_BCN_BLOCKED, &iport->flags)) - break; - - dev_dbg(&ihost->pdev->dev, - "%s: iport %p, iteration %d," - " phase %d: time_remaining %lu, bcns = %d\n", - __func__, iport, iteration, phy_state, - tmo, test_bit(IPORT_BCN_PENDING, &iport->flags)); - - res = isci_smp_get_phy_attached_dev_type(ihost, dev, phy_num, - &attached_device_type); - tmo = deadline - jiffies; - - if (res) { - dev_dbg(&ihost->pdev->dev, - "%s: iteration %d, phase %d:" - " SMP error=%d, time_remaining=%lu\n", - __func__, iteration, phy_state, res, tmo); - break; - } - dev_dbg(&ihost->pdev->dev, - "%s: iport %p, iteration %d," - " phase %d: time_remaining %lu, bcns = %d, " - "attdevtype = %x\n", - __func__, iport, iteration, phy_state, - tmo, test_bit(IPORT_BCN_PENDING, &iport->flags), - attached_device_type); - - switch (phy_state) { - case SMP_PHYWAIT_PHYDOWN: - /* Has the device gone away? */ - if (!attached_device_type) - phy_state = SMP_PHYWAIT_PHYUP; - - break; - - case SMP_PHYWAIT_PHYUP: - /* Has the device come back? */ - if (attached_device_type) - phy_state = SMP_PHYWAIT_DONE; - break; - - case SMP_PHYWAIT_DONE: - break; - } - - } - dev_dbg(&ihost->pdev->dev, "%s: done\n", __func__); -} - static int isci_reset_device(struct isci_host *ihost, struct isci_remote_device *idev) { struct sas_phy *phy = sas_find_local_phy(idev->domain_dev); - struct isci_port *iport = idev->isci_port; enum sci_status status; unsigned long flags; int rc; @@ -1570,10 +1354,6 @@ static int isci_reset_device(struct isci_host *ihost, } spin_unlock_irqrestore(&ihost->scic_lock, flags); - /* If this is a device on an expander, disable BCN processing. */ - if (!scsi_is_sas_phy_local(phy)) - set_bit(IPORT_BCN_BLOCKED, &iport->flags); - rc = sas_phy_reset(phy, true); /* Terminate in-progress I/O now. */ @@ -1584,21 +1364,6 @@ static int isci_reset_device(struct isci_host *ihost, status = sci_remote_device_reset_complete(idev); spin_unlock_irqrestore(&ihost->scic_lock, flags); - /* If this is a device on an expander, bring the phy back up. */ - if (!scsi_is_sas_phy_local(phy)) { - /* A phy reset will cause the device to go away then reappear. - * Since libsas will take action on incoming BCNs (eg. remove - * a device going through an SMP phy-control driven reset), - * we need to wait until the phy comes back up before letting - * discovery proceed in libsas. - */ - isci_wait_for_smp_phy_reset(idev, phy->number); - - spin_lock_irqsave(&ihost->scic_lock, flags); - isci_port_bcn_enable(ihost, idev->isci_port); - spin_unlock_irqrestore(&ihost->scic_lock, flags); - } - if (status != SCI_SUCCESS) { dev_dbg(&ihost->pdev->dev, "%s: sci_remote_device_reset_complete(%p) " -- cgit v1.2.3 From 7000f7c71e2457391e3249eac1ae53c91f49a8c0 Mon Sep 17 00:00:00 2001 From: Andrzej Jakowski Date: Thu, 27 Oct 2011 15:05:42 -0700 Subject: [SCSI] isci: overriding max_concurr_spinup oem parameter by max(oem, user) Fixes bug where max_concurr_spinup oem parameter should be overriden by max_concurr_spinup user parameter. Override should happen only when max_concurr_spinup user parameter is specified in command line (greater than 0). Also this fix shortens variables representing max_conxurr_spinup for oem and user parameters. Signed-off-by: Andrzej Jakowski Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/isci/host.c | 23 ++++++++++++++++------- drivers/scsi/isci/init.c | 2 +- drivers/scsi/isci/probe_roms.h | 4 ++-- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c index f07f30fada1b..e7fe9c4c85b8 100644 --- a/drivers/scsi/isci/host.c +++ b/drivers/scsi/isci/host.c @@ -1350,7 +1350,7 @@ static void isci_user_parameters_get(struct sci_user_parameters *u) u->stp_max_occupancy_timeout = stp_max_occ_to; u->ssp_max_occupancy_timeout = ssp_max_occ_to; u->no_outbound_task_timeout = no_outbound_task_to; - u->max_number_concurrent_device_spin_up = max_concurr_spinup; + u->max_concurr_spinup = max_concurr_spinup; } static void sci_controller_initial_state_enter(struct sci_base_state_machine *sm) @@ -1661,7 +1661,7 @@ static void sci_controller_set_default_config_parameters(struct isci_host *ihost ihost->oem_parameters.controller.mode_type = SCIC_PORT_AUTOMATIC_CONFIGURATION_MODE; /* Default to APC mode. */ - ihost->oem_parameters.controller.max_concurrent_dev_spin_up = 1; + ihost->oem_parameters.controller.max_concurr_spin_up = 1; /* Default to no SSC operation. */ ihost->oem_parameters.controller.do_enable_ssc = false; @@ -1787,7 +1787,8 @@ int sci_oem_parameters_validate(struct sci_oem_params *oem) } else return -EINVAL; - if (oem->controller.max_concurrent_dev_spin_up > MAX_CONCURRENT_DEVICE_SPIN_UP_COUNT) + if (oem->controller.max_concurr_spin_up > MAX_CONCURRENT_DEVICE_SPIN_UP_COUNT || + oem->controller.max_concurr_spin_up < 1) return -EINVAL; return 0; @@ -1810,6 +1811,16 @@ static enum sci_status sci_oem_parameters_set(struct isci_host *ihost) return SCI_FAILURE_INVALID_STATE; } +static u8 max_spin_up(struct isci_host *ihost) +{ + if (ihost->user_parameters.max_concurr_spinup) + return min_t(u8, ihost->user_parameters.max_concurr_spinup, + MAX_CONCURRENT_DEVICE_SPIN_UP_COUNT); + else + return min_t(u8, ihost->oem_parameters.controller.max_concurr_spin_up, + MAX_CONCURRENT_DEVICE_SPIN_UP_COUNT); +} + static void power_control_timeout(unsigned long data) { struct sci_timer *tmr = (struct sci_timer *)data; @@ -1839,8 +1850,7 @@ static void power_control_timeout(unsigned long data) if (iphy == NULL) continue; - if (ihost->power_control.phys_granted_power >= - ihost->oem_parameters.controller.max_concurrent_dev_spin_up) + if (ihost->power_control.phys_granted_power >= max_spin_up(ihost)) break; ihost->power_control.requesters[i] = NULL; @@ -1865,8 +1875,7 @@ void sci_controller_power_control_queue_insert(struct isci_host *ihost, { BUG_ON(iphy == NULL); - if (ihost->power_control.phys_granted_power < - ihost->oem_parameters.controller.max_concurrent_dev_spin_up) { + if (ihost->power_control.phys_granted_power < max_spin_up(ihost)) { ihost->power_control.phys_granted_power++; sci_phy_consume_power_handler(iphy); diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c index 43fe840fbe9c..a97edabcb85a 100644 --- a/drivers/scsi/isci/init.c +++ b/drivers/scsi/isci/init.c @@ -118,7 +118,7 @@ unsigned char phy_gen = 3; module_param(phy_gen, byte, 0); MODULE_PARM_DESC(phy_gen, "PHY generation (1: 1.5Gbps 2: 3.0Gbps 3: 6.0Gbps)"); -unsigned char max_concurr_spinup = 1; +unsigned char max_concurr_spinup; module_param(max_concurr_spinup, byte, 0); MODULE_PARM_DESC(max_concurr_spinup, "Max concurrent device spinup"); diff --git a/drivers/scsi/isci/probe_roms.h b/drivers/scsi/isci/probe_roms.h index dc007e692f4e..2c75248ca326 100644 --- a/drivers/scsi/isci/probe_roms.h +++ b/drivers/scsi/isci/probe_roms.h @@ -112,7 +112,7 @@ struct sci_user_parameters { * This field specifies the maximum number of direct attached devices * that can have power supplied to them simultaneously. */ - u8 max_number_concurrent_device_spin_up; + u8 max_concurr_spinup; /** * This field specifies the number of seconds to allow a phy to consume @@ -219,7 +219,7 @@ struct sci_bios_oem_param_block_hdr { struct sci_oem_params { struct { uint8_t mode_type; - uint8_t max_concurrent_dev_spin_up; + uint8_t max_concurr_spin_up; uint8_t do_enable_ssc; uint8_t reserved; } controller; -- cgit v1.2.3 From 044aceef33bba7a471a3ed47ac60998b2983b18b Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Fri, 28 Oct 2011 11:34:07 -0700 Subject: [SCSI] edd: Treat "XPRS" host bus type the same as "PCI" PCI Express devices will return "XPRS" host bus type during BIOS EDD call. "XPRS" should be treated just like "PCI" so that the proper pci_dev symlink will be created. Scripts such as fcoe_edd.sh will then work correctly. Signed-off-by: Michael Chan Reviewed-by: Matt Domsch Signed-off-by: Yi Zou Signed-off-by: James Bottomley --- drivers/firmware/edd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/edd.c b/drivers/firmware/edd.c index f1b7f659d3c9..e22957665808 100644 --- a/drivers/firmware/edd.c +++ b/drivers/firmware/edd.c @@ -151,7 +151,8 @@ edd_show_host_bus(struct edd_device *edev, char *buf) p += scnprintf(p, left, "\tbase_address: %x\n", info->params.interface_path.isa.base_address); } else if (!strncmp(info->params.host_bus_type, "PCIX", 4) || - !strncmp(info->params.host_bus_type, "PCI", 3)) { + !strncmp(info->params.host_bus_type, "PCI", 3) || + !strncmp(info->params.host_bus_type, "XPRS", 4)) { p += scnprintf(p, left, "\t%02x:%02x.%d channel: %u\n", info->params.interface_path.pci.bus, @@ -159,7 +160,6 @@ edd_show_host_bus(struct edd_device *edev, char *buf) info->params.interface_path.pci.function, info->params.interface_path.pci.channel); } else if (!strncmp(info->params.host_bus_type, "IBND", 4) || - !strncmp(info->params.host_bus_type, "XPRS", 4) || !strncmp(info->params.host_bus_type, "HTPT", 4)) { p += scnprintf(p, left, "\tTBD: %llx\n", @@ -668,7 +668,7 @@ edd_get_pci_dev(struct edd_device *edev) { struct edd_info *info = edd_dev_get_info(edev); - if (edd_dev_is_type(edev, "PCI")) { + if (edd_dev_is_type(edev, "PCI") || edd_dev_is_type(edev, "XPRS")) { return pci_get_bus_and_slot(info->params.interface_path.pci.bus, PCI_DEVFN(info->params.interface_path.pci.slot, info->params.interface_path.pci. -- cgit v1.2.3 From 14fc315fa30d128760c7edeff56530142576cd2e Mon Sep 17 00:00:00 2001 From: Vasu Dev Date: Fri, 28 Oct 2011 11:34:12 -0700 Subject: [SCSI] libfc: fix checking FC_TYPE_BLS Its checked after skb freed, so instead have fh_type cached and then check FC_TYPE_BLS against cached fh_type value. This wrong check was causing double exch locking as reported by Bhanu at https://lists.open-fcoe.org/pipermail/devel/2011-October/011793.html Signed-off-by: Vasu Dev Tested-by: Bhanu Prakash Gollapudi Signed-off-by: Yi Zou Signed-off-by: James Bottomley --- drivers/scsi/libfc/fc_exch.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c index 7c055fdca45d..81235f36adc1 100644 --- a/drivers/scsi/libfc/fc_exch.c +++ b/drivers/scsi/libfc/fc_exch.c @@ -469,6 +469,7 @@ static int fc_seq_send(struct fc_lport *lport, struct fc_seq *sp, struct fc_frame_header *fh = fc_frame_header_get(fp); int error; u32 f_ctl; + u8 fh_type = fh->fh_type; ep = fc_seq_exch(sp); WARN_ON((ep->esb_stat & ESB_ST_SEQ_INIT) != ESB_ST_SEQ_INIT); @@ -493,7 +494,7 @@ static int fc_seq_send(struct fc_lport *lport, struct fc_seq *sp, */ error = lport->tt.frame_send(lport, fp); - if (fh->fh_type == FC_TYPE_BLS) + if (fh_type == FC_TYPE_BLS) return error; /* -- cgit v1.2.3 From b6e3c84034b93e6acc895711f74730e235dfe9d2 Mon Sep 17 00:00:00 2001 From: Vasu Dev Date: Fri, 28 Oct 2011 11:34:17 -0700 Subject: [SCSI] libfc: avoid exchanges collision during lport reset Currently timer delay is large and is using msleep to avoid avoid exchanges collision across lport reset, so instead do this by initializing exches pool indexes during reset also. Signed-off-by: Vasu Dev Tested-by: Bhanu Prakash Gollapudi Signed-off-by: Yi Zou Signed-off-by: James Bottomley --- drivers/scsi/libfc/fc_exch.c | 4 ++++ drivers/scsi/libfc/fc_lport.c | 10 +--------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c index 81235f36adc1..1b22130035da 100644 --- a/drivers/scsi/libfc/fc_exch.c +++ b/drivers/scsi/libfc/fc_exch.c @@ -1793,6 +1793,9 @@ restart: goto restart; } } + pool->next_index = 0; + pool->left = FC_XID_UNKNOWN; + pool->right = FC_XID_UNKNOWN; spin_unlock_bh(&pool->lock); } @@ -2281,6 +2284,7 @@ struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lport, goto free_mempool; for_each_possible_cpu(cpu) { pool = per_cpu_ptr(mp->pool, cpu); + pool->next_index = 0; pool->left = FC_XID_UNKNOWN; pool->right = FC_XID_UNKNOWN; spin_lock_init(&pool->lock); diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c index 628f347404f9..e0fb89133566 100644 --- a/drivers/scsi/libfc/fc_lport.c +++ b/drivers/scsi/libfc/fc_lport.c @@ -1030,16 +1030,8 @@ static void fc_lport_enter_reset(struct fc_lport *lport) FCH_EVT_LIPRESET, 0); fc_vports_linkchange(lport); fc_lport_reset_locked(lport); - if (lport->link_up) { - /* - * Wait upto resource allocation time out before - * doing re-login since incomplete FIP exchanged - * from last session may collide with exchanges - * in new session. - */ - msleep(lport->r_a_tov); + if (lport->link_up) fc_lport_enter_flogi(lport); - } } /** -- cgit v1.2.3 From 907c07d45199f954ddcf66c2c9763c87d012cb15 Mon Sep 17 00:00:00 2001 From: Vasu Dev Date: Fri, 28 Oct 2011 11:34:23 -0700 Subject: [SCSI] libfc: improve flogi retries to avoid lport stuck Adds more cases to do flogi retry, now also retry on getting bad response due to either no ELS response or flogi response payload length not large enough. In those cases flogi was not retried and that was leaving lport offline. Signed-off-by: Vasu Dev Tested-by: Bhanu Prakash Gollapudi Signed-off-by: Yi Zou Signed-off-by: James Bottomley --- drivers/scsi/fcoe/fcoe.c | 13 +++---- drivers/scsi/libfc/fc_lport.c | 90 +++++++++++++++++++++++-------------------- 2 files changed, 54 insertions(+), 49 deletions(-) diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 61384ee4049b..cefbe44bb84a 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -2347,14 +2347,11 @@ static void fcoe_flogi_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg) goto done; mac = fr_cb(fp)->granted_mac; - if (is_zero_ether_addr(mac)) { - /* pre-FIP */ - if (fcoe_ctlr_recv_flogi(fip, lport, fp)) { - fc_frame_free(fp); - return; - } - } - fcoe_update_src_mac(lport, mac); + /* pre-FIP */ + if (is_zero_ether_addr(mac)) + fcoe_ctlr_recv_flogi(fip, lport, fp); + if (!is_zero_ether_addr(mac)) + fcoe_update_src_mac(lport, mac); done: fc_lport_flogi_resp(seq, fp, lport); } diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c index e0fb89133566..2cb12b9cd3e8 100644 --- a/drivers/scsi/libfc/fc_lport.c +++ b/drivers/scsi/libfc/fc_lport.c @@ -1473,6 +1473,7 @@ void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp, void *lp_arg) { struct fc_lport *lport = lp_arg; + struct fc_frame_header *fh; struct fc_els_flogi *flp; u32 did; u16 csp_flags; @@ -1500,49 +1501,56 @@ void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp, goto err; } + fh = fc_frame_header_get(fp); did = fc_frame_did(fp); - if (fc_frame_payload_op(fp) == ELS_LS_ACC && did) { - flp = fc_frame_payload_get(fp, sizeof(*flp)); - if (flp) { - mfs = ntohs(flp->fl_csp.sp_bb_data) & - FC_SP_BB_DATA_MASK; - if (mfs >= FC_SP_MIN_MAX_PAYLOAD && - mfs < lport->mfs) - lport->mfs = mfs; - csp_flags = ntohs(flp->fl_csp.sp_features); - r_a_tov = ntohl(flp->fl_csp.sp_r_a_tov); - e_d_tov = ntohl(flp->fl_csp.sp_e_d_tov); - if (csp_flags & FC_SP_FT_EDTR) - e_d_tov /= 1000000; - - lport->npiv_enabled = !!(csp_flags & FC_SP_FT_NPIV_ACC); - - if ((csp_flags & FC_SP_FT_FPORT) == 0) { - if (e_d_tov > lport->e_d_tov) - lport->e_d_tov = e_d_tov; - lport->r_a_tov = 2 * e_d_tov; - fc_lport_set_port_id(lport, did, fp); - printk(KERN_INFO "host%d: libfc: " - "Port (%6.6x) entered " - "point-to-point mode\n", - lport->host->host_no, did); - fc_lport_ptp_setup(lport, fc_frame_sid(fp), - get_unaligned_be64( - &flp->fl_wwpn), - get_unaligned_be64( - &flp->fl_wwnn)); - } else { - lport->e_d_tov = e_d_tov; - lport->r_a_tov = r_a_tov; - fc_host_fabric_name(lport->host) = - get_unaligned_be64(&flp->fl_wwnn); - fc_lport_set_port_id(lport, did, fp); - fc_lport_enter_dns(lport); - } - } - } else { - FC_LPORT_DBG(lport, "FLOGI RJT or bad response\n"); + if (fh->fh_r_ctl != FC_RCTL_ELS_REP || did == 0 || + fc_frame_payload_op(fp) != ELS_LS_ACC) { + FC_LPORT_DBG(lport, "FLOGI not accepted or bad response\n"); fc_lport_error(lport, fp); + goto err; + } + + flp = fc_frame_payload_get(fp, sizeof(*flp)); + if (!flp) { + FC_LPORT_DBG(lport, "FLOGI bad response\n"); + fc_lport_error(lport, fp); + goto err; + } + + mfs = ntohs(flp->fl_csp.sp_bb_data) & + FC_SP_BB_DATA_MASK; + if (mfs >= FC_SP_MIN_MAX_PAYLOAD && + mfs < lport->mfs) + lport->mfs = mfs; + csp_flags = ntohs(flp->fl_csp.sp_features); + r_a_tov = ntohl(flp->fl_csp.sp_r_a_tov); + e_d_tov = ntohl(flp->fl_csp.sp_e_d_tov); + if (csp_flags & FC_SP_FT_EDTR) + e_d_tov /= 1000000; + + lport->npiv_enabled = !!(csp_flags & FC_SP_FT_NPIV_ACC); + + if ((csp_flags & FC_SP_FT_FPORT) == 0) { + if (e_d_tov > lport->e_d_tov) + lport->e_d_tov = e_d_tov; + lport->r_a_tov = 2 * e_d_tov; + fc_lport_set_port_id(lport, did, fp); + printk(KERN_INFO "host%d: libfc: " + "Port (%6.6x) entered " + "point-to-point mode\n", + lport->host->host_no, did); + fc_lport_ptp_setup(lport, fc_frame_sid(fp), + get_unaligned_be64( + &flp->fl_wwpn), + get_unaligned_be64( + &flp->fl_wwnn)); + } else { + lport->e_d_tov = e_d_tov; + lport->r_a_tov = r_a_tov; + fc_host_fabric_name(lport->host) = + get_unaligned_be64(&flp->fl_wwnn); + fc_lport_set_port_id(lport, did, fp); + fc_lport_enter_dns(lport); } out: -- cgit v1.2.3 From 99a700bcc75429ba84a672d04f0b650dcc5b3042 Mon Sep 17 00:00:00 2001 From: "Robin H. Johnson" Date: Mon, 24 Oct 2011 22:30:08 +0000 Subject: [SCSI] mv_sas: OCZ RevoDrive3 & zDrive R4 support In the OCZ RevoDrive3/zDrive R4 series, the "OCZ SuperScale Storage Controller" with "Virtualized Controller Architecture 2.0" really seems to be a Marvell 88SE9485 part, with OCZ firmware/BIOS. Developed and tested on OCZ RevoDrive3 120GB [PCI 1b85:1021] Should work on: - OCZ RevoDrive3 (2x SandForce 2281) - OCZ RevoDrive3 X2 (4x SandForce 2281) - OCZ zDrive R4 CM84 (4x SandForce 2281) - OCZ zDrive R4 CM88 (8x SandForce 2281) - OCZ zDrive R4 RM84 (4x SandForce 2582) - OCZ zDrive R4 RM88 (8x SandForce 2582) All of this because a friend recently bought a OCZ RevoDrive3 and was bitten by the lack of Linux support. Notes from testing: ------------------- - SMART works. - VPD Device Identification is "OCZ-REVODRIVE3" - Thin provisioning/TRIM seems to be implemented as WRITE SAME UNMAP, with deterministic (non-zero) read after TRIM, but I'm not sure if it works 100% in my testing. - Some of the tuning in the firmware seems to ensure much better performance when in a RAID0 setup than using the two devices seperately. I have not tested booting from the SSD, because all of this was developed and tested remotely from the actual hardware. Signed-off-by: Robin H. Johnson Thanks-To: Gordon Pritchard Acked-by: Xiangliang Yu Signed-off-by: James Bottomley --- drivers/scsi/mvsas/mv_init.c | 10 ++++++++++ include/linux/pci_ids.h | 2 ++ 2 files changed, 12 insertions(+) diff --git a/drivers/scsi/mvsas/mv_init.c b/drivers/scsi/mvsas/mv_init.c index 621b5e072758..6f589195746c 100644 --- a/drivers/scsi/mvsas/mv_init.c +++ b/drivers/scsi/mvsas/mv_init.c @@ -732,6 +732,16 @@ static struct pci_device_id __devinitdata mvs_pci_table[] = { .class_mask = 0, .driver_data = chip_9485, }, + { PCI_VDEVICE(OCZ, 0x1021), chip_9485}, /* OCZ RevoDrive3 */ + { PCI_VDEVICE(OCZ, 0x1022), chip_9485}, /* OCZ RevoDrive3/zDriveR4 (exact model unknown) */ + { PCI_VDEVICE(OCZ, 0x1040), chip_9485}, /* OCZ RevoDrive3/zDriveR4 (exact model unknown) */ + { PCI_VDEVICE(OCZ, 0x1041), chip_9485}, /* OCZ RevoDrive3/zDriveR4 (exact model unknown) */ + { PCI_VDEVICE(OCZ, 0x1042), chip_9485}, /* OCZ RevoDrive3/zDriveR4 (exact model unknown) */ + { PCI_VDEVICE(OCZ, 0x1043), chip_9485}, /* OCZ RevoDrive3/zDriveR4 (exact model unknown) */ + { PCI_VDEVICE(OCZ, 0x1044), chip_9485}, /* OCZ RevoDrive3/zDriveR4 (exact model unknown) */ + { PCI_VDEVICE(OCZ, 0x1080), chip_9485}, /* OCZ RevoDrive3/zDriveR4 (exact model unknown) */ + { PCI_VDEVICE(OCZ, 0x1083), chip_9485}, /* OCZ RevoDrive3/zDriveR4 (exact model unknown) */ + { PCI_VDEVICE(OCZ, 0x1084), chip_9485}, /* OCZ RevoDrive3/zDriveR4 (exact model unknown) */ { } /* terminate list */ }; diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 1679ff6931f9..3fdf251389de 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2873,3 +2873,5 @@ #define PCI_VENDOR_ID_XEN 0x5853 #define PCI_DEVICE_ID_XEN_PLATFORM 0x0001 + +#define PCI_VENDOR_ID_OCZ 0x1b85 -- cgit v1.2.3 From eae627e4ba07cb5765bbfdfec514f6808326a691 Mon Sep 17 00:00:00 2001 From: Marcos Paulo de Souza Date: Wed, 2 Nov 2011 11:17:17 -0200 Subject: [SCSI] scsi_transport_iscsi: Fix documentation os parameter Fixes documentation of a parameter of iscsi_bsg_host_add function to silence to make htmldocs Signed-off-by: Marcos Paulo de Souza Reviewed-by: Mike Christie Signed-off-by: James Bottomley --- drivers/scsi/scsi_transport_iscsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index 1bcd65a509e6..96029e6d027f 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -520,7 +520,7 @@ fail_host_msg: /** * iscsi_bsg_host_add - Create and add the bsg hooks to receive requests * @shost: shost for iscsi_host - * @cls_host: iscsi_cls_host adding the structures to + * @ihost: iscsi_cls_host adding the structures to */ static int iscsi_bsg_host_add(struct Scsi_Host *shost, struct iscsi_cls_host *ihost) -- cgit v1.2.3 From ab72002bc53eb8cf06615503878ee71494921fcc Mon Sep 17 00:00:00 2001 From: "Moger, Babu" Date: Thu, 27 Oct 2011 14:36:32 -0400 Subject: [SCSI] scsi_dh_alua: Fix the time inteval for alua rtpg commands This patch corrects the retry interval for alua rtpg command. Purpose was to retry the commands in seconds. But that was not happening. Reason is msleep takes argument in milliseconds. Also added minor text after successful attach. Signed-off-by: Babu Moger Acked-by: Hannes Reinecke Signed-off-by: James Bottomley --- drivers/scsi/device_handler/scsi_dh_alua.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index 627f4b5e5176..fe4df2da309c 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -507,7 +507,7 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_dh_data *h) int len, k, off, valid_states = 0; unsigned char *ucp; unsigned err; - unsigned long expiry, interval = 1; + unsigned long expiry, interval = 1000; expiry = round_jiffies_up(jiffies + ALUA_FAILOVER_TIMEOUT); retry: @@ -734,6 +734,7 @@ static int alua_bus_attach(struct scsi_device *sdev) spin_lock_irqsave(sdev->request_queue->queue_lock, flags); sdev->scsi_dh_data = scsi_dh_data; spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); + sdev_printk(KERN_NOTICE, sdev, "%s: Attached\n", ALUA_DH_NAME); return 0; -- cgit v1.2.3 From f7c9c6bb14f3104608a3a83cadea10a6943d2804 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Thu, 3 Nov 2011 08:56:22 +1100 Subject: [SCSI] Fix block queue and elevator memory leak in scsi_alloc_sdev When looking at memory consumption issues I noticed quite a lot of memory in the kmalloc-2048 bucket: OBJS ACTIVE USE OBJ SIZE SLABS OBJ/SLAB CACHE SIZE NAME 6561 6471 98% 2.30K 243 27 15552K kmalloc-2048 Over 15MB. slub debug shows that cfq is responsible for almost all of it: # sort -nr /sys/kernel/slab/kmalloc-2048/alloc_calls 6402 .cfq_init_queue+0xec/0x460 age=43423/43564/43655 pid=1 cpus=4,11,13 In scsi_alloc_sdev we do scsi_alloc_queue but if slave_alloc fails we don't free it with scsi_free_queue. The patch below fixes the issue: OBJS ACTIVE USE OBJ SIZE SLABS OBJ/SLAB CACHE SIZE NAME 135 72 53% 2.30K 5 27 320K kmalloc-2048 # cat /sys/kernel/slab/kmalloc-2048/alloc_calls 3 .cfq_init_queue+0xec/0x460 age=3811/3876/3925 pid=1 cpus=4,11,13 Signed-off-by: Anton Blanchard Cc: #2.6.38+ Signed-off-by: James Bottomley --- drivers/scsi/scsi_scan.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 44e8ca398efa..72273a0e5666 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -322,6 +322,7 @@ out_device_destroy: scsi_device_set_state(sdev, SDEV_DEL); transport_destroy_device(&sdev->sdev_gendev); put_device(&sdev->sdev_dev); + scsi_free_queue(sdev->request_queue); put_device(&sdev->sdev_gendev); out: if (display_failure_msg) -- cgit v1.2.3