summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorken <ken@FreeBSD.org>2012-01-26 18:17:21 +0000
committerken <ken@FreeBSD.org>2012-01-26 18:17:21 +0000
commit9e157c5aece56db2baf55253289ede7ecfef72b0 (patch)
tree40d4c8e4ee1e48d5ade6eb6e61dfec60342fd8a6
parent7b6e352dc6de233372b2374a31b24815cde2ec92 (diff)
downloadFreeBSD-src-9e157c5aece56db2baf55253289ede7ecfef72b0.zip
FreeBSD-src-9e157c5aece56db2baf55253289ede7ecfef72b0.tar.gz
Bring in the LSI-supported version of the mps(4) driver.
This involves significant changes to the mps(4) driver, but is not a complete rewrite. Some of the changes in this version of the driver: - Integrated RAID (IR) support. - Support for WarpDrive controllers. - Support for SCSI protection information (EEDP). - Support for TLR (Transport Level Retries), needed for tape drives. - Improved error recovery code. - ioctl interface compatible with LSI utilities. mps.4: Update the mps(4) driver man page somewhat for the driver changes. The list of supported hardware still needs to be updated to reflect the full list of supported cards. conf/files: Add the new driver files. mps/mpi/*: Updated version of the MPI header files, with a BSD style copyright. mps/*: See above for a description of the new driver features. modules/mps/Makefile: Add the new mps(4) driver files. Submitted by: Kashyap Desai <Kashyap.Desai@lsi.com> Reviewed by: ken MFC after: 1 week
-rw-r--r--sys/conf/files3
-rw-r--r--sys/dev/mps/mpi/mpi2.h62
-rw-r--r--sys/dev/mps/mpi/mpi2_cnfg.h321
-rw-r--r--sys/dev/mps/mpi/mpi2_hbd.h42
-rw-r--r--sys/dev/mps/mpi/mpi2_history.txt149
-rw-r--r--sys/dev/mps/mpi/mpi2_init.h61
-rw-r--r--sys/dev/mps/mpi/mpi2_ioc.h162
-rw-r--r--sys/dev/mps/mpi/mpi2_ra.h33
-rw-r--r--sys/dev/mps/mpi/mpi2_raid.h37
-rw-r--r--sys/dev/mps/mpi/mpi2_sas.h45
-rw-r--r--sys/dev/mps/mpi/mpi2_targ.h39
-rw-r--r--sys/dev/mps/mpi/mpi2_tool.h87
-rw-r--r--sys/dev/mps/mpi/mpi2_type.h33
-rw-r--r--sys/dev/mps/mps.c761
-rw-r--r--sys/dev/mps/mps_config.c1393
-rw-r--r--sys/dev/mps/mps_ioctl.h285
-rw-r--r--sys/dev/mps/mps_mapping.c2268
-rw-r--r--sys/dev/mps/mps_mapping.h71
-rw-r--r--sys/dev/mps/mps_pci.c62
-rw-r--r--sys/dev/mps/mps_sas.c2989
-rw-r--r--sys/dev/mps/mps_sas.h161
-rw-r--r--sys/dev/mps/mps_sas_lsi.c865
-rw-r--r--sys/dev/mps/mps_table.c6
-rw-r--r--sys/dev/mps/mps_user.c1495
-rw-r--r--sys/dev/mps/mpsvar.h400
-rw-r--r--sys/modules/mps/Makefile5
26 files changed, 10536 insertions, 1299 deletions
diff --git a/sys/conf/files b/sys/conf/files
index 85c87b0..bdada67 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1469,8 +1469,11 @@ dev/mmc/mmcbus_if.m standard
dev/mmc/mmcsd.c optional mmcsd
dev/mn/if_mn.c optional mn pci
dev/mps/mps.c optional mps
+dev/mps/mps_config.c optional mps
+dev/mps/mps_mapping.c optional mps
dev/mps/mps_pci.c optional mps pci
dev/mps/mps_sas.c optional mps
+dev/mps/mps_sas_lsi.c optional mps
dev/mps/mps_table.c optional mps
dev/mps/mps_user.c optional mps
dev/mpt/mpt.c optional mpt
diff --git a/sys/dev/mps/mpi/mpi2.h b/sys/dev/mps/mpi/mpi2.h
index 6d883bc..b9e46ad 100644
--- a/sys/dev/mps/mpi/mpi2.h
+++ b/sys/dev/mps/mpi/mpi2.h
@@ -1,6 +1,35 @@
-/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ *
+ * $FreeBSD$
+ */
+
/*
- * Copyright (c) 2000-2009 LSI Corporation.
+ * Copyright (c) 2000-2011 LSI Corporation.
*
*
* Name: mpi2.h
@@ -9,7 +38,7 @@
* scatter/gather formats.
* Creation Date: June 21, 2006
*
- * mpi2.h Version: 02.00.14
+ * mpi2.h Version: 02.00.18
*
* Version History
* ---------------
@@ -58,6 +87,15 @@
* Added MSI-x index mask and shift for Reply Post Host
* Index register.
* Added function code for Host Based Discovery Action.
+ * 02-10-10 02.00.15 Bumped MPI2_HEADER_VERSION_UNIT.
+ * Added define for MPI2_FUNCTION_PWR_MGMT_CONTROL.
+ * Added defines for product-specific range of message
+ * function codes, 0xF0 to 0xFF.
+ * 05-12-10 02.00.16 Bumped MPI2_HEADER_VERSION_UNIT.
+ * Added alternative defines for the SGE Direction bit.
+ * 08-11-10 02.00.17 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 11-10-10 02.00.18 Bumped MPI2_HEADER_VERSION_UNIT.
+ * Added MPI2_IEEE_SGE_FLAGS_SYSTEMPLBCPI_ADDR define.
* --------------------------------------------------------------------------
*/
@@ -83,7 +121,7 @@
#define MPI2_VERSION_02_00 (0x0200)
/* versioning for this MPI header set */
-#define MPI2_HEADER_VERSION_UNIT (0x0E)
+#define MPI2_HEADER_VERSION_UNIT (0x12)
#define MPI2_HEADER_VERSION_DEV (0x00)
#define MPI2_HEADER_VERSION_UNIT_MASK (0xFF00)
#define MPI2_HEADER_VERSION_UNIT_SHIFT (8)
@@ -476,8 +514,6 @@ typedef union _MPI2_REPLY_DESCRIPTORS_UNION
/*****************************************************************************
*
* Message Functions
-* 0x80 -> 0x8F reserved for private message use per product
-*
*
*****************************************************************************/
@@ -508,6 +544,9 @@ typedef union _MPI2_REPLY_DESCRIPTORS_UNION
#define MPI2_FUNCTION_TARGET_CMD_BUF_LIST_POST (0x25) /* Target Command Buffer Post List */
#define MPI2_FUNCTION_RAID_ACCELERATOR (0x2C) /* RAID Accelerator */
#define MPI2_FUNCTION_HOST_BASED_DISCOVERY_ACTION (0x2F) /* Host Based Discovery Action */
+#define MPI2_FUNCTION_PWR_MGMT_CONTROL (0x30) /* Power Management Control */
+#define MPI2_FUNCTION_MIN_PRODUCT_SPECIFIC (0xF0) /* beginning of product-specific range */
+#define MPI2_FUNCTION_MAX_PRODUCT_SPECIFIC (0xFF) /* end of product-specific range */
@@ -922,6 +961,9 @@ typedef struct _MPI2_MPI_SGE_UNION
#define MPI2_SGE_FLAGS_IOC_TO_HOST (0x00)
#define MPI2_SGE_FLAGS_HOST_TO_IOC (0x04)
+#define MPI2_SGE_FLAGS_DEST (MPI2_SGE_FLAGS_IOC_TO_HOST)
+#define MPI2_SGE_FLAGS_SOURCE (MPI2_SGE_FLAGS_HOST_TO_IOC)
+
/* Address Size */
#define MPI2_SGE_FLAGS_32_BIT_ADDRESSING (0x00)
@@ -1046,11 +1088,11 @@ typedef struct _MPI2_IEEE_SGE_UNION
/* Data Location Address Space */
#define MPI2_IEEE_SGE_FLAGS_ADDR_MASK (0x03)
-#define MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR (0x00)
-#define MPI2_IEEE_SGE_FLAGS_IOCDDR_ADDR (0x01)
+#define MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR (0x00) /* IEEE Simple Element only */
+#define MPI2_IEEE_SGE_FLAGS_IOCDDR_ADDR (0x01) /* IEEE Simple Element only */
#define MPI2_IEEE_SGE_FLAGS_IOCPLB_ADDR (0x02)
-#define MPI2_IEEE_SGE_FLAGS_IOCPLBNTA_ADDR (0x03)
-
+#define MPI2_IEEE_SGE_FLAGS_IOCPLBNTA_ADDR (0x03) /* IEEE Simple Element only */
+#define MPI2_IEEE_SGE_FLAGS_SYSTEMPLBCPI_ADDR (0x03) /* IEEE Chain Element only */
/****************************************************************************
* IEEE SGE operation Macros
diff --git a/sys/dev/mps/mpi/mpi2_cnfg.h b/sys/dev/mps/mpi/mpi2_cnfg.h
index 78f26f1..ef3334f 100644
--- a/sys/dev/mps/mpi/mpi2_cnfg.h
+++ b/sys/dev/mps/mpi/mpi2_cnfg.h
@@ -1,13 +1,42 @@
-/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ *
+ * $FreeBSD$
+ */
+
/*
- * Copyright (c) 2000-2009 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.13
+ * mpi2_cnfg.h Version: 02.00.17
*
* Version History
* ---------------
@@ -110,6 +139,31 @@
* Added Ethernet configuration pages.
* 10-28-09 02.00.13 Added MPI2_IOUNITPAGE1_ENABLE_HOST_BASED_DISCOVERY.
* Added SAS PHY Page 4 structure and defines.
+ * 02-10-10 02.00.14 Modified the comments for the configuration page
+ * structures that contain an array of data. The host
+ * should use the "count" field in the page data (e.g. the
+ * NumPhys field) to determine the number of valid elements
+ * in the array.
+ * Added/modified some MPI2_MFGPAGE_DEVID_SAS defines.
+ * Added PowerManagementCapabilities to IO Unit Page 7.
+ * Added PortWidthModGroup field to
+ * MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS.
+ * Added MPI2_CONFIG_PAGE_SASIOUNIT_6 and related defines.
+ * Added MPI2_CONFIG_PAGE_SASIOUNIT_7 and related defines.
+ * Added MPI2_CONFIG_PAGE_SASIOUNIT_8 and related defines.
+ * 05-12-10 02.00.15 Added MPI2_RAIDVOL0_STATUS_FLAG_VOL_NOT_CONSISTENT
+ * define.
+ * Added MPI2_PHYSDISK0_INCOMPATIBLE_MEDIA_TYPE define.
+ * Added MPI2_SAS_NEG_LINK_RATE_UNSUPPORTED_PHY define.
+ * 08-11-10 02.00.16 Removed IO Unit Page 1 device path (multi-pathing)
+ * defines.
+ * 11-10-10 02.00.17 Added ReceptacleID field (replacing Reserved1) to
+ * MPI2_MANPAGE7_CONNECTOR_INFO and reworked defines for
+ * the Pinout field.
+ * Added BoardTemperature and BoardTemperatureUnits fields
+ * to MPI2_CONFIG_PAGE_IO_UNIT_7.
+ * Added MPI2_CONFIG_EXTPAGETYPE_EXT_MANUFACTURING define
+ * and MPI2_CONFIG_PAGE_EXT_MAN_PS structure.
* --------------------------------------------------------------------------
*/
@@ -193,6 +247,7 @@ typedef union _MPI2_CONFIG_EXT_PAGE_HEADER_UNION
#define MPI2_CONFIG_EXTPAGETYPE_DRIVER_MAPPING (0x17)
#define MPI2_CONFIG_EXTPAGETYPE_SAS_PORT (0x18)
#define MPI2_CONFIG_EXTPAGETYPE_ETHERNET (0x19)
+#define MPI2_CONFIG_EXTPAGETYPE_EXT_MANUFACTURING (0x1A)
/*****************************************************************************
@@ -322,7 +377,7 @@ typedef struct _MPI2_CONFIG_REQUEST
#define MPI2_CONFIG_ACTION_PAGE_READ_NVRAM (0x06)
#define MPI2_CONFIG_ACTION_PAGE_GET_CHANGEABLE (0x07)
-/* values for SGLFlags field are in the SGL section of mpi2.h */
+/* use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */
/* Config Reply Message */
@@ -368,14 +423,19 @@ typedef struct _MPI2_CONFIG_REPLY
#define MPI2_MFGPAGE_DEVID_SAS2116_1 (0x0064)
#define MPI2_MFGPAGE_DEVID_SAS2116_2 (0x0065)
+#define MPI2_MFGPAGE_DEVID_SSS6200 (0x007E)
+
#define MPI2_MFGPAGE_DEVID_SAS2208_1 (0x0080)
#define MPI2_MFGPAGE_DEVID_SAS2208_2 (0x0081)
#define MPI2_MFGPAGE_DEVID_SAS2208_3 (0x0082)
#define MPI2_MFGPAGE_DEVID_SAS2208_4 (0x0083)
#define MPI2_MFGPAGE_DEVID_SAS2208_5 (0x0084)
#define MPI2_MFGPAGE_DEVID_SAS2208_6 (0x0085)
-#define MPI2_MFGPAGE_DEVID_SAS2208_7 (0x0086)
-#define MPI2_MFGPAGE_DEVID_SAS2208_8 (0x0087)
+#define MPI2_MFGPAGE_DEVID_SAS2308_1 (0x0086)
+#define MPI2_MFGPAGE_DEVID_SAS2308_2 (0x0087)
+#define MPI2_MFGPAGE_DEVID_SAS2308_3 (0x006E)
+
+
/* Manufacturing Page 0 */
@@ -541,7 +601,7 @@ typedef struct _MPI2_CONFIG_PAGE_MAN_4
/*
* Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check Header.PageLength or NumPhys at runtime.
+ * one and check the value returned for NumPhys at runtime.
*/
#ifndef MPI2_MAN_PAGE_5_PHY_ENTRIES
#define MPI2_MAN_PAGE_5_PHY_ENTRIES (1)
@@ -590,23 +650,31 @@ typedef struct _MPI2_MANPAGE7_CONNECTOR_INFO
U32 Pinout; /* 0x00 */
U8 Connector[16]; /* 0x04 */
U8 Location; /* 0x14 */
- U8 Reserved1; /* 0x15 */
+ U8 ReceptacleID; /* 0x15 */
U16 Slot; /* 0x16 */
U32 Reserved2; /* 0x18 */
} MPI2_MANPAGE7_CONNECTOR_INFO, MPI2_POINTER PTR_MPI2_MANPAGE7_CONNECTOR_INFO,
Mpi2ManPage7ConnectorInfo_t, MPI2_POINTER pMpi2ManPage7ConnectorInfo_t;
/* defines for the Pinout field */
-#define MPI2_MANPAGE7_PINOUT_SFF_8484_L4 (0x00080000)
-#define MPI2_MANPAGE7_PINOUT_SFF_8484_L3 (0x00040000)
-#define MPI2_MANPAGE7_PINOUT_SFF_8484_L2 (0x00020000)
-#define MPI2_MANPAGE7_PINOUT_SFF_8484_L1 (0x00010000)
-#define MPI2_MANPAGE7_PINOUT_SFF_8470_L4 (0x00000800)
-#define MPI2_MANPAGE7_PINOUT_SFF_8470_L3 (0x00000400)
-#define MPI2_MANPAGE7_PINOUT_SFF_8470_L2 (0x00000200)
-#define MPI2_MANPAGE7_PINOUT_SFF_8470_L1 (0x00000100)
-#define MPI2_MANPAGE7_PINOUT_SFF_8482 (0x00000002)
-#define MPI2_MANPAGE7_PINOUT_CONNECTION_UNKNOWN (0x00000001)
+#define MPI2_MANPAGE7_PINOUT_LANE_MASK (0x0000FF00)
+#define MPI2_MANPAGE7_PINOUT_LANE_SHIFT (8)
+
+#define MPI2_MANPAGE7_PINOUT_TYPE_MASK (0x000000FF)
+#define MPI2_MANPAGE7_PINOUT_TYPE_UNKNOWN (0x00)
+#define MPI2_MANPAGE7_PINOUT_SATA_SINGLE (0x01)
+#define MPI2_MANPAGE7_PINOUT_SFF_8482 (0x02)
+#define MPI2_MANPAGE7_PINOUT_SFF_8486 (0x03)
+#define MPI2_MANPAGE7_PINOUT_SFF_8484 (0x04)
+#define MPI2_MANPAGE7_PINOUT_SFF_8087 (0x05)
+#define MPI2_MANPAGE7_PINOUT_SFF_8643_4I (0x06)
+#define MPI2_MANPAGE7_PINOUT_SFF_8643_8I (0x07)
+#define MPI2_MANPAGE7_PINOUT_SFF_8470 (0x08)
+#define MPI2_MANPAGE7_PINOUT_SFF_8088 (0x09)
+#define MPI2_MANPAGE7_PINOUT_SFF_8644_4X (0x0A)
+#define MPI2_MANPAGE7_PINOUT_SFF_8644_8X (0x0B)
+#define MPI2_MANPAGE7_PINOUT_SFF_8644_16X (0x0C)
+#define MPI2_MANPAGE7_PINOUT_SFF_8436 (0x0D)
/* defines for the Location field */
#define MPI2_MANPAGE7_LOCATION_UNKNOWN (0x01)
@@ -619,7 +687,7 @@ typedef struct _MPI2_MANPAGE7_CONNECTOR_INFO
/*
* Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check NumPhys at runtime.
+ * one and check the value returned for NumPhys at runtime.
*/
#ifndef MPI2_MANPAGE7_CONNECTOR_INFO_MAX
#define MPI2_MANPAGE7_CONNECTOR_INFO_MAX (1)
@@ -640,7 +708,7 @@ typedef struct _MPI2_CONFIG_PAGE_MAN_7
MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_7,
Mpi2ManufacturingPage7_t, MPI2_POINTER pMpi2ManufacturingPage7_t;
-#define MPI2_MANUFACTURING7_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING7_PAGEVERSION (0x01)
/* defines for the Flags field */
#define MPI2_MANPAGE7_FLAG_USE_SLOT_INFO (0x00000001)
@@ -717,6 +785,7 @@ typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_1
/* IO Unit Page 1 Flags defines */
#define MPI2_IOUNITPAGE1_ENABLE_HOST_BASED_DISCOVERY (0x00000800)
#define MPI2_IOUNITPAGE1_MASK_SATA_WRITE_CACHE (0x00000600)
+#define MPI2_IOUNITPAGE1_SATA_WRITE_CACHE_SHIFT (9)
#define MPI2_IOUNITPAGE1_ENABLE_SATA_WRITE_CACHE (0x00000000)
#define MPI2_IOUNITPAGE1_DISABLE_SATA_WRITE_CACHE (0x00000200)
#define MPI2_IOUNITPAGE1_UNCHANGED_SATA_WRITE_CACHE (0x00000400)
@@ -724,15 +793,13 @@ typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_1
#define MPI2_IOUNITPAGE1_DISABLE_IR (0x00000040)
#define MPI2_IOUNITPAGE1_DISABLE_TASK_SET_FULL_HANDLING (0x00000020)
#define MPI2_IOUNITPAGE1_IR_USE_STATIC_VOLUME_ID (0x00000004)
-#define MPI2_IOUNITPAGE1_MULTI_PATHING (0x00000002)
-#define MPI2_IOUNITPAGE1_SINGLE_PATHING (0x00000000)
/* IO Unit Page 3 */
/*
* Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check Header.PageLength at runtime.
+ * one and check the value returned for GPIOCount at runtime.
*/
#ifndef MPI2_IO_UNIT_PAGE_3_GPIO_VAL_MAX
#define MPI2_IO_UNIT_PAGE_3_GPIO_VAL_MAX (1)
@@ -761,7 +828,7 @@ typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_3
/*
* Upper layer code (drivers, utilities, etc.) should leave this define set to
- * one and check Header.PageLength or NumDmaEngines at runtime.
+ * one and check the value returned for NumDmaEngines at runtime.
*/
#ifndef MPI2_IOUNITPAGE5_DMAENGINE_ENTRIES
#define MPI2_IOUNITPAGE5_DMAENGINE_ENTRIES (1)
@@ -826,15 +893,17 @@ typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_7
U8 PCIeWidth; /* 0x06 */
U8 PCIeSpeed; /* 0x07 */
U32 ProcessorState; /* 0x08 */
- U32 Reserved2; /* 0x0C */
+ U32 PowerManagementCapabilities; /* 0x0C */
U16 IOCTemperature; /* 0x10 */
U8 IOCTemperatureUnits; /* 0x12 */
U8 IOCSpeed; /* 0x13 */
- U32 Reserved3; /* 0x14 */
+ U16 BoardTemperature; /* 0x14 */
+ U8 BoardTemperatureUnits; /* 0x16 */
+ U8 Reserved3; /* 0x17 */
} MPI2_CONFIG_PAGE_IO_UNIT_7, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IO_UNIT_7,
Mpi2IOUnitPage7_t, MPI2_POINTER pMpi2IOUnitPage7_t;
-#define MPI2_IOUNITPAGE7_PAGEVERSION (0x00)
+#define MPI2_IOUNITPAGE7_PAGEVERSION (0x02)
/* defines for IO Unit Page 7 PCIeWidth field */
#define MPI2_IOUNITPAGE7_PCIE_WIDTH_X1 (0x01)
@@ -855,6 +924,13 @@ typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_7
#define MPI2_IOUNITPAGE7_PSTATE_DISABLED (0x01)
#define MPI2_IOUNITPAGE7_PSTATE_ENABLED (0x02)
+/* defines for IO Unit Page 7 PowerManagementCapabilities field */
+#define MPI2_IOUNITPAGE7_PMCAP_12_5_PCT_IOCSPEED (0x00000400)
+#define MPI2_IOUNITPAGE7_PMCAP_25_0_PCT_IOCSPEED (0x00000200)
+#define MPI2_IOUNITPAGE7_PMCAP_50_0_PCT_IOCSPEED (0x00000100)
+#define MPI2_IOUNITPAGE7_PMCAP_PCIE_WIDTH_CHANGE (0x00000008)
+#define MPI2_IOUNITPAGE7_PMCAP_PCIE_SPEED_CHANGE (0x00000004)
+
/* defines for IO Unit Page 7 IOCTemperatureUnits field */
#define MPI2_IOUNITPAGE7_IOC_TEMP_NOT_PRESENT (0x00)
#define MPI2_IOUNITPAGE7_IOC_TEMP_FAHRENHEIT (0x01)
@@ -866,6 +942,11 @@ typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_7
#define MPI2_IOUNITPAGE7_IOC_SPEED_QUARTER (0x04)
#define MPI2_IOUNITPAGE7_IOC_SPEED_EIGHTH (0x08)
+/* defines for IO Unit Page 7 BoardTemperatureUnits field */
+#define MPI2_IOUNITPAGE7_BOARD_TEMP_NOT_PRESENT (0x00)
+#define MPI2_IOUNITPAGE7_BOARD_TEMP_FAHRENHEIT (0x01)
+#define MPI2_IOUNITPAGE7_BOARD_TEMP_CELSIUS (0x02)
+
/****************************************************************************
@@ -1198,7 +1279,7 @@ typedef struct _MPI2_CONFIG_PAGE_BIOS_3
/*
* Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check Header.PageLength or NumPhys at runtime.
+ * one and check the value returned for NumPhys at runtime.
*/
#ifndef MPI2_BIOS_PAGE_4_PHY_ENTRIES
#define MPI2_BIOS_PAGE_4_PHY_ENTRIES (1)
@@ -1272,7 +1353,7 @@ typedef struct _MPI2_RAIDVOL0_SETTINGS
/*
* Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check Header.PageLength at runtime.
+ * one and check the value returned for NumPhysDisks at runtime.
*/
#ifndef MPI2_RAID_VOL_PAGE_0_PHYSDISK_MAX
#define MPI2_RAID_VOL_PAGE_0_PHYSDISK_MAX (1)
@@ -1329,6 +1410,7 @@ typedef struct _MPI2_CONFIG_PAGE_RAID_VOL_0
#define MPI2_RAIDVOL0_STATUS_FLAG_CAPACITY_EXPANSION (0x00040000)
#define MPI2_RAIDVOL0_STATUS_FLAG_BACKGROUND_INIT (0x00020000)
#define MPI2_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS (0x00010000)
+#define MPI2_RAIDVOL0_STATUS_FLAG_VOL_NOT_CONSISTENT (0x00000080)
#define MPI2_RAIDVOL0_STATUS_FLAG_OCE_ALLOWED (0x00000040)
#define MPI2_RAIDVOL0_STATUS_FLAG_BGI_COMPLETE (0x00000020)
#define MPI2_RAIDVOL0_STATUS_FLAG_1E_OFFSET_MIRROR (0x00000000)
@@ -1451,11 +1533,15 @@ typedef struct _MPI2_CONFIG_PAGE_RD_PDISK_0
#define MPI2_PHYSDISK0_INCOMPATIBLE_MAX_LBA (0x03)
#define MPI2_PHYSDISK0_INCOMPATIBLE_SATA_EXTENDED_CMD (0x04)
#define MPI2_PHYSDISK0_INCOMPATIBLE_REMOVEABLE_MEDIA (0x05)
+#define MPI2_PHYSDISK0_INCOMPATIBLE_MEDIA_TYPE (0x06)
#define MPI2_PHYSDISK0_INCOMPATIBLE_UNKNOWN (0xFF)
/* PhysDiskAttributes defines */
+#define MPI2_PHYSDISK0_ATTRIB_MEDIA_MASK (0x0C)
#define MPI2_PHYSDISK0_ATTRIB_SOLID_STATE_DRIVE (0x08)
#define MPI2_PHYSDISK0_ATTRIB_HARD_DISK_DRIVE (0x04)
+
+#define MPI2_PHYSDISK0_ATTRIB_PROTOCOL_MASK (0x03)
#define MPI2_PHYSDISK0_ATTRIB_SAS_PROTOCOL (0x02)
#define MPI2_PHYSDISK0_ATTRIB_SATA_PROTOCOL (0x01)
@@ -1474,7 +1560,7 @@ typedef struct _MPI2_CONFIG_PAGE_RD_PDISK_0
/*
* Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check Header.PageLength or NumPhysDiskPaths at runtime.
+ * one and check the value returned for NumPhysDiskPaths at runtime.
*/
#ifndef MPI2_RAID_PHYS_DISK1_PATH_MAX
#define MPI2_RAID_PHYS_DISK1_PATH_MAX (1)
@@ -1527,6 +1613,7 @@ typedef struct _MPI2_CONFIG_PAGE_RD_PDISK_1
#define MPI2_SAS_NEG_LINK_RATE_SATA_OOB_COMPLETE (0x03)
#define MPI2_SAS_NEG_LINK_RATE_PORT_SELECTOR (0x04)
#define MPI2_SAS_NEG_LINK_RATE_SMP_RESET_IN_PROGRESS (0x05)
+#define MPI2_SAS_NEG_LINK_RATE_UNSUPPORTED_PHY (0x06)
#define MPI2_SAS_NEG_LINK_RATE_1_5 (0x08)
#define MPI2_SAS_NEG_LINK_RATE_3_0 (0x09)
#define MPI2_SAS_NEG_LINK_RATE_6_0 (0x0A)
@@ -1553,6 +1640,7 @@ typedef struct _MPI2_CONFIG_PAGE_RD_PDISK_1
#define MPI2_SAS_PHYINFO_PHY_VACANT (0x80000000)
#define MPI2_SAS_PHYINFO_PHY_POWER_CONDITION_MASK (0x18000000)
+#define MPI2_SAS_PHYINFO_SHIFT_PHY_POWER_CONDITION (27)
#define MPI2_SAS_PHYINFO_PHY_POWER_ACTIVE (0x00000000)
#define MPI2_SAS_PHYINFO_PHY_POWER_PARTIAL (0x08000000)
#define MPI2_SAS_PHYINFO_PHY_POWER_SLUMBER (0x10000000)
@@ -1636,7 +1724,7 @@ typedef struct _MPI2_SAS_IO_UNIT0_PHY_DATA
/*
* Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check Header.ExtPageLength or NumPhys at runtime.
+ * one and check the value returned for NumPhys at runtime.
*/
#ifndef MPI2_SAS_IOUNIT0_PHY_MAX
#define MPI2_SAS_IOUNIT0_PHY_MAX (1)
@@ -1707,7 +1795,7 @@ typedef struct _MPI2_SAS_IO_UNIT1_PHY_DATA
/*
* Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check Header.ExtPageLength or NumPhys at runtime.
+ * one and check the value returned for NumPhys at runtime.
*/
#ifndef MPI2_SAS_IOUNIT1_PHY_MAX
#define MPI2_SAS_IOUNIT1_PHY_MAX (1)
@@ -1798,7 +1886,7 @@ typedef struct _MPI2_SAS_IOUNIT4_SPINUP_GROUP
/*
* Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * four and check Header.ExtPageLength or NumPhys at runtime.
+ * one and check the value returned for NumPhys at runtime.
*/
#ifndef MPI2_SAS_IOUNIT4_PHY_MAX
#define MPI2_SAS_IOUNIT4_PHY_MAX (4)
@@ -1837,7 +1925,7 @@ typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_4
typedef struct _MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS
{
U8 ControlFlags; /* 0x00 */
- U8 Reserved1; /* 0x01 */
+ U8 PortWidthModGroup; /* 0x01 */
U16 InactivityTimerExponent; /* 0x02 */
U8 SATAPartialTimeout; /* 0x04 */
U8 Reserved2; /* 0x05 */
@@ -1857,6 +1945,9 @@ typedef struct _MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS
#define MPI2_SASIOUNIT5_CONTROL_SATA_SLUMBER_ENABLE (0x02)
#define MPI2_SASIOUNIT5_CONTROL_SATA_PARTIAL_ENABLE (0x01)
+/* defines for PortWidthModeGroup field */
+#define MPI2_SASIOUNIT5_PWMG_DISABLE (0xFF)
+
/* defines for InactivityTimerExponent field */
#define MPI2_SASIOUNIT5_ITE_MASK_SAS_SLUMBER (0x7000)
#define MPI2_SASIOUNIT5_ITE_SHIFT_SAS_SLUMBER (12)
@@ -1878,7 +1969,7 @@ typedef struct _MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS
/*
* Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check Header.ExtPageLength or NumPhys at runtime.
+ * one and check the value returned for NumPhys at runtime.
*/
#ifndef MPI2_SAS_IOUNIT5_PHY_MAX
#define MPI2_SAS_IOUNIT5_PHY_MAX (1)
@@ -1896,7 +1987,137 @@ typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_5
MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SASIOUNIT_5,
Mpi2SasIOUnitPage5_t, MPI2_POINTER pMpi2SasIOUnitPage5_t;
-#define MPI2_SASIOUNITPAGE5_PAGEVERSION (0x00)
+#define MPI2_SASIOUNITPAGE5_PAGEVERSION (0x01)
+
+
+/* SAS IO Unit Page 6 */
+
+typedef struct _MPI2_SAS_IO_UNIT6_PORT_WIDTH_MOD_GROUP_STATUS
+{
+ U8 CurrentStatus; /* 0x00 */
+ U8 CurrentModulation; /* 0x01 */
+ U8 CurrentUtilization; /* 0x02 */
+ U8 Reserved1; /* 0x03 */
+ U32 Reserved2; /* 0x04 */
+} MPI2_SAS_IO_UNIT6_PORT_WIDTH_MOD_GROUP_STATUS,
+ MPI2_POINTER PTR_MPI2_SAS_IO_UNIT6_PORT_WIDTH_MOD_GROUP_STATUS,
+ Mpi2SasIOUnit6PortWidthModGroupStatus_t,
+ MPI2_POINTER pMpi2SasIOUnit6PortWidthModGroupStatus_t;
+
+/* defines for CurrentStatus field */
+#define MPI2_SASIOUNIT6_STATUS_UNAVAILABLE (0x00)
+#define MPI2_SASIOUNIT6_STATUS_UNCONFIGURED (0x01)
+#define MPI2_SASIOUNIT6_STATUS_INVALID_CONFIG (0x02)
+#define MPI2_SASIOUNIT6_STATUS_LINK_DOWN (0x03)
+#define MPI2_SASIOUNIT6_STATUS_OBSERVATION_ONLY (0x04)
+#define MPI2_SASIOUNIT6_STATUS_INACTIVE (0x05)
+#define MPI2_SASIOUNIT6_STATUS_ACTIVE_IOUNIT (0x06)
+#define MPI2_SASIOUNIT6_STATUS_ACTIVE_HOST (0x07)
+
+/* defines for CurrentModulation field */
+#define MPI2_SASIOUNIT6_MODULATION_25_PERCENT (0x00)
+#define MPI2_SASIOUNIT6_MODULATION_50_PERCENT (0x01)
+#define MPI2_SASIOUNIT6_MODULATION_75_PERCENT (0x02)
+#define MPI2_SASIOUNIT6_MODULATION_100_PERCENT (0x03)
+
+/*
+ * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ * one and check the value returned for NumGroups at runtime.
+ */
+#ifndef MPI2_SAS_IOUNIT6_GROUP_MAX
+#define MPI2_SAS_IOUNIT6_GROUP_MAX (1)
+#endif
+
+typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_6
+{
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
+ U32 Reserved1; /* 0x08 */
+ U32 Reserved2; /* 0x0C */
+ U8 NumGroups; /* 0x10 */
+ U8 Reserved3; /* 0x11 */
+ U16 Reserved4; /* 0x12 */
+ MPI2_SAS_IO_UNIT6_PORT_WIDTH_MOD_GROUP_STATUS
+ PortWidthModulationGroupStatus[MPI2_SAS_IOUNIT6_GROUP_MAX]; /* 0x14 */
+} MPI2_CONFIG_PAGE_SASIOUNIT_6,
+ MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SASIOUNIT_6,
+ Mpi2SasIOUnitPage6_t, MPI2_POINTER pMpi2SasIOUnitPage6_t;
+
+#define MPI2_SASIOUNITPAGE6_PAGEVERSION (0x00)
+
+
+/* SAS IO Unit Page 7 */
+
+typedef struct _MPI2_SAS_IO_UNIT7_PORT_WIDTH_MOD_GROUP_SETTINGS
+{
+ U8 Flags; /* 0x00 */
+ U8 Reserved1; /* 0x01 */
+ U16 Reserved2; /* 0x02 */
+ U8 Threshold75Pct; /* 0x04 */
+ U8 Threshold50Pct; /* 0x05 */
+ U8 Threshold25Pct; /* 0x06 */
+ U8 Reserved3; /* 0x07 */
+} MPI2_SAS_IO_UNIT7_PORT_WIDTH_MOD_GROUP_SETTINGS,
+ MPI2_POINTER PTR_MPI2_SAS_IO_UNIT7_PORT_WIDTH_MOD_GROUP_SETTINGS,
+ Mpi2SasIOUnit7PortWidthModGroupSettings_t,
+ MPI2_POINTER pMpi2SasIOUnit7PortWidthModGroupSettings_t;
+
+/* defines for Flags field */
+#define MPI2_SASIOUNIT7_FLAGS_ENABLE_PORT_WIDTH_MODULATION (0x01)
+
+
+/*
+ * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ * one and check the value returned for NumGroups at runtime.
+ */
+#ifndef MPI2_SAS_IOUNIT7_GROUP_MAX
+#define MPI2_SAS_IOUNIT7_GROUP_MAX (1)
+#endif
+
+typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_7
+{
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
+ U8 SamplingInterval; /* 0x08 */
+ U8 WindowLength; /* 0x09 */
+ U16 Reserved1; /* 0x0A */
+ U32 Reserved2; /* 0x0C */
+ U32 Reserved3; /* 0x10 */
+ U8 NumGroups; /* 0x14 */
+ U8 Reserved4; /* 0x15 */
+ U16 Reserved5; /* 0x16 */
+ MPI2_SAS_IO_UNIT7_PORT_WIDTH_MOD_GROUP_SETTINGS
+ PortWidthModulationGroupSettings[MPI2_SAS_IOUNIT7_GROUP_MAX]; /* 0x18 */
+} MPI2_CONFIG_PAGE_SASIOUNIT_7,
+ MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SASIOUNIT_7,
+ Mpi2SasIOUnitPage7_t, MPI2_POINTER pMpi2SasIOUnitPage7_t;
+
+#define MPI2_SASIOUNITPAGE7_PAGEVERSION (0x00)
+
+
+/* SAS IO Unit Page 8 */
+
+typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_8
+{
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
+ U32 Reserved1; /* 0x08 */
+ U32 PowerManagementCapabilities; /* 0x0C */
+ U32 Reserved2; /* 0x10 */
+} MPI2_CONFIG_PAGE_SASIOUNIT_8,
+ MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SASIOUNIT_8,
+ Mpi2SasIOUnitPage8_t, MPI2_POINTER pMpi2SasIOUnitPage8_t;
+
+#define MPI2_SASIOUNITPAGE8_PAGEVERSION (0x00)
+
+/* defines for PowerManagementCapabilities field */
+#define MPI2_SASIOUNIT8_PM_HOST_PORT_WIDTH_MOD (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)
@@ -2187,7 +2408,7 @@ typedef struct _MPI2_SASPHY2_PHY_EVENT
/*
* Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check Header.ExtPageLength or NumPhyEvents at runtime.
+ * one and check the value returned for NumPhyEvents at runtime.
*/
#ifndef MPI2_SASPHY2_PHY_EVENT_MAX
#define MPI2_SASPHY2_PHY_EVENT_MAX (1)
@@ -2280,7 +2501,7 @@ typedef struct _MPI2_SASPHY3_PHY_EVENT_CONFIG
/*
* Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check Header.ExtPageLength or NumPhyEvents at runtime.
+ * one and check the value returned for NumPhyEvents at runtime.
*/
#ifndef MPI2_SASPHY3_PHY_EVENT_MAX
#define MPI2_SASPHY3_PHY_EVENT_MAX (1)
@@ -2392,7 +2613,7 @@ typedef struct _MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0
/*
* Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check Header.ExtPageLength or NumPhys at runtime.
+ * one and check the value returned for NumLogEntries at runtime.
*/
#ifndef MPI2_LOG_0_NUM_LOG_ENTRIES
#define MPI2_LOG_0_NUM_LOG_ENTRIES (1)
@@ -2442,7 +2663,7 @@ typedef struct _MPI2_CONFIG_PAGE_LOG_0
/*
* Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check Header.ExtPageLength or NumPhys at runtime.
+ * one and check the value returned for NumElements at runtime.
*/
#ifndef MPI2_RAIDCONFIG0_MAX_ELEMENTS
#define MPI2_RAIDCONFIG0_MAX_ELEMENTS (1)
@@ -2642,5 +2863,25 @@ typedef struct _MPI2_CONFIG_PAGE_ETHERNET_1
#define MPI2_ETHPG1_MS_DATA_RATE_1GBIT (0x03)
+/****************************************************************************
+* Extended Manufacturing Config Pages
+****************************************************************************/
+
+/*
+ * Generic structure to use for product-specific extended manufacturing pages
+ * (currently Extended Manufacturing Page 40 through Extended Manufacturing
+ * Page 60).
+ */
+
+typedef struct _MPI2_CONFIG_PAGE_EXT_MAN_PS
+{
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
+ U32 ProductSpecificInfo; /* 0x08 */
+} MPI2_CONFIG_PAGE_EXT_MAN_PS,
+ MPI2_POINTER PTR_MPI2_CONFIG_PAGE_EXT_MAN_PS,
+ Mpi2ExtManufacturingPagePS_t, MPI2_POINTER pMpi2ExtManufacturingPagePS_t;
+
+/* PageVersion should be provided by product-specific code */
+
#endif
diff --git a/sys/dev/mps/mpi/mpi2_hbd.h b/sys/dev/mps/mpi/mpi2_hbd.h
index d14e352..e31fc5e 100644
--- a/sys/dev/mps/mpi/mpi2_hbd.h
+++ b/sys/dev/mps/mpi/mpi2_hbd.h
@@ -1,13 +1,42 @@
-/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ *
+ * $FreeBSD$
+ */
+
/*
- * Copyright (c) 2009 LSI Corporation.
+ * Copyright (c) 2009-2011 LSI Corporation.
*
*
* Name: mpi2_hbd.h
* Title: MPI Host Based Discovery messages and structures
* Creation Date: October 21, 2009
*
- * mpi2_hbd.h Version: 02.00.00
+ * mpi2_hbd.h Version: 02.00.01
*
* Version History
* ---------------
@@ -15,6 +44,8 @@
* Date Version Description
* -------- -------- ------------------------------------------------------
* 10-28-09 02.00.00 Initial version.
+ * 08-11-10 02.00.01 Removed PortGroups, DmaGroup, and ControlGroup from
+ * HBD Action request, replaced by AdditionalInfo field.
* --------------------------------------------------------------------------
*/
@@ -48,10 +79,7 @@ typedef struct _MPI2_HBD_ACTION_REQUEST
U8 Port; /* 0x25 */
U8 MaxConnections; /* 0x26 */
U8 MaxRate; /* 0x27 */
- U8 PortGroups; /* 0x28 */
- U8 DmaGroup; /* 0x29 */
- U8 ControlGroup; /* 0x2A */
- U8 Reserved6; /* 0x2B */
+ U32 AdditionalInfo; /* 0x28 */
U16 InitialAWT; /* 0x2C */
U16 Reserved7; /* 0x2E */
U32 Reserved8; /* 0x30 */
diff --git a/sys/dev/mps/mpi/mpi2_history.txt b/sys/dev/mps/mpi/mpi2_history.txt
index d70df0d..01dc3b6e 100644
--- a/sys/dev/mps/mpi/mpi2_history.txt
+++ b/sys/dev/mps/mpi/mpi2_history.txt
@@ -1,29 +1,58 @@
-/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ *
+ * $FreeBSD$
+ */
+
==============================
Fusion-MPT MPI 2.0 Header File Change History
==============================
- Copyright (c) 2000-2009 LSI Corporation.
+ Copyright (c) 2000-2011 LSI Corporation.
---------------------------------------
- Header Set Release Version: 02.00.14
- Header Set Release Date: 10-28-09
+ Header Set Release Version: 02.00.18
+ Header Set Release Date: 11-10-10
---------------------------------------
Filename Current version Prior version
---------- --------------- -------------
- mpi2.h 02.00.14 02.00.13
- mpi2_cnfg.h 02.00.13 02.00.12
- mpi2_init.h 02.00.08 02.00.07
- mpi2_ioc.h 02.00.13 02.00.12
- mpi2_raid.h 02.00.04 02.00.04
- mpi2_sas.h 02.00.03 02.00.02
- mpi2_targ.h 02.00.03 02.00.03
- mpi2_tool.h 02.00.04 02.00.04
+ mpi2.h 02.00.18 02.00.17
+ mpi2_cnfg.h 02.00.17 02.00.16
+ mpi2_init.h 02.00.11 02.00.10
+ mpi2_ioc.h 02.00.16 02.00.15
+ mpi2_raid.h 02.00.05 02.00.05
+ mpi2_sas.h 02.00.05 02.00.05
+ mpi2_targ.h 02.00.04 02.00.04
+ mpi2_tool.h 02.00.06 02.00.06
mpi2_type.h 02.00.00 02.00.00
mpi2_ra.h 02.00.00 02.00.00
- mpi2_hbd.h 02.00.00
- mpi2_history.txt 02.00.14 02.00.13
+ mpi2_hbd.h 02.00.01 02.00.01
+ mpi2_history.txt 02.00.18 02.00.17
* Date Version Description
@@ -72,6 +101,15 @@ mpi2.h
* Added MSI-x index mask and shift for Reply Post Host
* Index register.
* Added function code for Host Based Discovery Action.
+ * 02-10-10 02.00.15 Bumped MPI2_HEADER_VERSION_UNIT.
+ * Added define for MPI2_FUNCTION_PWR_MGMT_CONTROL.
+ * Added defines for product-specific range of message
+ * function codes, 0xF0 to 0xFF.
+ * 05-12-10 02.00.16 Bumped MPI2_HEADER_VERSION_UNIT.
+ * Added alternative defines for the SGE Direction bit.
+ * 08-11-10 02.00.17 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 11-10-10 02.00.18 Bumped MPI2_HEADER_VERSION_UNIT.
+ * Added MPI2_IEEE_SGE_FLAGS_SYSTEMPLBCPI_ADDR define.
* --------------------------------------------------------------------------
mpi2_cnfg.h
@@ -171,6 +209,31 @@ mpi2_cnfg.h
* Added Ethernet configuration pages.
* 10-28-09 02.00.13 Added MPI2_IOUNITPAGE1_ENABLE_HOST_BASED_DISCOVERY.
* Added SAS PHY Page 4 structure and defines.
+ * 02-10-10 02.00.14 Modified the comments for the configuration page
+ * structures that contain an array of data. The host
+ * should use the "count" field in the page data (e.g. the
+ * NumPhys field) to determine the number of valid elements
+ * in the array.
+ * Added/modified some MPI2_MFGPAGE_DEVID_SAS defines.
+ * Added PowerManagementCapabilities to IO Unit Page 7.
+ * Added PortWidthModGroup field to
+ * MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS.
+ * Added MPI2_CONFIG_PAGE_SASIOUNIT_6 and related defines.
+ * Added MPI2_CONFIG_PAGE_SASIOUNIT_7 and related defines.
+ * Added MPI2_CONFIG_PAGE_SASIOUNIT_8 and related defines.
+ * 05-12-10 02.00.15 Added MPI2_RAIDVOL0_STATUS_FLAG_VOL_NOT_CONSISTENT
+ * define.
+ * Added MPI2_PHYSDISK0_INCOMPATIBLE_MEDIA_TYPE define.
+ * Added MPI2_SAS_NEG_LINK_RATE_UNSUPPORTED_PHY define.
+ * 08-11-10 02.00.16 Removed IO Unit Page 1 device path (multi-pathing)
+ * defines.
+ * 11-10-10 02.00.17 Added ReceptacleID field (replacing Reserved1) to
+ * MPI2_MANPAGE7_CONNECTOR_INFO and reworked defines for
+ * the Pinout field.
+ * Added BoardTemperature and BoardTemperatureUnits fields
+ * to MPI2_CONFIG_PAGE_IO_UNIT_7.
+ * Added MPI2_CONFIG_EXTPAGETYPE_EXT_MANUFACTURING define
+ * and MPI2_CONFIG_PAGE_EXT_MAN_PS structure.
* --------------------------------------------------------------------------
mpi2_init.h
@@ -192,6 +255,9 @@ mpi2_init.h
* both SCSI IO Error Reply and SCSI Task Management Reply.
* Added ResponseInfo field to MPI2_SCSI_TASK_MANAGE_REPLY.
* Added MPI2_SCSITASKMGMT_RSP_TM_OVERLAPPED_TAG define.
+ * 02-10-10 02.00.09 Removed unused structure that had "#if 0" around it.
+ * 05-12-10 02.00.10 Added optional vendor-unique region to SCSI IO Request.
+ * 11-10-10 02.00.11 Added MPI2_SCSIIO_NUM_SGLOFFSETS define.
* --------------------------------------------------------------------------
mpi2_ioc.h
@@ -280,6 +346,12 @@ mpi2_ioc.h
* (MPI2_FW_HEADER_PID_).
* Modified values for SAS ProductID Family
* (MPI2_FW_HEADER_PID_FAMILY_).
+ * 02-10-10 02.00.14 Added SAS Quiesce Event structure and defines.
+ * Added PowerManagementControl Request structures and
+ * defines.
+ * 05-12-10 02.00.15 Marked Task Set Full Event as obsolete.
+ * Added MPI2_EVENT_SAS_TOPO_LR_UNSUPPORTED_PHY define.
+ * 11-10-10 02.00.16 Added MPI2_FW_DOWNLOAD_ITYPE_MIN_PRODUCT_SPECIFIC.
* --------------------------------------------------------------------------
mpi2_raid.h
@@ -292,6 +364,7 @@ mpi2_raid.h
* can be sized by the build environment.
* 07-30-09 02.00.04 Added proper define for the Use Default Settings bit of
* VolumeCreationFlags and marked the old one as obsolete.
+ * 05-12-10 02.00.05 Added MPI2_RAID_VOL_FLAGS_OP_MDC define.
* --------------------------------------------------------------------------
mpi2_sas.h
@@ -302,6 +375,8 @@ mpi2_sas.h
* Request.
* 10-28-09 02.00.03 Changed the type of SGL in MPI2_SATA_PASSTHROUGH_REQUEST
* to MPI2_SGE_IO_UNION since it supports chained SGLs.
+ * 05-12-10 02.00.04 Modified some comments.
+ * 08-11-10 02.00.05 Added NCQ operations to SAS IO Unit Control.
* --------------------------------------------------------------------------
mpi2_targ.h
@@ -313,6 +388,7 @@ mpi2_targ.h
* MPI2_TARGET_CMD_BUF_POST_BASE_REQUEST.
* Target Status Send Request only takes a single SGE for
* response data.
+ * 02-10-10 02.00.04 Added comment to MPI2_TARGET_SSP_RSP_IU structure.
* --------------------------------------------------------------------------
mpi2_tool.h
@@ -325,6 +401,9 @@ mpi2_tool.h
* and reply messages.
* Added MPI2_DIAG_BUF_TYPE_EXTENDED.
* Incremented MPI2_DIAG_BUF_TYPE_COUNT.
+ * 05-12-10 02.00.05 Added Diagnostic Data Upload tool.
+ * 08-11-10 02.00.06 Added defines that were missing for Diagnostic Buffer
+ * Post Request.
* --------------------------------------------------------------------------
mpi2_type.h
@@ -337,24 +416,40 @@ mpi2_ra.h
mpi2_hbd.h
* 10-28-09 02.00.00 Initial version.
+ * 08-11-10 02.00.01 Removed PortGroups, DmaGroup, and ControlGroup from
+ * HBD Action request, replaced by AdditionalInfo field.
* --------------------------------------------------------------------------
mpi2_history.txt Parts list history
-Filename 02.00.14 02.00.13 02.00.12
----------- -------- -------- --------
-mpi2.h 02.00.14 02.00.13 02.00.12
-mpi2_cnfg.h 02.00.13 02.00.12 02.00.11
-mpi2_init.h 02.00.08 02.00.07 02.00.07
-mpi2_ioc.h 02.00.13 02.00.12 02.00.11
-mpi2_raid.h 02.00.04 02.00.04 02.00.03
-mpi2_sas.h 02.00.03 02.00.02 02.00.02
-mpi2_targ.h 02.00.03 02.00.03 02.00.03
-mpi2_tool.h 02.00.04 02.00.04 02.00.03
-mpi2_type.h 02.00.00 02.00.00 02.00.00
-mpi2_ra.h 02.00.00 02.00.00 02.00.00
-mpi2_hbd.h 02.00.00
+Filename 02.00.18
+---------- --------
+mpi2.h 02.00.18
+mpi2_cnfg.h 02.00.17
+mpi2_init.h 02.00.11
+mpi2_ioc.h 02.00.16
+mpi2_raid.h 02.00.05
+mpi2_sas.h 02.00.05
+mpi2_targ.h 02.00.04
+mpi2_tool.h 02.00.06
+mpi2_type.h 02.00.00
+mpi2_ra.h 02.00.00
+mpi2_hbd.h 02.00.01
+
+Filename 02.00.17 02.00.16 02.00.15 02.00.14 02.00.13 02.00.12
+---------- -------- -------- -------- -------- -------- --------
+mpi2.h 02.00.17 02.00.16 02.00.15 02.00.14 02.00.13 02.00.12
+mpi2_cnfg.h 02.00.16 02.00.15 02.00.14 02.00.13 02.00.12 02.00.11
+mpi2_init.h 02.00.10 02.00.10 02.00.09 02.00.08 02.00.07 02.00.07
+mpi2_ioc.h 02.00.15 02.00.15 02.00.14 02.00.13 02.00.12 02.00.11
+mpi2_raid.h 02.00.05 02.00.05 02.00.04 02.00.04 02.00.04 02.00.03
+mpi2_sas.h 02.00.05 02.00.04 02.00.03 02.00.03 02.00.02 02.00.02
+mpi2_targ.h 02.00.04 02.00.04 02.00.04 02.00.03 02.00.03 02.00.03
+mpi2_tool.h 02.00.06 02.00.05 02.00.04 02.00.04 02.00.04 02.00.03
+mpi2_type.h 02.00.00 02.00.00 02.00.00 02.00.00 02.00.00 02.00.00
+mpi2_ra.h 02.00.00 02.00.00 02.00.00 02.00.00 02.00.00 02.00.00
+mpi2_hbd.h 02.00.01 02.00.00 02.00.00 02.00.00
Filename 02.00.11 02.00.10 02.00.09 02.00.08 02.00.07 02.00.06
---------- -------- -------- -------- -------- -------- --------
diff --git a/sys/dev/mps/mpi/mpi2_init.h b/sys/dev/mps/mpi/mpi2_init.h
index 8d2b1f9..ca4a685 100644
--- a/sys/dev/mps/mpi/mpi2_init.h
+++ b/sys/dev/mps/mpi/mpi2_init.h
@@ -1,13 +1,42 @@
-/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ *
+ * $FreeBSD$
+ */
+
/*
- * Copyright (c) 2000-2009 LSI Corporation.
+ * Copyright (c) 2000-2011 LSI Corporation.
*
*
* Name: mpi2_init.h
* Title: MPI SCSI initiator mode messages and structures
* Creation Date: June 23, 2006
*
- * mpi2_init.h Version: 02.00.08
+ * mpi2_init.h Version: 02.00.11
*
* Version History
* ---------------
@@ -32,6 +61,9 @@
* both SCSI IO Error Reply and SCSI Task Management Reply.
* Added ResponseInfo field to MPI2_SCSI_TASK_MANAGE_REPLY.
* Added MPI2_SCSITASKMGMT_RSP_TM_OVERLAPPED_TAG define.
+ * 02-10-10 02.00.09 Removed unused structure that had "#if 0" around it.
+ * 05-12-10 02.00.10 Added optional vendor-unique region to SCSI IO Request.
+ * 11-10-10 02.00.11 Added MPI2_SCSIIO_NUM_SGLOFFSETS define.
* --------------------------------------------------------------------------
*/
@@ -58,20 +90,6 @@ typedef struct
} MPI2_SCSI_IO_CDB_EEDP32, MPI2_POINTER PTR_MPI2_SCSI_IO_CDB_EEDP32,
Mpi2ScsiIoCdbEedp32_t, MPI2_POINTER pMpi2ScsiIoCdbEedp32_t;
-/* TBD: I don't think this is needed for MPI2/Gen2 */
-#if 0
-typedef struct
-{
- U8 CDB[16]; /* 0x00 */
- U32 DataLength; /* 0x10 */
- U32 PrimaryReferenceTag; /* 0x14 */
- U16 PrimaryApplicationTag; /* 0x18 */
- U16 PrimaryApplicationTagMask; /* 0x1A */
- U32 TransferLength; /* 0x1C */
-} MPI2_SCSI_IO32_CDB_EEDP16, MPI2_POINTER PTR_MPI2_SCSI_IO32_CDB_EEDP16,
- Mpi2ScsiIo32CdbEedp16_t, MPI2_POINTER pMpi2ScsiIo32CdbEedp16_t;
-#endif
-
typedef union
{
U8 CDB32[32];
@@ -112,7 +130,13 @@ typedef struct _MPI2_SCSI_IO_REQUEST
U8 LUN[8]; /* 0x34 */
U32 Control; /* 0x3C */
MPI2_SCSI_IO_CDB_UNION CDB; /* 0x40 */
+
+#ifdef MPI2_SCSI_IO_VENDOR_UNIQUE_REGION /* typically this is left undefined */
+ MPI2_SCSI_IO_VENDOR_UNIQUE VendorRegion;
+#endif
+
MPI2_SGE_IO_UNION SGL; /* 0x60 */
+
} MPI2_SCSI_IO_REQUEST, MPI2_POINTER PTR_MPI2_SCSI_IO_REQUEST,
Mpi2SCSIIORequest_t, MPI2_POINTER pMpi2SCSIIORequest_t;
@@ -146,6 +170,9 @@ typedef struct _MPI2_SCSI_IO_REQUEST
#define MPI2_SCSIIO_SGLFLAGS_SGL1_SHIFT (4)
#define MPI2_SCSIIO_SGLFLAGS_SGL0_SHIFT (0)
+/* number of SGLOffset fields */
+#define MPI2_SCSIIO_NUM_SGLOFFSETS (4)
+
/* SCSI IO IoFlags bits */
/* Large CDB Address Space */
diff --git a/sys/dev/mps/mpi/mpi2_ioc.h b/sys/dev/mps/mpi/mpi2_ioc.h
index 24a5662..ca19a5c 100644
--- a/sys/dev/mps/mpi/mpi2_ioc.h
+++ b/sys/dev/mps/mpi/mpi2_ioc.h
@@ -1,13 +1,42 @@
-/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ *
+ * $FreeBSD$
+ */
+
/*
- * Copyright (c) 2000-2009 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.13
+ * mpi2_ioc.h Version: 02.00.16
*
* Version History
* ---------------
@@ -99,6 +128,12 @@
* (MPI2_FW_HEADER_PID_).
* Modified values for SAS ProductID Family
* (MPI2_FW_HEADER_PID_FAMILY_).
+ * 02-10-10 02.00.14 Added SAS Quiesce Event structure and defines.
+ * Added PowerManagementControl Request structures and
+ * defines.
+ * 05-12-10 02.00.15 Marked Task Set Full Event as obsolete.
+ * Added MPI2_EVENT_SAS_TOPO_LR_UNSUPPORTED_PHY define.
+ * 11-10-10 02.00.16 Added MPI2_FW_DOWNLOAD_ITYPE_MIN_PRODUCT_SPECIFIC.
* --------------------------------------------------------------------------
*/
@@ -454,7 +489,7 @@ typedef struct _MPI2_EVENT_NOTIFICATION_REPLY
#define MPI2_EVENT_STATE_CHANGE (0x0002)
#define MPI2_EVENT_HARD_RESET_RECEIVED (0x0005)
#define MPI2_EVENT_EVENT_CHANGE (0x000A)
-#define MPI2_EVENT_TASK_SET_FULL (0x000E)
+#define MPI2_EVENT_TASK_SET_FULL (0x000E) /* obsolete */
#define MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE (0x000F)
#define MPI2_EVENT_IR_OPERATION_STATUS (0x0014)
#define MPI2_EVENT_SAS_DISCOVERY (0x0016)
@@ -470,6 +505,7 @@ typedef struct _MPI2_EVENT_NOTIFICATION_REPLY
#define MPI2_EVENT_SAS_PHY_COUNTER (0x0022)
#define MPI2_EVENT_GPIO_INTERRUPT (0x0023)
#define MPI2_EVENT_HOST_BASED_DISCOVERY_PHY (0x0024)
+#define MPI2_EVENT_SAS_QUIESCE (0x0025)
/* Log Entry Added Event data */
@@ -515,6 +551,7 @@ typedef struct _MPI2_EVENT_DATA_HARD_RESET_RECEIVED
MPI2_POINTER pMpi2EventDataHardResetReceived_t;
/* Task Set Full Event data */
+/* this event is obsolete */
typedef struct _MPI2_EVENT_DATA_TASK_SET_FULL
{
@@ -829,6 +866,7 @@ typedef struct _MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST
#define MPI2_EVENT_SAS_TOPO_LR_SATA_OOB_COMPLETE (0x03)
#define MPI2_EVENT_SAS_TOPO_LR_PORT_SELECTOR (0x04)
#define MPI2_EVENT_SAS_TOPO_LR_SMP_RESET_IN_PROGRESS (0x05)
+#define MPI2_EVENT_SAS_TOPO_LR_UNSUPPORTED_PHY (0x06)
#define MPI2_EVENT_SAS_TOPO_LR_RATE_1_5 (0x08)
#define MPI2_EVENT_SAS_TOPO_LR_RATE_3_0 (0x09)
#define MPI2_EVENT_SAS_TOPO_LR_RATE_6_0 (0x0A)
@@ -896,6 +934,23 @@ typedef struct _MPI2_EVENT_DATA_SAS_PHY_COUNTER
/* use MPI2_SASPHY3_TFLAGS_ values from mpi2_cnfg.h for the ThresholdFlags field */
+/* SAS Quiesce Event data */
+
+typedef struct _MPI2_EVENT_DATA_SAS_QUIESCE
+{
+ U8 ReasonCode; /* 0x00 */
+ U8 Reserved1; /* 0x01 */
+ U16 Reserved2; /* 0x02 */
+ U32 Reserved3; /* 0x04 */
+} MPI2_EVENT_DATA_SAS_QUIESCE,
+ MPI2_POINTER PTR_MPI2_EVENT_DATA_SAS_QUIESCE,
+ Mpi2EventDataSasQuiesce_t, MPI2_POINTER pMpi2EventDataSasQuiesce_t;
+
+/* SAS Quiesce Event data ReasonCode values */
+#define MPI2_EVENT_SAS_QUIESCE_RC_STARTED (0x01)
+#define MPI2_EVENT_SAS_QUIESCE_RC_COMPLETED (0x02)
+
+
/* Host Based Discovery Phy Event data */
typedef struct _MPI2_EVENT_HBD_PHY_SAS
@@ -1009,7 +1064,9 @@ typedef struct _MPI2_FW_DOWNLOAD_REQUEST
#define MPI2_FW_DOWNLOAD_ITYPE_CONFIG_1 (0x07)
#define MPI2_FW_DOWNLOAD_ITYPE_CONFIG_2 (0x08)
#define MPI2_FW_DOWNLOAD_ITYPE_MEGARAID (0x09)
+#define MPI2_FW_DOWNLOAD_ITYPE_COMPLETE (0x0A)
#define MPI2_FW_DOWNLOAD_ITYPE_COMMON_BOOT_BLOCK (0x0B)
+#define MPI2_FW_DOWNLOAD_ITYPE_MIN_PRODUCT_SPECIFIC (0xF0)
/* FWDownload TransactionContext Element */
typedef struct _MPI2_FW_DOWNLOAD_TCSGE
@@ -1186,7 +1243,6 @@ typedef struct _MPI2_FW_IMAGE_HEADER
#define MPI2_FW_HEADER_PID_PROD_MASK (0x0F00)
#define MPI2_FW_HEADER_PID_PROD_A (0x0000)
-#define MPI2_FW_HEADER_PID_PROD_MASK (0x0F00)
#define MPI2_FW_HEADER_PID_PROD_TARGET_INITIATOR_SCSI (0x0200)
#define MPI2_FW_HEADER_PID_PROD_IR_SCSI (0x0700)
@@ -1410,5 +1466,101 @@ typedef struct _MPI2_INIT_IMAGE_FOOTER
#define MPI2_INIT_IMAGE_RESETVECTOR_OFFSET (0x14)
+/****************************************************************************
+* PowerManagementControl message
+****************************************************************************/
+
+/* PowerManagementControl Request message */
+typedef struct _MPI2_PWR_MGMT_CONTROL_REQUEST
+{
+ U8 Feature; /* 0x00 */
+ U8 Reserved1; /* 0x01 */
+ U8 ChainOffset; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 Reserved2; /* 0x04 */
+ U8 Reserved3; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved4; /* 0x0A */
+ U8 Parameter1; /* 0x0C */
+ U8 Parameter2; /* 0x0D */
+ U8 Parameter3; /* 0x0E */
+ U8 Parameter4; /* 0x0F */
+ U32 Reserved5; /* 0x10 */
+ U32 Reserved6; /* 0x14 */
+} MPI2_PWR_MGMT_CONTROL_REQUEST, MPI2_POINTER PTR_MPI2_PWR_MGMT_CONTROL_REQUEST,
+ Mpi2PwrMgmtControlRequest_t, MPI2_POINTER pMpi2PwrMgmtControlRequest_t;
+
+/* defines for the Feature field */
+#define MPI2_PM_CONTROL_FEATURE_DA_PHY_POWER_COND (0x01)
+#define MPI2_PM_CONTROL_FEATURE_PORT_WIDTH_MODULATION (0x02)
+#define MPI2_PM_CONTROL_FEATURE_PCIE_LINK (0x03)
+#define MPI2_PM_CONTROL_FEATURE_IOC_SPEED (0x04)
+#define MPI2_PM_CONTROL_FEATURE_MIN_PRODUCT_SPECIFIC (0x80)
+#define MPI2_PM_CONTROL_FEATURE_MAX_PRODUCT_SPECIFIC (0xFF)
+
+/* parameter usage for the MPI2_PM_CONTROL_FEATURE_DA_PHY_POWER_COND Feature */
+/* Parameter1 contains a PHY number */
+/* Parameter2 indicates power condition action using these defines */
+#define MPI2_PM_CONTROL_PARAM2_PARTIAL (0x01)
+#define MPI2_PM_CONTROL_PARAM2_SLUMBER (0x02)
+#define MPI2_PM_CONTROL_PARAM2_EXIT_PWR_MGMT (0x03)
+/* Parameter3 and Parameter4 are reserved */
+
+/* parameter usage for the MPI2_PM_CONTROL_FEATURE_PORT_WIDTH_MODULATION Feature */
+/* Parameter1 contains SAS port width modulation group number */
+/* Parameter2 indicates IOC action using these defines */
+#define MPI2_PM_CONTROL_PARAM2_REQUEST_OWNERSHIP (0x01)
+#define MPI2_PM_CONTROL_PARAM2_CHANGE_MODULATION (0x02)
+#define MPI2_PM_CONTROL_PARAM2_RELINQUISH_OWNERSHIP (0x03)
+/* Parameter3 indicates desired modulation level using these defines */
+#define MPI2_PM_CONTROL_PARAM3_25_PERCENT (0x00)
+#define MPI2_PM_CONTROL_PARAM3_50_PERCENT (0x01)
+#define MPI2_PM_CONTROL_PARAM3_75_PERCENT (0x02)
+#define MPI2_PM_CONTROL_PARAM3_100_PERCENT (0x03)
+/* Parameter4 is reserved */
+
+/* parameter usage for the MPI2_PM_CONTROL_FEATURE_PCIE_LINK Feature */
+/* Parameter1 indicates desired PCIe link speed using these defines */
+#define MPI2_PM_CONTROL_PARAM1_PCIE_2_5_GBPS (0x00)
+#define MPI2_PM_CONTROL_PARAM1_PCIE_5_0_GBPS (0x01)
+#define MPI2_PM_CONTROL_PARAM1_PCIE_8_0_GBPS (0x02)
+/* Parameter2 indicates desired PCIe link width using these defines */
+#define MPI2_PM_CONTROL_PARAM2_WIDTH_X1 (0x01)
+#define MPI2_PM_CONTROL_PARAM2_WIDTH_X2 (0x02)
+#define MPI2_PM_CONTROL_PARAM2_WIDTH_X4 (0x04)
+#define MPI2_PM_CONTROL_PARAM2_WIDTH_X8 (0x08)
+/* Parameter3 and Parameter4 are reserved */
+
+/* parameter usage for the MPI2_PM_CONTROL_FEATURE_IOC_SPEED Feature */
+/* Parameter1 indicates desired IOC hardware clock speed using these defines */
+#define MPI2_PM_CONTROL_PARAM1_FULL_IOC_SPEED (0x01)
+#define MPI2_PM_CONTROL_PARAM1_HALF_IOC_SPEED (0x02)
+#define MPI2_PM_CONTROL_PARAM1_QUARTER_IOC_SPEED (0x04)
+#define MPI2_PM_CONTROL_PARAM1_EIGHTH_IOC_SPEED (0x08)
+/* Parameter2, Parameter3, and Parameter4 are reserved */
+
+
+/* PowerManagementControl Reply message */
+typedef struct _MPI2_PWR_MGMT_CONTROL_REPLY
+{
+ U8 Feature; /* 0x00 */
+ U8 Reserved1; /* 0x01 */
+ U8 MsgLength; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 Reserved2; /* 0x04 */
+ U8 Reserved3; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved4; /* 0x0A */
+ U16 Reserved5; /* 0x0C */
+ U16 IOCStatus; /* 0x0E */
+ U32 IOCLogInfo; /* 0x10 */
+} MPI2_PWR_MGMT_CONTROL_REPLY, MPI2_POINTER PTR_MPI2_PWR_MGMT_CONTROL_REPLY,
+ Mpi2PwrMgmtControlReply_t, MPI2_POINTER pMpi2PwrMgmtControlReply_t;
+
+
#endif
diff --git a/sys/dev/mps/mpi/mpi2_ra.h b/sys/dev/mps/mpi/mpi2_ra.h
index 18b0b3d..0f01226 100644
--- a/sys/dev/mps/mpi/mpi2_ra.h
+++ b/sys/dev/mps/mpi/mpi2_ra.h
@@ -1,6 +1,35 @@
-/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ *
+ * $FreeBSD$
+ */
+
/*
- * Copyright (c) 2009 LSI Corporation.
+ * Copyright (c) 2011 LSI Corporation.
*
*
* Name: mpi2_ra.h
diff --git a/sys/dev/mps/mpi/mpi2_raid.h b/sys/dev/mps/mpi/mpi2_raid.h
index f653028..557468a 100644
--- a/sys/dev/mps/mpi/mpi2_raid.h
+++ b/sys/dev/mps/mpi/mpi2_raid.h
@@ -1,13 +1,42 @@
-/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ *
+ * $FreeBSD$
+ */
+
/*
- * Copyright (c) 2000-2008 LSI Corporation.
+ * Copyright (c) 2000-2011 LSI Corporation.
*
*
* Name: mpi2_raid.h
* Title: MPI Integrated RAID messages and structures
* Creation Date: April 26, 2007
*
- * mpi2_raid.h Version: 02.00.04
+ * mpi2_raid.h Version: 02.00.05
*
* Version History
* ---------------
@@ -23,6 +52,7 @@
* can be sized by the build environment.
* 07-30-09 02.00.04 Added proper define for the Use Default Settings bit of
* VolumeCreationFlags and marked the old one as obsolete.
+ * 05-12-10 02.00.05 Added MPI2_RAID_VOL_FLAGS_OP_MDC define.
* --------------------------------------------------------------------------
*/
@@ -261,6 +291,7 @@ typedef struct _MPI2_RAID_VOL_INDICATOR
#define MPI2_RAID_VOL_FLAGS_OP_ONLINE_CAP_EXPANSION (0x00000001)
#define MPI2_RAID_VOL_FLAGS_OP_CONSISTENCY_CHECK (0x00000002)
#define MPI2_RAID_VOL_FLAGS_OP_RESYNC (0x00000003)
+#define MPI2_RAID_VOL_FLAGS_OP_MDC (0x00000004)
/* RAID Action Reply ActionData union */
diff --git a/sys/dev/mps/mpi/mpi2_sas.h b/sys/dev/mps/mpi/mpi2_sas.h
index ef64a730..1f3341f 100644
--- a/sys/dev/mps/mpi/mpi2_sas.h
+++ b/sys/dev/mps/mpi/mpi2_sas.h
@@ -1,13 +1,42 @@
-/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ *
+ * $FreeBSD$
+ */
+
/*
- * Copyright (c) 2000-2007 LSI Corporation.
+ * Copyright (c) 2000-2011 LSI Corporation.
*
*
* Name: mpi2_sas.h
* Title: MPI Serial Attached SCSI structures and definitions
* Creation Date: February 9, 2007
*
- * mpi2.h Version: 02.00.03
+ * mpi2_sas.h Version: 02.00.05
*
* Version History
* ---------------
@@ -21,6 +50,8 @@
* Request.
* 10-28-09 02.00.03 Changed the type of SGL in MPI2_SATA_PASSTHROUGH_REQUEST
* to MPI2_SGE_IO_UNION since it supports chained SGLs.
+ * 05-12-10 02.00.04 Modified some comments.
+ * 08-11-10 02.00.05 Added NCQ operations to SAS IO Unit Control.
* --------------------------------------------------------------------------
*/
@@ -111,7 +142,7 @@ typedef struct _MPI2_SMP_PASSTHROUGH_REQUEST
/* values for PassthroughFlags field */
#define MPI2_SMP_PT_REQ_PT_FLAGS_IMMEDIATE (0x80)
-/* values for SGLFlags field are in the SGL section of mpi2.h */
+/* use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */
/* SMP Passthrough Reply Message */
@@ -163,7 +194,7 @@ typedef struct _MPI2_SATA_PASSTHROUGH_REQUEST
U32 Reserved4; /* 0x14 */
U32 DataLength; /* 0x18 */
U8 CommandFIS[20]; /* 0x1C */
- MPI2_SGE_IO_UNION SGL; /* 0x20 */
+ MPI2_SGE_IO_UNION SGL; /* 0x30 */
} MPI2_SATA_PASSTHROUGH_REQUEST, MPI2_POINTER PTR_MPI2_SATA_PASSTHROUGH_REQUEST,
Mpi2SataPassthroughRequest_t, MPI2_POINTER pMpi2SataPassthroughRequest_t;
@@ -175,7 +206,7 @@ typedef struct _MPI2_SATA_PASSTHROUGH_REQUEST
#define MPI2_SATA_PT_REQ_PT_FLAGS_WRITE (0x0002)
#define MPI2_SATA_PT_REQ_PT_FLAGS_READ (0x0001)
-/* values for SGLFlags field are in the SGL section of mpi2.h */
+/* use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */
/* SATA Passthrough Reply Message */
@@ -246,6 +277,8 @@ typedef struct _MPI2_SAS_IOUNIT_CONTROL_REQUEST
#define MPI2_SAS_OP_REMOVE_DEVICE (0x0D)
#define MPI2_SAS_OP_LOOKUP_MAPPING (0x0E)
#define MPI2_SAS_OP_SET_IOC_PARAMETER (0x0F)
+#define MPI2_SAS_OP_DEV_ENABLE_NCQ (0x14)
+#define MPI2_SAS_OP_DEV_DISABLE_NCQ (0x15)
#define MPI2_SAS_OP_PRODUCT_SPECIFIC_MIN (0x80)
/* values for the PrimFlags field */
diff --git a/sys/dev/mps/mpi/mpi2_targ.h b/sys/dev/mps/mpi/mpi2_targ.h
index 50f38d0..fcd694c 100644
--- a/sys/dev/mps/mpi/mpi2_targ.h
+++ b/sys/dev/mps/mpi/mpi2_targ.h
@@ -1,13 +1,42 @@
-/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ *
+ * $FreeBSD$
+ */
+
/*
- * Copyright (c) 2000-2008 LSI Corporation.
+ * Copyright (c) 2000-2011 LSI Corporation.
*
*
* Name: mpi2_targ.h
* Title: MPI Target mode messages and structures
* Creation Date: September 8, 2006
*
- * mpi2_targ.h Version: 02.00.03
+ * mpi2_targ.h Version: 02.00.04
*
* Version History
* ---------------
@@ -22,6 +51,7 @@
* MPI2_TARGET_CMD_BUF_POST_BASE_REQUEST.
* Target Status Send Request only takes a single SGE for
* response data.
+ * 02-10-10 02.00.04 Added comment to MPI2_TARGET_SSP_RSP_IU structure.
* --------------------------------------------------------------------------
*/
@@ -343,6 +373,7 @@ typedef struct _MPI2_TARGET_STATUS_SEND_REQUEST
typedef struct _MPI2_TARGET_SSP_RSP_IU
{
U32 Reserved0[6]; /* reserved for SSP header */ /* 0x00 */
+
/* start of RESPONSE information unit */
U32 Reserved1; /* 0x18 */
U32 Reserved2; /* 0x1C */
@@ -352,6 +383,8 @@ typedef struct _MPI2_TARGET_SSP_RSP_IU
U32 Reserved4; /* 0x24 */
U32 SenseDataLength; /* 0x28 */
U32 ResponseDataLength; /* 0x2C */
+
+ /* start of Response or Sense Data (size may vary dynamically) */
U8 ResponseSenseData[4]; /* 0x30 */
} MPI2_TARGET_SSP_RSP_IU, MPI2_POINTER PTR_MPI2_TARGET_SSP_RSP_IU,
Mpi2TargetSspRspIu_t, MPI2_POINTER pMpi2TargetSspRspIu_t;
diff --git a/sys/dev/mps/mpi/mpi2_tool.h b/sys/dev/mps/mpi/mpi2_tool.h
index f782507..16c0ffc 100644
--- a/sys/dev/mps/mpi/mpi2_tool.h
+++ b/sys/dev/mps/mpi/mpi2_tool.h
@@ -1,13 +1,42 @@
-/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ *
+ * $FreeBSD$
+ */
+
/*
- * Copyright (c) 2000-2009 LSI Corporation.
+ * Copyright (c) 2000-2011 LSI Corporation.
*
*
* Name: mpi2_tool.h
* Title: MPI diagnostic tool structures and definitions
* Creation Date: March 26, 2007
*
- * mpi2_tool.h Version: 02.00.04
+ * mpi2_tool.h Version: 02.00.06
*
* Version History
* ---------------
@@ -23,6 +52,9 @@
* and reply messages.
* Added MPI2_DIAG_BUF_TYPE_EXTENDED.
* Incremented MPI2_DIAG_BUF_TYPE_COUNT.
+ * 05-12-10 02.00.05 Added Diagnostic Data Upload tool.
+ * 08-11-10 02.00.06 Added defines that were missing for Diagnostic Buffer
+ * Post Request.
* --------------------------------------------------------------------------
*/
@@ -38,6 +70,7 @@
/* defines for the Tools */
#define MPI2_TOOLBOX_CLEAN_TOOL (0x00)
#define MPI2_TOOLBOX_MEMORY_MOVE_TOOL (0x01)
+#define MPI2_TOOLBOX_DIAG_DATA_UPLOAD_TOOL (0x02)
#define MPI2_TOOLBOX_ISTWI_READ_WRITE_TOOL (0x03)
#define MPI2_TOOLBOX_BEACON_TOOL (0x05)
#define MPI2_TOOLBOX_DIAGNOSTIC_CLI_TOOL (0x06)
@@ -121,6 +154,46 @@ typedef struct _MPI2_TOOLBOX_MEM_MOVE_REQUEST
/****************************************************************************
+* Toolbox Diagnostic Data Upload request
+****************************************************************************/
+
+typedef struct _MPI2_TOOLBOX_DIAG_DATA_UPLOAD_REQUEST
+{
+ U8 Tool; /* 0x00 */
+ U8 Reserved1; /* 0x01 */
+ U8 ChainOffset; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 Reserved2; /* 0x04 */
+ U8 Reserved3; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved4; /* 0x0A */
+ U8 SGLFlags; /* 0x0C */
+ U8 Reserved5; /* 0x0D */
+ U16 Reserved6; /* 0x0E */
+ U32 Flags; /* 0x10 */
+ U32 DataLength; /* 0x14 */
+ MPI2_SGE_SIMPLE_UNION SGL; /* 0x18 */
+} MPI2_TOOLBOX_DIAG_DATA_UPLOAD_REQUEST,
+ MPI2_POINTER PTR_MPI2_TOOLBOX_DIAG_DATA_UPLOAD_REQUEST,
+ Mpi2ToolboxDiagDataUploadRequest_t,
+ MPI2_POINTER pMpi2ToolboxDiagDataUploadRequest_t;
+
+/* use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */
+
+
+typedef struct _MPI2_DIAG_DATA_UPLOAD_HEADER
+{
+ U32 DiagDataLength; /* 00h */
+ U8 FormatCode; /* 04h */
+ U8 Reserved1; /* 05h */
+ U16 Reserved2; /* 06h */
+} MPI2_DIAG_DATA_UPLOAD_HEADER, MPI2_POINTER PTR_MPI2_DIAG_DATA_UPLOAD_HEADER,
+ Mpi2DiagDataUploadHeader_t, MPI2_POINTER pMpi2DiagDataUploadHeader_t;
+
+
+/****************************************************************************
* Toolbox ISTWI Read Write Tool
****************************************************************************/
@@ -164,7 +237,7 @@ typedef struct _MPI2_TOOLBOX_ISTWI_READ_WRITE_REQUEST
#define MPI2_TOOL_ISTWI_ACTION_RELEASE_BUS (0x11)
#define MPI2_TOOL_ISTWI_ACTION_RESET (0x12)
-/* values for SGLFlags field are in the SGL section of mpi2.h */
+/* use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */
/* Toolbox ISTWI Read Write Tool reply message */
@@ -251,7 +324,7 @@ typedef struct _MPI2_TOOLBOX_DIAGNOSTIC_CLI_REQUEST
Mpi2ToolboxDiagnosticCliRequest_t,
MPI2_POINTER pMpi2ToolboxDiagnosticCliRequest_t;
-/* values for SGLFlags field are in the SGL section of mpi2.h */
+/* use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */
/* Toolbox Diagnostic CLI Tool reply message */
@@ -319,6 +392,10 @@ typedef struct _MPI2_DIAG_BUFFER_POST_REQUEST
/* count of the number of buffer types */
#define MPI2_DIAG_BUF_TYPE_COUNT (0x03)
+/* values for the Flags field */
+#define MPI2_DIAG_BUF_FLAG_RELEASE_ON_FULL (0x00000002)
+#define MPI2_DIAG_BUF_FLAG_IMMEDIATE_RELEASE (0x00000001)
+
/****************************************************************************
* Diagnostic Buffer Post reply
diff --git a/sys/dev/mps/mpi/mpi2_type.h b/sys/dev/mps/mpi/mpi2_type.h
index 9effe68..fa4ecd9 100644
--- a/sys/dev/mps/mpi/mpi2_type.h
+++ b/sys/dev/mps/mpi/mpi2_type.h
@@ -1,6 +1,35 @@
-/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ *
+ * $FreeBSD$
+ */
+
/*
- * Copyright (c) 2000-2007 LSI Corporation.
+ * Copyright (c) 2000-2011 LSI Corporation.
*
*
* Name: mpi2_type.h
diff --git a/sys/dev/mps/mps.c b/sys/dev/mps/mps.c
index ed70c43..d3d068b 100644
--- a/sys/dev/mps/mps.c
+++ b/sys/dev/mps/mps.c
@@ -22,6 +22,36 @@
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
+ *
+ */
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ *
+ * $FreeBSD$
*/
#include <sys/cdefs.h>
@@ -29,6 +59,7 @@ __FBSDID("$FreeBSD$");
/* Communications core for LSI MPT2 */
+/* TODO Move headers to mpsvar */
#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
@@ -43,29 +74,44 @@ __FBSDID("$FreeBSD$");
#include <sys/malloc.h>
#include <sys/uio.h>
#include <sys/sysctl.h>
+#include <sys/queue.h>
+#include <sys/kthread.h>
#include <sys/endian.h>
+#include <sys/eventhandler.h>
#include <machine/bus.h>
#include <machine/resource.h>
#include <sys/rman.h>
+#include <dev/pci/pcivar.h>
+
#include <cam/scsi/scsi_all.h>
#include <dev/mps/mpi/mpi2_type.h>
#include <dev/mps/mpi/mpi2.h>
#include <dev/mps/mpi/mpi2_ioc.h>
+#include <dev/mps/mpi/mpi2_sas.h>
#include <dev/mps/mpi/mpi2_cnfg.h>
+#include <dev/mps/mpi/mpi2_init.h>
+#include <dev/mps/mpi/mpi2_tool.h>
+#include <dev/mps/mps_ioctl.h>
#include <dev/mps/mpsvar.h>
#include <dev/mps/mps_table.h>
+static int mps_diag_reset(struct mps_softc *sc);
+static int mps_init_queues(struct mps_softc *sc);
+static int mps_message_unit_reset(struct mps_softc *sc);
+static int mps_transition_operational(struct mps_softc *sc);
static void mps_startup(void *arg);
-static void mps_startup_complete(struct mps_softc *sc, struct mps_command *cm);
static int mps_send_iocinit(struct mps_softc *sc);
static int mps_attach_log(struct mps_softc *sc);
static __inline void mps_complete_command(struct mps_command *cm);
-static void mps_dispatch_event(struct mps_softc *sc, uintptr_t data, MPI2_EVENT_NOTIFICATION_REPLY *reply);
+static void mps_dispatch_event(struct mps_softc *sc, uintptr_t data,
+ MPI2_EVENT_NOTIFICATION_REPLY *reply);
static void mps_config_complete(struct mps_softc *sc, struct mps_command *cm);
static void mps_periodic(void *);
+static int mps_reregister_events(struct mps_softc *sc);
+static void mps_enqueue_request(struct mps_softc *sc, struct mps_command *cm);
SYSCTL_NODE(_hw, OID_AUTO, mps, CTLFLAG_RD, 0, "MPS Driver Parameters");
@@ -78,7 +124,7 @@ MALLOC_DEFINE(M_MPT2, "mps", "mpt2 driver memory");
static char mpt2_reset_magic[] = { 0x00, 0x0f, 0x04, 0x0b, 0x02, 0x07, 0x0d };
static int
-mps_hard_reset(struct mps_softc *sc)
+mps_diag_reset(struct mps_softc *sc)
{
uint32_t reg;
int i, error, tries = 0;
@@ -129,7 +175,7 @@ mps_hard_reset(struct mps_softc *sc)
}
static int
-mps_soft_reset(struct mps_softc *sc)
+mps_message_unit_reset(struct mps_softc *sc)
{
mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
@@ -160,7 +206,7 @@ mps_transition_ready(struct mps_softc *sc)
* resetting it.
*/
if (reg & MPI2_DOORBELL_USED) {
- mps_hard_reset(sc);
+ mps_diag_reset(sc);
DELAY(50000);
continue;
}
@@ -181,10 +227,10 @@ mps_transition_ready(struct mps_softc *sc)
} else if (state == MPI2_IOC_STATE_FAULT) {
mps_dprint(sc, MPS_INFO, "IOC in fault state 0x%x\n",
state & MPI2_DOORBELL_FAULT_CODE_MASK);
- mps_hard_reset(sc);
+ mps_diag_reset(sc);
} else if (state == MPI2_IOC_STATE_OPERATIONAL) {
/* Need to take ownership */
- mps_soft_reset(sc);
+ mps_message_unit_reset(sc);
} else if (state == MPI2_IOC_STATE_RESET) {
/* Wait a bit, IOC might be in transition */
mps_dprint(sc, MPS_FAULT,
@@ -220,14 +266,108 @@ mps_transition_operational(struct mps_softc *sc)
state = reg & MPI2_IOC_STATE_MASK;
if (state != MPI2_IOC_STATE_READY) {
- if ((error = mps_transition_ready(sc)) != 0)
+ if ((error = mps_transition_ready(sc)) != 0) {
+ mps_dprint(sc, MPS_FAULT,
+ "%s failed to transition ready\n", __func__);
return (error);
+ }
}
error = mps_send_iocinit(sc);
return (error);
}
+/*
+ * XXX Some of this should probably move to mps.c
+ *
+ * The terms diag reset and hard reset are used interchangeably in the MPI
+ * docs to mean resetting the controller chip. In this code diag reset
+ * cleans everything up, and the hard reset function just sends the reset
+ * sequence to the chip. This should probably be refactored so that every
+ * subsystem gets a reset notification of some sort, and can clean up
+ * appropriately.
+ */
+int
+mps_reinit(struct mps_softc *sc)
+{
+ int error;
+ uint32_t db;
+
+ mps_printf(sc, "%s sc %p\n", __func__, sc);
+
+ mtx_assert(&sc->mps_mtx, MA_OWNED);
+
+ if (sc->mps_flags & MPS_FLAGS_DIAGRESET) {
+ mps_printf(sc, "%s reset already in progress\n", __func__);
+ return 0;
+ }
+
+ /* make sure the completion callbacks can recognize they're getting
+ * a NULL cm_reply due to a reset.
+ */
+ sc->mps_flags |= MPS_FLAGS_DIAGRESET;
+
+ mps_printf(sc, "%s mask interrupts\n", __func__);
+ mps_mask_intr(sc);
+
+ error = mps_diag_reset(sc);
+ if (error != 0) {
+ panic("%s hard reset failed with error %d\n",
+ __func__, error);
+ }
+
+ /* Restore the PCI state, including the MSI-X registers */
+ mps_pci_restore(sc);
+
+ /* Give the I/O subsystem special priority to get itself prepared */
+ mpssas_handle_reinit(sc);
+
+ /* reinitialize queues after the reset */
+ bzero(sc->free_queue, sc->fqdepth * 4);
+ mps_init_queues(sc);
+
+ /* get the chip out of the reset state */
+ error = mps_transition_operational(sc);
+ if (error != 0)
+ panic("%s transition operational failed with error %d\n",
+ __func__, error);
+
+ /* Reinitialize the reply queue. This is delicate because this
+ * function is typically invoked by task mgmt completion callbacks,
+ * which are called by the interrupt thread. We need to make sure
+ * the interrupt handler loop will exit when we return to it, and
+ * that it will recognize the indexes we've changed.
+ */
+ sc->replypostindex = 0;
+ mps_regwrite(sc, MPI2_REPLY_FREE_HOST_INDEX_OFFSET, sc->replyfreeindex);
+ mps_regwrite(sc, MPI2_REPLY_POST_HOST_INDEX_OFFSET, sc->replypostindex);
+
+ db = mps_regread(sc, MPI2_DOORBELL_OFFSET);
+ mps_printf(sc, "%s doorbell 0x%08x\n", __func__, db);
+
+ mps_printf(sc, "%s unmask interrupts post %u free %u\n", __func__,
+ sc->replypostindex, sc->replyfreeindex);
+
+ mps_unmask_intr(sc);
+
+ mps_printf(sc, "%s restarting post %u free %u\n", __func__,
+ sc->replypostindex, sc->replyfreeindex);
+
+ /* restart will reload the event masks clobbered by the reset, and
+ * then enable the port.
+ */
+ mps_reregister_events(sc);
+
+ /* the end of discovery will release the simq, so we're done. */
+ mps_printf(sc, "%s finished sc %p post %u free %u\n",
+ __func__, sc,
+ sc->replypostindex, sc->replyfreeindex);
+
+ sc->mps_flags &= ~MPS_FLAGS_DIAGRESET;
+
+ return 0;
+}
+
/* Wait for the chip to ACK a word that we've put into its FIFO */
static int
mps_wait_db_ack(struct mps_softc *sc)
@@ -382,51 +522,25 @@ mps_request_sync(struct mps_softc *sc, void *req, MPI2_DEFAULT_REPLY *reply,
return (0);
}
-void
+static void
mps_enqueue_request(struct mps_softc *sc, struct mps_command *cm)
{
- mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+ mps_dprint(sc, MPS_TRACE, "%s SMID %u cm %p ccb %p\n", __func__,
+ cm->cm_desc.Default.SMID, cm, cm->cm_ccb);
if (sc->mps_flags & MPS_FLAGS_ATTACH_DONE)
mtx_assert(&sc->mps_mtx, MA_OWNED);
- if ((cm->cm_desc.Default.SMID < 1)
- || (cm->cm_desc.Default.SMID >= sc->num_reqs)) {
- mps_printf(sc, "%s: invalid SMID %d, desc %#x %#x\n",
- __func__, cm->cm_desc.Default.SMID,
- cm->cm_desc.Words.High, cm->cm_desc.Words.Low);
- }
+ if (++sc->io_cmds_active > sc->io_cmds_highwater)
+ sc->io_cmds_highwater++;
+
mps_regwrite(sc, MPI2_REQUEST_DESCRIPTOR_POST_LOW_OFFSET,
cm->cm_desc.Words.Low);
mps_regwrite(sc, MPI2_REQUEST_DESCRIPTOR_POST_HIGH_OFFSET,
cm->cm_desc.Words.High);
}
-int
-mps_request_polled(struct mps_softc *sc, struct mps_command *cm)
-{
- int error, timeout = 0;
-
- error = 0;
-
- cm->cm_flags |= MPS_CM_FLAGS_POLLED;
- cm->cm_complete = NULL;
- mps_map_command(sc, cm);
-
- while ((cm->cm_flags & MPS_CM_FLAGS_COMPLETE) == 0) {
- mps_intr(sc);
- DELAY(50 * 1000);
- if (timeout++ > 1000) {
- mps_dprint(sc, MPS_FAULT, "polling failed\n");
- error = ETIMEDOUT;
- break;
- }
- }
-
- return (error);
-}
-
/*
* Just the FACTS, ma'am.
*/
@@ -469,9 +583,19 @@ mps_get_portfacts(struct mps_softc *sc, MPI2_PORT_FACTS_REPLY *facts, int port)
cm->cm_data = NULL;
error = mps_request_polled(sc, cm);
reply = (MPI2_PORT_FACTS_REPLY *)cm->cm_reply;
- if ((reply->IOCStatus & MPI2_IOCSTATUS_MASK) != MPI2_IOCSTATUS_SUCCESS)
+ if (reply == NULL) {
+ mps_printf(sc, "%s NULL reply\n", __func__);
+ goto done;
+ }
+ if ((reply->IOCStatus & MPI2_IOCSTATUS_MASK) != MPI2_IOCSTATUS_SUCCESS) {
+ mps_printf(sc,
+ "%s error %d iocstatus 0x%x iocloginfo 0x%x type 0x%x\n",
+ __func__, error, reply->IOCStatus, reply->IOCLogInfo,
+ reply->PortType);
error = ENXIO;
+ }
bcopy(reply, facts, sizeof(MPI2_PORT_FACTS_REPLY));
+done:
mps_free_command(sc, cm);
return (error);
@@ -522,35 +646,6 @@ mps_send_iocinit(struct mps_softc *sc)
return (error);
}
-static int
-mps_send_portenable(struct mps_softc *sc)
-{
- MPI2_PORT_ENABLE_REQUEST *request;
- struct mps_command *cm;
-
- mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
-
- if ((cm = mps_alloc_command(sc)) == NULL)
- return (EBUSY);
- request = (MPI2_PORT_ENABLE_REQUEST *)cm->cm_req;
- request->Function = MPI2_FUNCTION_PORT_ENABLE;
- request->MsgFlags = 0;
- request->VP_ID = 0;
- cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
- cm->cm_complete = mps_startup_complete;
-
- mps_enqueue_request(sc, cm);
- return (0);
-}
-
-static int
-mps_send_mur(struct mps_softc *sc)
-{
-
- /* Placeholder */
- return (0);
-}
-
void
mps_memaddr_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
{
@@ -685,7 +780,7 @@ mps_alloc_requests(struct mps_softc *sc)
bus_dmamap_load(sc->req_dmat, sc->req_map, sc->req_frames, rsize,
mps_memaddr_cb, &sc->req_busaddr, 0);
- rsize = sc->facts->IOCRequestFrameSize * MPS_CHAIN_FRAMES * 4;
+ rsize = sc->facts->IOCRequestFrameSize * sc->max_chains * 4;
if (bus_dma_tag_create( sc->mps_parent_dmat, /* parent */
16, 0, /* algnmnt, boundary */
BUS_SPACE_MAXADDR_32BIT,/* lowaddr */
@@ -733,9 +828,9 @@ mps_alloc_requests(struct mps_softc *sc)
bus_dmamap_load(sc->sense_dmat, sc->sense_map, sc->sense_frames, rsize,
mps_memaddr_cb, &sc->sense_busaddr, 0);
- sc->chains = malloc(sizeof(struct mps_chain) * MPS_CHAIN_FRAMES,
- M_MPT2, M_WAITOK | M_ZERO);
- for (i = 0; i < MPS_CHAIN_FRAMES; i++) {
+ sc->chains = malloc(sizeof(struct mps_chain) * sc->max_chains, M_MPT2,
+ M_WAITOK | M_ZERO);
+ for (i = 0; i < sc->max_chains; i++) {
chain = &sc->chains[i];
chain->chain = (MPI2_SGE_IO_UNION *)(sc->chain_frames +
i * sc->facts->IOCRequestFrameSize * 4);
@@ -759,7 +854,7 @@ mps_alloc_requests(struct mps_softc *sc)
busdma_lock_mutex, /* lockfunc */
&sc->mps_mtx, /* lockarg */
&sc->buffer_dmat)) {
- device_printf(sc->mps_dev, "Cannot allocate sense DMA tag\n");
+ device_printf(sc->mps_dev, "Cannot allocate buffer DMA tag\n");
return (ENOMEM);
}
@@ -780,12 +875,16 @@ mps_alloc_requests(struct mps_softc *sc)
cm->cm_desc.Default.SMID = i;
cm->cm_sc = sc;
TAILQ_INIT(&cm->cm_chain_list);
- callout_init(&cm->cm_callout, 1 /*MPSAFE*/);
+ callout_init_mtx(&cm->cm_callout, &sc->mps_mtx, 0);
/* XXX Is a failure here a critical problem? */
if (bus_dmamap_create(sc->buffer_dmat, 0, &cm->cm_dmamap) == 0)
- mps_free_command(sc, cm);
+ if (i <= sc->facts->HighPriorityCredit)
+ mps_free_high_priority_command(sc, cm);
+ else
+ mps_free_command(sc, cm);
else {
+ panic("failed to allocate command %d\n", i);
sc->num_reqs = i;
break;
}
@@ -819,28 +918,53 @@ mps_init_queues(struct mps_softc *sc)
return (0);
}
-int
-mps_attach(struct mps_softc *sc)
+/* Get the driver parameter tunables. Lowest priority are the driver defaults.
+ * Next are the global settings, if they exist. Highest are the per-unit
+ * settings, if they exist.
+ */
+static void
+mps_get_tunables(struct mps_softc *sc)
{
- int i, error;
- char tmpstr[80], tmpstr2[80];
+ char tmpstr[80];
+
+ /* XXX default to some debugging for now */
+ sc->mps_debug = MPS_FAULT;
+ sc->disable_msix = 0;
+ sc->disable_msi = 0;
+ sc->max_chains = MPS_CHAIN_FRAMES;
/*
- * Grab any tunable-set debug level so that tracing works as early
- * as possible.
+ * Grab the global variables.
*/
- snprintf(tmpstr, sizeof(tmpstr), "hw.mps.%d.debug_level",
+ TUNABLE_INT_FETCH("hw.mps.debug_level", &sc->mps_debug);
+ TUNABLE_INT_FETCH("hw.mps.disable_msix", &sc->disable_msix);
+ TUNABLE_INT_FETCH("hw.mps.disable_msi", &sc->disable_msi);
+ TUNABLE_INT_FETCH("hw.mps.max_chains", &sc->max_chains);
+
+ /* Grab the unit-instance variables */
+ snprintf(tmpstr, sizeof(tmpstr), "dev.mps.%d.debug_level",
device_get_unit(sc->mps_dev));
TUNABLE_INT_FETCH(tmpstr, &sc->mps_debug);
- snprintf(tmpstr, sizeof(tmpstr), "hw.mps.%d.allow_multiple_tm_cmds",
+
+ snprintf(tmpstr, sizeof(tmpstr), "dev.mps.%d.disable_msix",
device_get_unit(sc->mps_dev));
- TUNABLE_INT_FETCH(tmpstr, &sc->allow_multiple_tm_cmds);
+ TUNABLE_INT_FETCH(tmpstr, &sc->disable_msix);
- mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+ snprintf(tmpstr, sizeof(tmpstr), "dev.mps.%d.disable_msi",
+ device_get_unit(sc->mps_dev));
+ TUNABLE_INT_FETCH(tmpstr, &sc->disable_msi);
- mtx_init(&sc->mps_mtx, "MPT2SAS lock", NULL, MTX_DEF);
- callout_init_mtx(&sc->periodic, &sc->mps_mtx, 0);
- TAILQ_INIT(&sc->event_list);
+ snprintf(tmpstr, sizeof(tmpstr), "dev.mps.%d.max_chains",
+ device_get_unit(sc->mps_dev));
+ TUNABLE_INT_FETCH(tmpstr, &sc->max_chains);
+}
+
+static void
+mps_setup_sysctl(struct mps_softc *sc)
+{
+ struct sysctl_ctx_list *sysctl_ctx = NULL;
+ struct sysctl_oid *sysctl_tree = NULL;
+ char tmpstr[80], tmpstr2[80];
/*
* Setup the sysctl variable so the user can change the debug level
@@ -850,44 +974,85 @@ mps_attach(struct mps_softc *sc)
device_get_unit(sc->mps_dev));
snprintf(tmpstr2, sizeof(tmpstr2), "%d", device_get_unit(sc->mps_dev));
- sysctl_ctx_init(&sc->sysctl_ctx);
- sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
- SYSCTL_STATIC_CHILDREN(_hw_mps), OID_AUTO, tmpstr2, CTLFLAG_RD,
- 0, tmpstr);
- if (sc->sysctl_tree == NULL)
- return (ENOMEM);
+ sysctl_ctx = device_get_sysctl_ctx(sc->mps_dev);
+ if (sysctl_ctx != NULL)
+ sysctl_tree = device_get_sysctl_tree(sc->mps_dev);
- SYSCTL_ADD_UINT(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree),
+ if (sysctl_tree == NULL) {
+ sysctl_ctx_init(&sc->sysctl_ctx);
+ sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
+ SYSCTL_STATIC_CHILDREN(_hw_mps), OID_AUTO, tmpstr2,
+ CTLFLAG_RD, 0, tmpstr);
+ if (sc->sysctl_tree == NULL)
+ return;
+ sysctl_ctx = &sc->sysctl_ctx;
+ sysctl_tree = sc->sysctl_tree;
+ }
+
+ SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree),
OID_AUTO, "debug_level", CTLFLAG_RW, &sc->mps_debug, 0,
"mps debug level");
- SYSCTL_ADD_UINT(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree),
- OID_AUTO, "allow_multiple_tm_cmds", CTLFLAG_RW,
- &sc->allow_multiple_tm_cmds, 0,
- "allow multiple simultaneous task management cmds");
+ SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree),
+ OID_AUTO, "disable_msix", CTLFLAG_RD, &sc->disable_msix, 0,
+ "Disable the use of MSI-X interrupts");
+
+ SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree),
+ OID_AUTO, "disable_msi", CTLFLAG_RD, &sc->disable_msi, 0,
+ "Disable the use of MSI interrupts");
+
+ SYSCTL_ADD_STRING(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree),
+ OID_AUTO, "firmware_version", CTLFLAG_RW, &sc->fw_version,
+ strlen(sc->fw_version), "firmware version");
+
+ SYSCTL_ADD_STRING(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree),
+ OID_AUTO, "driver_version", CTLFLAG_RW, MPS_DRIVER_VERSION,
+ strlen(MPS_DRIVER_VERSION), "driver version");
- SYSCTL_ADD_INT(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree),
+ SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree),
OID_AUTO, "io_cmds_active", CTLFLAG_RD,
&sc->io_cmds_active, 0, "number of currently active commands");
- SYSCTL_ADD_INT(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree),
+ SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree),
OID_AUTO, "io_cmds_highwater", CTLFLAG_RD,
&sc->io_cmds_highwater, 0, "maximum active commands seen");
- SYSCTL_ADD_INT(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree),
+ SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree),
OID_AUTO, "chain_free", CTLFLAG_RD,
&sc->chain_free, 0, "number of free chain elements");
- SYSCTL_ADD_INT(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree),
+ SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree),
OID_AUTO, "chain_free_lowwater", CTLFLAG_RD,
&sc->chain_free_lowwater, 0,"lowest number of free chain elements");
- SYSCTL_ADD_UQUAD(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree),
+ SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree),
+ OID_AUTO, "max_chains", CTLFLAG_RD,
+ &sc->max_chains, 0,"maximum chain frames that will be allocated");
+
+#if __FreeBSD_version >= 900030
+ SYSCTL_ADD_UQUAD(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree),
OID_AUTO, "chain_alloc_fail", CTLFLAG_RD,
&sc->chain_alloc_fail, "chain allocation failures");
+#endif //FreeBSD_version >= 900030
+}
- if ((error = mps_transition_ready(sc)) != 0)
+int
+mps_attach(struct mps_softc *sc)
+{
+ int i, error;
+
+ mps_get_tunables(sc);
+
+ mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+
+ mtx_init(&sc->mps_mtx, "MPT2SAS lock", NULL, MTX_DEF);
+ callout_init_mtx(&sc->periodic, &sc->mps_mtx, 0);
+ TAILQ_INIT(&sc->event_list);
+
+ if ((error = mps_transition_ready(sc)) != 0) {
+ mps_printf(sc, "%s failed to transition ready\n", __func__);
return (error);
+ }
sc->facts = malloc(sizeof(MPI2_IOC_FACTS_REPLY), M_MPT2,
M_ZERO|M_NOWAIT);
@@ -896,11 +1061,15 @@ mps_attach(struct mps_softc *sc)
mps_print_iocfacts(sc, sc->facts);
- mps_printf(sc, "Firmware: %02d.%02d.%02d.%02d\n",
+ snprintf(sc->fw_version, sizeof(sc->fw_version),
+ "%02d.%02d.%02d.%02d",
sc->facts->FWVersion.Struct.Major,
sc->facts->FWVersion.Struct.Minor,
sc->facts->FWVersion.Struct.Unit,
sc->facts->FWVersion.Struct.Dev);
+
+ mps_printf(sc, "Firmware: %s, Driver: %s\n", sc->fw_version,
+ MPS_DRIVER_VERSION);
mps_printf(sc, "IOCCapabilities: %b\n", sc->facts->IOCCapabilities,
"\20" "\3ScsiTaskFull" "\4DiagTrace" "\5SnapBuf" "\6ExtBuf"
"\7EEDP" "\10BiDirTarg" "\11Multicast" "\14TransRetry" "\15IR"
@@ -914,34 +1083,66 @@ mps_attach(struct mps_softc *sc)
*/
if ((sc->facts->IOCCapabilities &
MPI2_IOCFACTS_CAPABILITY_EVENT_REPLAY) == 0) {
- mps_hard_reset(sc);
+ mps_diag_reset(sc);
if ((error = mps_transition_ready(sc)) != 0)
return (error);
}
/*
+ * Set flag if IR Firmware is loaded.
+ */
+ if (sc->facts->IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID)
+ sc->ir_firmware = 1;
+
+ /*
+ * Check if controller supports FW diag buffers and set flag to enable
+ * each type.
+ */
+ if (sc->facts->IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER)
+ sc->fw_diag_buffer_list[MPI2_DIAG_BUF_TYPE_TRACE].enabled =
+ TRUE;
+ if (sc->facts->IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER)
+ sc->fw_diag_buffer_list[MPI2_DIAG_BUF_TYPE_SNAPSHOT].enabled =
+ TRUE;
+ if (sc->facts->IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER)
+ sc->fw_diag_buffer_list[MPI2_DIAG_BUF_TYPE_EXTENDED].enabled =
+ TRUE;
+
+ /*
+ * Set flag if EEDP is supported and if TLR is supported.
+ */
+ if (sc->facts->IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_EEDP)
+ sc->eedp_enabled = TRUE;
+ if (sc->facts->IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_TLR)
+ sc->control_TLR = TRUE;
+
+ /*
* Size the queues. Since the reply queues always need one free entry,
* we'll just deduct one reply message here.
*/
sc->num_reqs = MIN(MPS_REQ_FRAMES, sc->facts->RequestCredit);
sc->num_replies = MIN(MPS_REPLY_FRAMES + MPS_EVT_REPLY_FRAMES,
sc->facts->MaxReplyDescriptorPostQueueDepth) - 1;
- mps_dprint(sc, MPS_INFO, "num_reqs %d, num_replies %d\n", sc->num_reqs,
- sc->num_replies);
TAILQ_INIT(&sc->req_list);
+ TAILQ_INIT(&sc->high_priority_req_list);
TAILQ_INIT(&sc->chain_list);
TAILQ_INIT(&sc->tm_list);
- TAILQ_INIT(&sc->io_list);
if (((error = mps_alloc_queues(sc)) != 0) ||
((error = mps_alloc_replies(sc)) != 0) ||
((error = mps_alloc_requests(sc)) != 0)) {
+ mps_printf(sc, "%s failed to alloc\n", __func__);
mps_free(sc);
return (error);
}
if (((error = mps_init_queues(sc)) != 0) ||
((error = mps_transition_operational(sc)) != 0)) {
+ mps_printf(sc, "%s failed to transition operational\n", __func__);
mps_free(sc);
return (error);
}
@@ -964,6 +1165,8 @@ mps_attach(struct mps_softc *sc)
sc->facts->NumberOfPorts, M_MPT2, M_ZERO|M_WAITOK);
for (i = 0; i < sc->facts->NumberOfPorts; i++) {
if ((error = mps_get_portfacts(sc, &sc->pfacts[i], i)) != 0) {
+ mps_printf(sc, "%s failed to get portfacts for port %d\n",
+ __func__, i);
mps_free(sc);
return (error);
}
@@ -982,10 +1185,17 @@ mps_attach(struct mps_softc *sc)
}
if ((error = mps_pci_setup_interrupts(sc)) != 0) {
+ mps_printf(sc, "%s failed to setup interrupts\n", __func__);
mps_free(sc);
return (error);
}
+ /*
+ * The static page function currently read is ioc page8. Others can be
+ * added in future.
+ */
+ mps_base_static_config_pages(sc);
+
/* Start the periodic watchdog check on the IOC Doorbell */
mps_periodic(sc);
@@ -1001,11 +1211,24 @@ mps_attach(struct mps_softc *sc)
error = EINVAL;
}
+ /*
+ * Allow IR to shutdown gracefully when shutdown occurs.
+ */
+ sc->shutdown_eh = EVENTHANDLER_REGISTER(shutdown_final,
+ mpssas_ir_shutdown, sc, SHUTDOWN_PRI_DEFAULT);
+
+ if (sc->shutdown_eh == NULL)
+ mps_dprint(sc, MPS_FAULT, "shutdown event registration "
+ "failed\n");
+
+ mps_setup_sysctl(sc);
+
sc->mps_flags |= MPS_FLAGS_ATTACH_DONE;
return (error);
}
+/* Run through any late-start handlers. */
static void
mps_startup(void *arg)
{
@@ -1015,7 +1238,9 @@ mps_startup(void *arg)
mps_lock(sc);
mps_unmask_intr(sc);
- mps_send_portenable(sc);
+ /* initialize device mapping tables */
+ mps_mapping_initialize(sc);
+ mpssas_startup(sc);
mps_unlock(sc);
}
@@ -1033,36 +1258,14 @@ mps_periodic(void *arg)
db = mps_regread(sc, MPI2_DOORBELL_OFFSET);
if ((db & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) {
device_printf(sc->mps_dev, "IOC Fault 0x%08x, Resetting\n", db);
- /* XXX Need to broaden this to re-initialize the chip */
- mps_hard_reset(sc);
- db = mps_regread(sc, MPI2_DOORBELL_OFFSET);
- if ((db & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) {
- device_printf(sc->mps_dev, "Second IOC Fault 0x%08x, "
- "Giving up!\n", db);
- return;
- }
+
+ mps_reinit(sc);
}
callout_reset(&sc->periodic, MPS_PERIODIC_DELAY * hz, mps_periodic, sc);
}
static void
-mps_startup_complete(struct mps_softc *sc, struct mps_command *cm)
-{
- MPI2_PORT_ENABLE_REPLY *reply;
-
- mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
-
- reply = (MPI2_PORT_ENABLE_REPLY *)cm->cm_reply;
- if ((reply->IOCStatus & MPI2_IOCSTATUS_MASK) != MPI2_IOCSTATUS_SUCCESS)
- mps_dprint(sc, MPS_FAULT, "Portenable failed\n");
-
- mps_free_command(sc, cm);
- config_intrhook_disestablish(&sc->mps_ich);
-
-}
-
-static void
mps_log_evt_handler(struct mps_softc *sc, uintptr_t data,
MPI2_EVENT_NOTIFICATION_REPLY *event)
{
@@ -1134,7 +1337,7 @@ mps_free(struct mps_softc *sc)
/* Put the IOC back in the READY state. */
mps_lock(sc);
- if ((error = mps_send_mur(sc)) != 0) {
+ if ((error = mps_transition_ready(sc)) != 0) {
mps_unlock(sc);
return (error);
}
@@ -1197,6 +1400,12 @@ mps_free(struct mps_softc *sc)
if (sc->sysctl_tree != NULL)
sysctl_ctx_free(&sc->sysctl_ctx);
+ mps_mapping_free_memory(sc);
+
+ /* Deregister the shutdown function */
+ if (sc->shutdown_eh != NULL)
+ EVENTHANDLER_DEREGISTER(shutdown_final, sc->shutdown_eh);
+
mtx_destroy(&sc->mps_mtx);
return (0);
@@ -1208,14 +1417,26 @@ mps_complete_command(struct mps_command *cm)
if (cm->cm_flags & MPS_CM_FLAGS_POLLED)
cm->cm_flags |= MPS_CM_FLAGS_COMPLETE;
- if (cm->cm_complete != NULL)
+ if (cm->cm_complete != NULL) {
+ mps_dprint(cm->cm_sc, MPS_TRACE,
+ "%s cm %p calling cm_complete %p data %p reply %p\n",
+ __func__, cm, cm->cm_complete, cm->cm_complete_data,
+ cm->cm_reply);
cm->cm_complete(cm->cm_sc, cm);
+ }
if (cm->cm_flags & MPS_CM_FLAGS_WAKEUP) {
mps_dprint(cm->cm_sc, MPS_TRACE, "%s: waking up %p\n",
__func__, cm);
wakeup(cm);
}
+
+ if (cm->cm_sc->io_cmds_active != 0) {
+ cm->cm_sc->io_cmds_active--;
+ } else {
+ mps_dprint(cm->cm_sc, MPS_INFO, "Warning: io_cmds_active is "
+ "out of sync - resynching to 0\n");
+ }
}
void
@@ -1251,6 +1472,7 @@ mps_intr_msi(void *data)
struct mps_softc *sc;
sc = (struct mps_softc *)data;
+ mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
mps_lock(sc);
mps_intr_locked(data);
mps_unlock(sc);
@@ -1268,20 +1490,35 @@ mps_intr_locked(void *data)
struct mps_command *cm = NULL;
uint8_t flags;
u_int pq;
+ MPI2_DIAG_RELEASE_REPLY *rel_rep;
+ mps_fw_diagnostic_buffer_t *pBuffer;
sc = (struct mps_softc *)data;
pq = sc->replypostindex;
+ mps_dprint(sc, MPS_TRACE,
+ "%s sc %p starting with replypostindex %u\n",
+ __func__, sc, sc->replypostindex);
for ( ;; ) {
cm = NULL;
- desc = &sc->post_queue[pq];
+ desc = &sc->post_queue[sc->replypostindex];
flags = desc->Default.ReplyFlags &
MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK;
if ((flags == MPI2_RPY_DESCRIPT_FLAGS_UNUSED)
|| (desc->Words.High == 0xffffffff))
break;
+ /* increment the replypostindex now, so that event handlers
+ * and cm completion handlers which decide to do a diag
+ * reset can zero it without it getting incremented again
+ * afterwards, and we break out of this loop on the next
+ * iteration since the reply post queue has been cleared to
+ * 0xFF and all descriptors look unused (which they are).
+ */
+ if (++sc->replypostindex >= sc->pqdepth)
+ sc->replypostindex = 0;
+
switch (flags) {
case MPI2_RPY_DESCRIPT_FLAGS_SCSI_IO_SUCCESS:
cm = &sc->commands[desc->SCSIIOSuccess.SMID];
@@ -1323,8 +1560,32 @@ mps_intr_locked(void *data)
panic("Reply address out of range");
}
if (desc->AddressReply.SMID == 0) {
- mps_dispatch_event(sc, baddr,
- (MPI2_EVENT_NOTIFICATION_REPLY *) reply);
+ if (((MPI2_DEFAULT_REPLY *)reply)->Function ==
+ MPI2_FUNCTION_DIAG_BUFFER_POST) {
+ /*
+ * If SMID is 0 for Diag Buffer Post,
+ * this implies that the reply is due to
+ * a release function with a status that
+ * the buffer has been released. Set
+ * the buffer flags accordingly.
+ */
+ rel_rep =
+ (MPI2_DIAG_RELEASE_REPLY *)reply;
+ if (rel_rep->IOCStatus ==
+ MPI2_IOCSTATUS_DIAGNOSTIC_RELEASED)
+ {
+ pBuffer =
+ &sc->fw_diag_buffer_list[
+ rel_rep->BufferType];
+ pBuffer->valid_data = TRUE;
+ pBuffer->owned_by_firmware =
+ FALSE;
+ pBuffer->immediate = FALSE;
+ }
+ } else
+ mps_dispatch_event(sc, baddr,
+ (MPI2_EVENT_NOTIFICATION_REPLY *)
+ reply);
} else {
cm = &sc->commands[desc->AddressReply.SMID];
cm->cm_reply = reply;
@@ -1349,14 +1610,13 @@ mps_intr_locked(void *data)
desc->Words.Low = 0xffffffff;
desc->Words.High = 0xffffffff;
- if (++pq >= sc->pqdepth)
- pq = 0;
}
if (pq != sc->replypostindex) {
- mps_dprint(sc, MPS_INFO, "writing postindex %d\n", pq);
- mps_regwrite(sc, MPI2_REPLY_POST_HOST_INDEX_OFFSET, pq);
- sc->replypostindex = pq;
+ mps_dprint(sc, MPS_TRACE,
+ "%s sc %p writing postindex %d\n",
+ __func__, sc, sc->replypostindex);
+ mps_regwrite(sc, MPI2_REPLY_POST_HOST_INDEX_OFFSET, sc->replypostindex);
}
return;
@@ -1379,6 +1639,28 @@ mps_dispatch_event(struct mps_softc *sc, uintptr_t data,
if (handled == 0)
device_printf(sc->mps_dev, "Unhandled event 0x%x\n", event);
+
+ /*
+ * This is the only place that the event/reply should be freed.
+ * Anything wanting to hold onto the event data should have
+ * already copied it into their own storage.
+ */
+ mps_free_reply(sc, data);
+}
+
+static void
+mps_reregister_events_complete(struct mps_softc *sc, struct mps_command *cm)
+{
+ mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+
+ if (cm->cm_reply)
+ mps_print_event(sc,
+ (MPI2_EVENT_NOTIFICATION_REPLY *)cm->cm_reply);
+
+ mps_free_command(sc, cm);
+
+ /* next, send a port enable */
+ mpssas_startup(sc);
}
/*
@@ -1445,14 +1727,60 @@ mps_update_events(struct mps_softc *sc, struct mps_event_handle *handle,
error = mps_request_polled(sc, cm);
reply = (MPI2_EVENT_NOTIFICATION_REPLY *)cm->cm_reply;
- if ((reply->IOCStatus & MPI2_IOCSTATUS_MASK) != MPI2_IOCSTATUS_SUCCESS)
+ if ((reply == NULL) ||
+ (reply->IOCStatus & MPI2_IOCSTATUS_MASK) != MPI2_IOCSTATUS_SUCCESS)
error = ENXIO;
mps_print_event(sc, reply);
+ mps_dprint(sc, MPS_TRACE, "%s finished error %d\n", __func__, error);
mps_free_command(sc, cm);
return (error);
}
+static int
+mps_reregister_events(struct mps_softc *sc)
+{
+ MPI2_EVENT_NOTIFICATION_REQUEST *evtreq;
+ struct mps_command *cm;
+ struct mps_event_handle *eh;
+ int error, i;
+
+ mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+
+ /* first, reregister events */
+
+ memset(sc->event_mask, 0xff, 16);
+
+ TAILQ_FOREACH(eh, &sc->event_list, eh_list) {
+ for (i = 0; i < 16; i++)
+ sc->event_mask[i] &= ~eh->mask[i];
+ }
+
+ if ((cm = mps_alloc_command(sc)) == NULL)
+ return (EBUSY);
+ evtreq = (MPI2_EVENT_NOTIFICATION_REQUEST *)cm->cm_req;
+ evtreq->Function = MPI2_FUNCTION_EVENT_NOTIFICATION;
+ evtreq->MsgFlags = 0;
+ evtreq->SASBroadcastPrimitiveMasks = 0;
+#ifdef MPS_DEBUG_ALL_EVENTS
+ {
+ u_char fullmask[16];
+ memset(fullmask, 0x00, 16);
+ bcopy(fullmask, (uint8_t *)&evtreq->EventMasks, 16);
+ }
+#else
+ bcopy(sc->event_mask, (uint8_t *)&evtreq->EventMasks, 16);
+#endif
+ cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+ cm->cm_data = NULL;
+ cm->cm_complete = mps_reregister_events_complete;
+
+ error = mps_map_command(sc, cm);
+
+ mps_dprint(sc, MPS_TRACE, "%s finished with error %d\n", __func__, error);
+ return (error);
+}
+
int
mps_deregister_events(struct mps_softc *sc, struct mps_event_handle *handle)
{
@@ -1511,6 +1839,7 @@ mps_push_sge(struct mps_command *cm, void *sgep, size_t len, int segsleft)
MPI2_SGE_TRANSACTION_UNION *tc = sgep;
MPI2_SGE_SIMPLE64 *sge = sgep;
int error, type;
+ uint32_t saved_buf_len, saved_address_low, saved_address_high;
type = (tc->Flags & MPI2_SGE_FLAGS_ELEMENT_MASK);
@@ -1609,12 +1938,48 @@ mps_push_sge(struct mps_command *cm, void *sgep, size_t len, int segsleft)
if (segsleft == 1 && type == MPI2_SGE_FLAGS_SIMPLE_ELEMENT) {
/*
- * Last element of the last segment of the entire
- * buffer.
+ * If this is a bi-directional request, need to account for that
+ * here. Save the pre-filled sge values. These will be used
+ * either for the 2nd SGL or for a single direction SGL. If
+ * cm_out_len is non-zero, this is a bi-directional request, so
+ * fill in the OUT SGL first, then the IN SGL, otherwise just
+ * fill in the IN SGL. Note that at this time, when filling in
+ * 2 SGL's for a bi-directional request, they both use the same
+ * DMA buffer (same cm command).
*/
- sge->FlagsLength |= ((MPI2_SGE_FLAGS_LAST_ELEMENT |
+ saved_buf_len = sge->FlagsLength & 0x00FFFFFF;
+ saved_address_low = sge->Address.Low;
+ saved_address_high = sge->Address.High;
+ if (cm->cm_out_len) {
+ sge->FlagsLength = cm->cm_out_len |
+ ((uint32_t)(MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
+ MPI2_SGE_FLAGS_END_OF_BUFFER |
+ MPI2_SGE_FLAGS_HOST_TO_IOC |
+ MPI2_SGE_FLAGS_64_BIT_ADDRESSING) <<
+ MPI2_SGE_FLAGS_SHIFT);
+ cm->cm_sglsize -= len;
+ bcopy(sgep, cm->cm_sge, len);
+ cm->cm_sge = (MPI2_SGE_IO_UNION *)((uintptr_t)cm->cm_sge
+ + len);
+ }
+ sge->FlagsLength = saved_buf_len |
+ ((uint32_t)(MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
MPI2_SGE_FLAGS_END_OF_BUFFER |
- MPI2_SGE_FLAGS_END_OF_LIST) << MPI2_SGE_FLAGS_SHIFT);
+ MPI2_SGE_FLAGS_LAST_ELEMENT |
+ MPI2_SGE_FLAGS_END_OF_LIST |
+ MPI2_SGE_FLAGS_64_BIT_ADDRESSING) <<
+ MPI2_SGE_FLAGS_SHIFT);
+ if (cm->cm_flags & MPS_CM_FLAGS_DATAIN) {
+ sge->FlagsLength |=
+ ((uint32_t)(MPI2_SGE_FLAGS_IOC_TO_HOST) <<
+ MPI2_SGE_FLAGS_SHIFT);
+ } else {
+ sge->FlagsLength |=
+ ((uint32_t)(MPI2_SGE_FLAGS_HOST_TO_IOC) <<
+ MPI2_SGE_FLAGS_SHIFT);
+ }
+ sge->Address.Low = saved_address_low;
+ sge->Address.High = saved_address_high;
}
cm->cm_sglsize -= len;
@@ -1633,10 +1998,10 @@ mps_add_dmaseg(struct mps_command *cm, vm_paddr_t pa, size_t len, u_int flags,
MPI2_SGE_SIMPLE64 sge;
/*
- * This driver always uses 64-bit address elements for
- * simplicity.
+ * This driver always uses 64-bit address elements for simplicity.
*/
- flags |= MPI2_SGE_FLAGS_SIMPLE_ELEMENT | MPI2_SGE_FLAGS_ADDRESS_SIZE;
+ flags |= MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
+ MPI2_SGE_FLAGS_64_BIT_ADDRESSING;
sge.FlagsLength = len | (flags << MPI2_SGE_FLAGS_SHIFT);
mps_from_u64(pa, &sge.Address);
@@ -1664,8 +2029,8 @@ mps_data_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
}
/*
- * Set up DMA direction flags. Note that we don't support
- * bi-directional transfers, with the exception of SMP passthrough.
+ * Set up DMA direction flags. Bi-directional requests are also handled
+ * here. In that case, both direction flags will be set.
*/
sflags = 0;
if (cm->cm_flags & MPS_CM_FLAGS_SMP_PASS) {
@@ -1691,14 +2056,13 @@ mps_data_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
sflags |= MPI2_SGE_FLAGS_DIRECTION |
MPI2_SGE_FLAGS_END_OF_BUFFER;
} else if (cm->cm_flags & MPS_CM_FLAGS_DATAOUT) {
- sflags |= MPI2_SGE_FLAGS_DIRECTION;
+ sflags |= MPI2_SGE_FLAGS_HOST_TO_IOC;
dir = BUS_DMASYNC_PREWRITE;
} else
dir = BUS_DMASYNC_PREREAD;
for (i = 0; i < nsegs; i++) {
- if ((cm->cm_flags & MPS_CM_FLAGS_SMP_PASS)
- && (i != 0)) {
+ if ((cm->cm_flags & MPS_CM_FLAGS_SMP_PASS) && (i != 0)) {
sflags &= ~MPI2_SGE_FLAGS_DIRECTION;
}
error = mps_add_dmaseg(cm, segs[i].ds_addr, segs[i].ds_len,
@@ -1726,8 +2090,11 @@ mps_data_cb2(void *arg, bus_dma_segment_t *segs, int nsegs, bus_size_t mapsize,
}
/*
+ * This is the routine to enqueue commands ansynchronously.
* Note that the only error path here is from bus_dmamap_load(), which can
- * return EINPROGRESS if it is waiting for resources.
+ * return EINPROGRESS if it is waiting for resources. Other than this, it's
+ * assumed that if you have a command in-hand, then you have enough credits
+ * to use it.
*/
int
mps_map_command(struct mps_softc *sc, struct mps_command *cm)
@@ -1752,7 +2119,58 @@ mps_map_command(struct mps_softc *sc, struct mps_command *cm)
MPI2_SGE_FLAGS_SHIFT;
sge->Address = 0;
}
- mps_enqueue_request(sc, cm);
+ mps_enqueue_request(sc, cm);
+ }
+
+ return (error);
+}
+
+/*
+ * This is the routine to enqueue commands synchronously. An error of
+ * EINPROGRESS from mps_map_command() is ignored since the command will
+ * be executed and enqueued automatically. Other errors come from msleep().
+ */
+int
+mps_wait_command(struct mps_softc *sc, struct mps_command *cm, int timeout)
+{
+ int error;
+
+ mtx_assert(&sc->mps_mtx, MA_OWNED);
+
+ cm->cm_complete = NULL;
+ cm->cm_flags |= MPS_CM_FLAGS_WAKEUP;
+ error = mps_map_command(sc, cm);
+ if ((error != 0) && (error != EINPROGRESS))
+ return (error);
+ error = msleep(cm, &sc->mps_mtx, 0, "mpswait", timeout);
+ if (error == EWOULDBLOCK)
+ error = ETIMEDOUT;
+ return (error);
+}
+
+/*
+ * This is the routine to enqueue a command synchonously and poll for
+ * completion. Its use should be rare.
+ */
+int
+mps_request_polled(struct mps_softc *sc, struct mps_command *cm)
+{
+ int error, timeout = 0;
+
+ error = 0;
+
+ cm->cm_flags |= MPS_CM_FLAGS_POLLED;
+ cm->cm_complete = NULL;
+ mps_map_command(sc, cm);
+
+ while ((cm->cm_flags & MPS_CM_FLAGS_COMPLETE) == 0) {
+ mps_intr_locked(sc);
+ DELAY(50 * 1000);
+ if (timeout++ > 1000) {
+ mps_dprint(sc, MPS_FAULT, "polling failed\n");
+ error = ETIMEDOUT;
+ break;
+ }
}
return (error);
@@ -1816,11 +2234,13 @@ mps_read_config_page(struct mps_softc *sc, struct mps_config_params *params)
cm->cm_complete = mps_config_complete;
return (mps_map_command(sc, cm));
} else {
- cm->cm_complete = NULL;
- cm->cm_flags |= MPS_CM_FLAGS_WAKEUP;
- if ((error = mps_map_command(sc, cm)) != 0)
+ error = mps_wait_command(sc, cm, 0);
+ if (error) {
+ mps_dprint(sc, MPS_FAULT,
+ "Error %d reading config page\n", error);
+ mps_free_command(sc, cm);
return (error);
- msleep(cm, &sc->mps_mtx, 0, "mpswait", 0);
+ }
mps_config_complete(sc, cm);
}
@@ -1853,10 +2273,14 @@ mps_config_complete(struct mps_softc *sc, struct mps_command *cm)
*/
if ((cm->cm_flags & MPS_CM_FLAGS_ERROR_MASK) != 0) {
params->status = MPI2_IOCSTATUS_BUSY;
- goto bailout;
+ goto done;
}
reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
+ if (reply == NULL) {
+ params->status = MPI2_IOCSTATUS_BUSY;
+ goto done;
+ }
params->status = reply->IOCStatus;
if (params->hdr.Ext.ExtPageType != 0) {
params->hdr.Ext.ExtPageType = reply->ExtPageType;
@@ -1868,8 +2292,7 @@ mps_config_complete(struct mps_softc *sc, struct mps_command *cm)
params->hdr.Struct.PageVersion = reply->Header.PageVersion;
}
-bailout:
-
+done:
mps_free_command(sc, cm);
if (params->callback != NULL)
params->callback(sc, params);
diff --git a/sys/dev/mps/mps_config.c b/sys/dev/mps/mps_config.c
new file mode 100644
index 0000000..da1f3fd
--- /dev/null
+++ b/sys/dev/mps/mps_config.c
@@ -0,0 +1,1393 @@
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/* TODO Move headers to mpsvar */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/kthread.h>
+#include <sys/taskqueue.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/sysctl.h>
+#include <sys/eventhandler.h>
+#include <sys/uio.h>
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <dev/mps/mpi/mpi2_type.h>
+#include <dev/mps/mpi/mpi2.h>
+#include <dev/mps/mpi/mpi2_ioc.h>
+#include <dev/mps/mpi/mpi2_sas.h>
+#include <dev/mps/mpi/mpi2_cnfg.h>
+#include <dev/mps/mpi/mpi2_init.h>
+#include <dev/mps/mpi/mpi2_tool.h>
+#include <dev/mps/mps_ioctl.h>
+#include <dev/mps/mpsvar.h>
+
+/**
+ * mps_config_get_ioc_pg8 - obtain ioc page 8
+ * @sc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mps_config_get_ioc_pg8(struct mps_softc *sc, Mpi2ConfigReply_t *mpi_reply,
+ Mpi2IOCPage8_t *config_page)
+{
+ MPI2_CONFIG_REQUEST *request;
+ MPI2_CONFIG_REPLY *reply;
+ struct mps_command *cm;
+ MPI2_CONFIG_PAGE_IOC_8 *page = NULL;
+ int error = 0;
+ u16 ioc_status;
+
+ mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+
+ if ((cm = mps_alloc_command(sc)) == NULL) {
+ printf("%s: command alloc failed @ line %d\n", __func__,
+ __LINE__);
+ error = EBUSY;
+ goto out;
+ }
+ request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
+ bzero(request, sizeof(MPI2_CONFIG_REQUEST));
+ request->Function = MPI2_FUNCTION_CONFIG;
+ request->Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ request->Header.PageType = MPI2_CONFIG_PAGETYPE_IOC;
+ request->Header.PageNumber = 8;
+ request->Header.PageVersion = MPI2_IOCPAGE8_PAGEVERSION;
+ cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+ cm->cm_data = NULL;
+ error = mps_request_polled(sc, cm);
+ reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
+ if (error || (reply == NULL)) {
+ /* FIXME */
+ /* If the poll returns error then we need to do diag reset */
+ printf("%s: poll for header completed with error %d",
+ __func__, error);
+ error = ENXIO;
+ goto out;
+ }
+ ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+ bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ /* FIXME */
+ /* If the poll returns error then we need to do diag reset */
+ printf("%s: header read with error; iocstatus = 0x%x\n",
+ __func__, ioc_status);
+ error = ENXIO;
+ goto out;
+ }
+ /* We have to do free and alloc for the reply-free and reply-post
+ * counters to match - Need to review the reply FIFO handling.
+ */
+ mps_free_command(sc, cm);
+
+ if ((cm = mps_alloc_command(sc)) == NULL) {
+ printf("%s: command alloc failed @ line %d\n", __func__,
+ __LINE__);
+ error = EBUSY;
+ goto out;
+ }
+ request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
+ bzero(request, sizeof(MPI2_CONFIG_REQUEST));
+ request->Function = MPI2_FUNCTION_CONFIG;
+ request->Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ request->Header.PageType = MPI2_CONFIG_PAGETYPE_IOC;
+ request->Header.PageNumber = 8;
+ request->Header.PageVersion = MPI2_IOCPAGE8_PAGEVERSION;
+ request->Header.PageLength = mpi_reply->Header.PageLength;
+ cm->cm_length = le16toh(mpi_reply->Header.PageLength) * 4;
+ cm->cm_sge = &request->PageBufferSGE;
+ cm->cm_sglsize = sizeof(MPI2_SGE_IO_UNION);
+ cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE | MPS_CM_FLAGS_DATAIN;
+ cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+ page = malloc((cm->cm_length), M_MPT2, M_ZERO | M_NOWAIT);
+ if (!page) {
+ printf("%s: page alloc failed\n", __func__);
+ error = ENOMEM;
+ goto out;
+ }
+ cm->cm_data = page;
+ error = mps_request_polled(sc, cm);
+ reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
+ if (error || (reply == NULL)) {
+ /* FIXME */
+ /* If the poll returns error then we need to do diag reset */
+ printf("%s: poll for page completed with error %d",
+ __func__, error);
+ error = ENXIO;
+ goto out;
+ }
+ ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+ bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ /* FIXME */
+ /* If the poll returns error then we need to do diag reset */
+ printf("%s: page read with error; iocstatus = 0x%x\n",
+ __func__, ioc_status);
+ error = ENXIO;
+ goto out;
+ }
+ bcopy(page, config_page, MIN(cm->cm_length, (sizeof(Mpi2IOCPage8_t))));
+
+out:
+ free(page, M_MPT2);
+ if (cm)
+ mps_free_command(sc, cm);
+ return (error);
+}
+
+/**
+ * mps_config_get_man_pg10 - obtain Manufacturing Page 10 data and set flags
+ * accordingly. Currently, this page does not need to return to caller.
+ * @sc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mps_config_get_man_pg10(struct mps_softc *sc, Mpi2ConfigReply_t *mpi_reply)
+{
+ MPI2_CONFIG_REQUEST *request;
+ MPI2_CONFIG_REPLY *reply;
+ struct mps_command *cm;
+ pMpi2ManufacturingPagePS_t page = NULL;
+ uint32_t *pPS_info;
+ uint8_t OEM_Value = 0;
+ int error = 0;
+ u16 ioc_status;
+
+ mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+
+ if ((cm = mps_alloc_command(sc)) == NULL) {
+ printf("%s: command alloc failed @ line %d\n", __func__,
+ __LINE__);
+ error = EBUSY;
+ goto out;
+ }
+ request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
+ bzero(request, sizeof(MPI2_CONFIG_REQUEST));
+ request->Function = MPI2_FUNCTION_CONFIG;
+ request->Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ request->Header.PageType = MPI2_CONFIG_PAGETYPE_MANUFACTURING;
+ request->Header.PageNumber = 10;
+ request->Header.PageVersion = MPI2_MANUFACTURING10_PAGEVERSION;
+ cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+ cm->cm_data = NULL;
+ error = mps_request_polled(sc, cm);
+ reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
+ if (error || (reply == NULL)) {
+ /* FIXME */
+ /* If the poll returns error then we need to do diag reset */
+ printf("%s: poll for header completed with error %d",
+ __func__, error);
+ error = ENXIO;
+ goto out;
+ }
+ ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+ bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ /* FIXME */
+ /* If the poll returns error then we need to do diag reset */
+ printf("%s: header read with error; iocstatus = 0x%x\n",
+ __func__, ioc_status);
+ error = ENXIO;
+ goto out;
+ }
+ /* We have to do free and alloc for the reply-free and reply-post
+ * counters to match - Need to review the reply FIFO handling.
+ */
+ mps_free_command(sc, cm);
+
+ if ((cm = mps_alloc_command(sc)) == NULL) {
+ printf("%s: command alloc failed @ line %d\n", __func__,
+ __LINE__);
+ error = EBUSY;
+ goto out;
+ }
+ request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
+ bzero(request, sizeof(MPI2_CONFIG_REQUEST));
+ request->Function = MPI2_FUNCTION_CONFIG;
+ request->Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ request->Header.PageType = MPI2_CONFIG_PAGETYPE_MANUFACTURING;
+ request->Header.PageNumber = 10;
+ request->Header.PageVersion = MPI2_MANUFACTURING10_PAGEVERSION;
+ request->Header.PageLength = mpi_reply->Header.PageLength;
+ cm->cm_length = le16toh(mpi_reply->Header.PageLength) * 4;
+ cm->cm_sge = &request->PageBufferSGE;
+ cm->cm_sglsize = sizeof(MPI2_SGE_IO_UNION);
+ cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE | MPS_CM_FLAGS_DATAIN;
+ cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+ page = malloc(MPS_MAN_PAGE10_SIZE, M_MPT2, M_ZERO | M_NOWAIT);
+ if (!page) {
+ printf("%s: page alloc failed\n", __func__);
+ error = ENOMEM;
+ goto out;
+ }
+ cm->cm_data = page;
+ error = mps_request_polled(sc, cm);
+ reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
+ if (error || (reply == NULL)) {
+ /* FIXME */
+ /* If the poll returns error then we need to do diag reset */
+ printf("%s: poll for page completed with error %d",
+ __func__, error);
+ error = ENXIO;
+ goto out;
+ }
+ ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+ bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ /* FIXME */
+ /* If the poll returns error then we need to do diag reset */
+ printf("%s: page read with error; iocstatus = 0x%x\n",
+ __func__, ioc_status);
+ error = ENXIO;
+ goto out;
+ }
+
+ /*
+ * If OEM ID is unknown, fail the request.
+ */
+ sc->WD_hide_expose = MPS_WD_HIDE_ALWAYS;
+ OEM_Value = (uint8_t)(page->ProductSpecificInfo & 0x000000FF);
+ if (OEM_Value != MPS_WD_LSI_OEM) {
+ mps_dprint(sc, MPS_FAULT, "Unknown OEM value for WarpDrive "
+ "(0x%x)\n", OEM_Value);
+ error = ENXIO;
+ goto out;
+ }
+
+ /*
+ * Set the phys disks hide/expose value.
+ */
+ pPS_info = &page->ProductSpecificInfo;
+ sc->WD_hide_expose = (uint8_t)(pPS_info[5]);
+ sc->WD_hide_expose &= MPS_WD_HIDE_EXPOSE_MASK;
+ if ((sc->WD_hide_expose != MPS_WD_HIDE_ALWAYS) &&
+ (sc->WD_hide_expose != MPS_WD_EXPOSE_ALWAYS) &&
+ (sc->WD_hide_expose != MPS_WD_HIDE_IF_VOLUME)) {
+ mps_dprint(sc, MPS_FAULT, "Unknown value for WarpDrive "
+ "hide/expose: 0x%x\n", sc->WD_hide_expose);
+ error = ENXIO;
+ goto out;
+ }
+
+out:
+ free(page, M_MPT2);
+ if (cm)
+ mps_free_command(sc, cm);
+ return (error);
+}
+
+/**
+ * mps_base_static_config_pages - static start of day config pages.
+ * @sc: per adapter object
+ *
+ * Return nothing.
+ */
+void
+mps_base_static_config_pages(struct mps_softc *sc)
+{
+ Mpi2ConfigReply_t mpi_reply;
+ int retry;
+
+ retry = 0;
+ while (mps_config_get_ioc_pg8(sc, &mpi_reply, &sc->ioc_pg8)) {
+ retry++;
+ if (retry > 5) {
+ /* We need to Handle this situation */
+ /*FIXME*/
+ break;
+ }
+ }
+}
+
+/**
+ * mps_wd_config_pages - get info required to support WarpDrive. This needs to
+ * be called after discovery is complete to guarentee that IR info is there.
+ * @sc: per adapter object
+ *
+ * Return nothing.
+ */
+void
+mps_wd_config_pages(struct mps_softc *sc)
+{
+ Mpi2ConfigReply_t mpi_reply;
+ pMpi2RaidVolPage0_t raid_vol_pg0 = NULL;
+ Mpi2RaidPhysDiskPage0_t phys_disk_pg0;
+ pMpi2RaidVol0PhysDisk_t pRVPD;
+ uint32_t stripe_size, phys_disk_page_address;
+ uint16_t block_size;
+ uint8_t index, stripe_exp = 0, block_exp = 0;
+
+ /*
+ * Get the WD settings from manufacturing page 10 if using a WD HBA.
+ * This will be used to determine if phys disks should always be
+ * hidden, hidden only if part of a WD volume, or never hidden. Also,
+ * get the WD RAID Volume info and fail if volume does not exist or if
+ * volume does not meet the requirements for a WD volume. No retry
+ * here. Just default to HIDE ALWAYS if man Page10 fails, or clear WD
+ * Valid flag if Volume info fails.
+ */
+ sc->WD_valid_config = FALSE;
+ if (sc->mps_flags & MPS_FLAGS_WD_AVAILABLE) {
+ if (mps_config_get_man_pg10(sc, &mpi_reply)) {
+ mps_dprint(sc, MPS_FAULT,
+ "mps_config_get_man_pg10 failed! Using 0 (Hide "
+ "Always) for WarpDrive hide/expose value.\n");
+ sc->WD_hide_expose = MPS_WD_HIDE_ALWAYS;
+ }
+
+ /*
+ * Get first RAID Volume Page0 using GET_NEXT_HANDLE.
+ */
+ raid_vol_pg0 = malloc(sizeof(Mpi2RaidVolPage0_t) +
+ (sizeof(Mpi2RaidVol0PhysDisk_t) * MPS_MAX_DISKS_IN_VOL),
+ M_MPT2, M_ZERO | M_NOWAIT);
+ if (!raid_vol_pg0) {
+ printf("%s: page alloc failed\n", __func__);
+ goto out;
+ }
+
+ if (mps_config_get_raid_volume_pg0(sc, &mpi_reply, raid_vol_pg0,
+ 0x0000FFFF)) {
+ mps_dprint(sc, MPS_INFO,
+ "mps_config_get_raid_volume_pg0 failed! Assuming "
+ "WarpDrive IT mode.\n");
+ goto out;
+ }
+
+ /*
+ * Check for valid WD configuration:
+ * volume type is RAID0
+ * number of phys disks in the volume is no more than 8
+ */
+ if ((raid_vol_pg0->VolumeType != MPI2_RAID_VOL_TYPE_RAID0) ||
+ (raid_vol_pg0->NumPhysDisks > 8)) {
+ mps_dprint(sc, MPS_FAULT,
+ "Invalid WarpDrive configuration. Direct Drive I/O "
+ "will not be used.\n");
+ goto out;
+ }
+
+ /*
+ * Save the WD RAID data to be used during WD I/O.
+ */
+ sc->DD_max_lba = le64toh((uint64_t)raid_vol_pg0->MaxLBA.High <<
+ 32 | (uint64_t)raid_vol_pg0->MaxLBA.Low);
+ sc->DD_num_phys_disks = raid_vol_pg0->NumPhysDisks;
+ sc->DD_dev_handle = raid_vol_pg0->DevHandle;
+ sc->DD_stripe_size = raid_vol_pg0->StripeSize;
+ sc->DD_block_size = raid_vol_pg0->BlockSize;
+
+ /*
+ * Find power of 2 of stripe size and set this as the exponent.
+ * Fail if stripe size is 0.
+ */
+ stripe_size = raid_vol_pg0->StripeSize;
+ for (index = 0; index < 32; index++) {
+ if (stripe_size & 1)
+ break;
+ stripe_exp++;
+ stripe_size >>= 1;
+ }
+ if (index == 32) {
+ mps_dprint(sc, MPS_FAULT,
+ "RAID Volume's stripe size is 0. Direct Drive I/O "
+ "will not be used.\n");
+ goto out;
+ }
+ sc->DD_stripe_exponent = stripe_exp;
+
+ /*
+ * Find power of 2 of block size and set this as the exponent.
+ * Fail if block size is 0.
+ */
+ block_size = raid_vol_pg0->BlockSize;
+ for (index = 0; index < 16; index++) {
+ if (block_size & 1)
+ break;
+ block_exp++;
+ block_size >>= 1;
+ }
+ if (index == 16) {
+ mps_dprint(sc, MPS_FAULT,
+ "RAID Volume's block size is 0. Direct Drive I/O "
+ "will not be used.\n");
+ goto out;
+ }
+ sc->DD_block_exponent = block_exp;
+
+ /*
+ * Loop through all of the volume's Phys Disks to map the phys
+ * disk number into the columm map. This is used during Direct
+ * Drive I/O to send the request to the correct SSD.
+ */
+ pRVPD = (pMpi2RaidVol0PhysDisk_t)&raid_vol_pg0->PhysDisk;
+ for (index = 0; index < raid_vol_pg0->NumPhysDisks; index++) {
+ sc->DD_column_map[pRVPD->PhysDiskMap].phys_disk_num =
+ pRVPD->PhysDiskNum;
+ pRVPD++;
+ }
+
+ /*
+ * Get second RAID Volume Page0 using previous handle. This
+ * page should not exist. If it does, must not proceed with WD
+ * handling.
+ */
+ if (mps_config_get_raid_volume_pg0(sc, &mpi_reply,
+ raid_vol_pg0, (u32)raid_vol_pg0->DevHandle)) {
+ if (mpi_reply.IOCStatus !=
+ MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) {
+ mps_dprint(sc, MPS_FAULT,
+ "Multiple RAID Volume Page0! Direct Drive "
+ "I/O will not be used.\n");
+ goto out;
+ }
+ } else {
+ mps_dprint(sc, MPS_FAULT,
+ "Multiple volumes! Direct Drive I/O will not be "
+ "used.\n");
+ goto out;
+ }
+
+ /*
+ * Get RAID Volume Phys Disk Page 0 for all SSDs in the volume.
+ */
+ for (index = 0; index < raid_vol_pg0->NumPhysDisks; index++) {
+ phys_disk_page_address =
+ MPI2_PHYSDISK_PGAD_FORM_PHYSDISKNUM +
+ sc->DD_column_map[index].phys_disk_num;
+ if (mps_config_get_raid_pd_pg0(sc, &mpi_reply,
+ &phys_disk_pg0, phys_disk_page_address)) {
+ mps_dprint(sc, MPS_FAULT,
+ "mps_config_get_raid_pd_pg0 failed! Direct "
+ "Drive I/O will not be used.\n");
+ goto out;
+ }
+ if (phys_disk_pg0.DevHandle == 0xFFFF) {
+ mps_dprint(sc, MPS_FAULT,
+ "Invalid Phys Disk DevHandle! Direct Drive "
+ "I/O will not be used.\n");
+ goto out;
+ }
+ sc->DD_column_map[index].dev_handle =
+ phys_disk_pg0.DevHandle;
+ }
+ sc->WD_valid_config = TRUE;
+out:
+ if (raid_vol_pg0)
+ free(raid_vol_pg0, M_MPT2);
+ }
+}
+
+/**
+ * mps_config_get_dpm_pg0 - obtain driver persistent mapping page0
+ * @sc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @sz: size of buffer passed in config_page
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mps_config_get_dpm_pg0(struct mps_softc *sc, Mpi2ConfigReply_t *mpi_reply,
+ Mpi2DriverMappingPage0_t *config_page, u16 sz)
+{
+ MPI2_CONFIG_REQUEST *request;
+ MPI2_CONFIG_REPLY *reply;
+ struct mps_command *cm;
+ Mpi2DriverMappingPage0_t *page = NULL;
+ int error = 0;
+ u16 ioc_status;
+
+ mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+
+ memset(config_page, 0, sz);
+ if ((cm = mps_alloc_command(sc)) == NULL) {
+ printf("%s: command alloc failed @ line %d\n", __func__,
+ __LINE__);
+ error = EBUSY;
+ goto out;
+ }
+ request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
+ bzero(request, sizeof(MPI2_CONFIG_REQUEST));
+ request->Function = MPI2_FUNCTION_CONFIG;
+ request->Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ request->Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ request->ExtPageType = MPI2_CONFIG_EXTPAGETYPE_DRIVER_MAPPING;
+ request->Header.PageNumber = 0;
+ request->Header.PageVersion = MPI2_DRIVERMAPPING0_PAGEVERSION;
+ request->PageAddress = sc->max_dpm_entries <<
+ MPI2_DPM_PGAD_ENTRY_COUNT_SHIFT;
+ cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+ cm->cm_data = NULL;
+ error = mps_request_polled(sc, cm);
+ reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
+ if (error || (reply == NULL)) {
+ /* FIXME */
+ /* If the poll returns error then we need to do diag reset */
+ printf("%s: poll for header completed with error %d",
+ __func__, error);
+ error = ENXIO;
+ goto out;
+ }
+ ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+ bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ /* FIXME */
+ /* If the poll returns error then we need to do diag reset */
+ printf("%s: header read with error; iocstatus = 0x%x\n",
+ __func__, ioc_status);
+ error = ENXIO;
+ goto out;
+ }
+ /* We have to do free and alloc for the reply-free and reply-post
+ * counters to match - Need to review the reply FIFO handling.
+ */
+ mps_free_command(sc, cm);
+
+ if ((cm = mps_alloc_command(sc)) == NULL) {
+ printf("%s: command alloc failed @ line %d\n", __func__,
+ __LINE__);
+ error = EBUSY;
+ goto out;
+ }
+ request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
+ bzero(request, sizeof(MPI2_CONFIG_REQUEST));
+ request->Function = MPI2_FUNCTION_CONFIG;
+ request->Action = MPI2_CONFIG_ACTION_PAGE_READ_NVRAM;
+ request->Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ request->ExtPageType = MPI2_CONFIG_EXTPAGETYPE_DRIVER_MAPPING;
+ request->Header.PageNumber = 0;
+ request->Header.PageVersion = MPI2_DRIVERMAPPING0_PAGEVERSION;
+ request->PageAddress = sc->max_dpm_entries <<
+ MPI2_DPM_PGAD_ENTRY_COUNT_SHIFT;
+ request->ExtPageLength = mpi_reply->ExtPageLength;
+ cm->cm_length = le16toh(request->ExtPageLength) * 4;
+ cm->cm_sge = &request->PageBufferSGE;
+ cm->cm_sglsize = sizeof(MPI2_SGE_IO_UNION);
+ cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE | MPS_CM_FLAGS_DATAIN;
+ cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+ page = malloc(cm->cm_length, M_MPT2, M_ZERO|M_NOWAIT);
+ if (!page) {
+ printf("%s: page alloc failed\n", __func__);
+ error = ENOMEM;
+ goto out;
+ }
+ cm->cm_data = page;
+ error = mps_request_polled(sc, cm);
+ reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
+ if (error || (reply == NULL)) {
+ /* FIXME */
+ /* If the poll returns error then we need to do diag reset */
+ printf("%s: poll for page completed with error %d",
+ __func__, error);
+ error = ENXIO;
+ goto out;
+ }
+ ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+ bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ /* FIXME */
+ /* If the poll returns error then we need to do diag reset */
+ printf("%s: page read with error; iocstatus = 0x%x\n",
+ __func__, ioc_status);
+ error = ENXIO;
+ goto out;
+ }
+ bcopy(page, config_page, MIN(cm->cm_length, sz));
+out:
+ free(page, M_MPT2);
+ if (cm)
+ mps_free_command(sc, cm);
+ return (error);
+}
+
+/**
+ * mps_config_set_dpm_pg0 - write an entry in driver persistent mapping page0
+ * @sc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @entry_idx: entry index in DPM Page0 to be modified
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+
+int mps_config_set_dpm_pg0(struct mps_softc *sc, Mpi2ConfigReply_t *mpi_reply,
+ Mpi2DriverMappingPage0_t *config_page, u16 entry_idx)
+{
+ MPI2_CONFIG_REQUEST *request;
+ MPI2_CONFIG_REPLY *reply;
+ struct mps_command *cm;
+ MPI2_CONFIG_PAGE_DRIVER_MAPPING_0 *page = NULL;
+ int error = 0;
+ u16 ioc_status;
+
+ mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+
+ if ((cm = mps_alloc_command(sc)) == NULL) {
+ printf("%s: command alloc failed @ line %d\n", __func__,
+ __LINE__);
+ error = EBUSY;
+ goto out;
+ }
+ request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
+ bzero(request, sizeof(MPI2_CONFIG_REQUEST));
+ request->Function = MPI2_FUNCTION_CONFIG;
+ request->Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ request->Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ request->ExtPageType = MPI2_CONFIG_EXTPAGETYPE_DRIVER_MAPPING;
+ request->Header.PageNumber = 0;
+ request->Header.PageVersion = MPI2_DRIVERMAPPING0_PAGEVERSION;
+ request->PageAddress = 1 << MPI2_DPM_PGAD_ENTRY_COUNT_SHIFT;
+ request->PageAddress |= htole16(entry_idx);
+ cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+ cm->cm_data = NULL;
+ error = mps_request_polled(sc, cm);
+ reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
+ if (error || (reply == NULL)) {
+ /* FIXME */
+ /* If the poll returns error then we need to do diag reset */
+ printf("%s: poll for header completed with error %d",
+ __func__, error);
+ error = ENXIO;
+ goto out;
+ }
+ ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+ bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ /* FIXME */
+ /* If the poll returns error then we need to do diag reset */
+ printf("%s: header read with error; iocstatus = 0x%x\n",
+ __func__, ioc_status);
+ error = ENXIO;
+ goto out;
+ }
+ /* We have to do free and alloc for the reply-free and reply-post
+ * counters to match - Need to review the reply FIFO handling.
+ */
+ mps_free_command(sc, cm);
+
+ if ((cm = mps_alloc_command(sc)) == NULL) {
+ printf("%s: command alloc failed @ line %d\n", __func__,
+ __LINE__);
+ error = EBUSY;
+ goto out;
+ }
+ request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
+ bzero(request, sizeof(MPI2_CONFIG_REQUEST));
+ request->Function = MPI2_FUNCTION_CONFIG;
+ request->Action = MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM;
+ request->Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ request->ExtPageType = MPI2_CONFIG_EXTPAGETYPE_DRIVER_MAPPING;
+ request->Header.PageNumber = 0;
+ request->Header.PageVersion = MPI2_DRIVERMAPPING0_PAGEVERSION;
+ request->ExtPageLength = mpi_reply->ExtPageLength;
+ request->PageAddress = 1 << MPI2_DPM_PGAD_ENTRY_COUNT_SHIFT;
+ request->PageAddress |= htole16(entry_idx);
+ cm->cm_length = le16toh(mpi_reply->ExtPageLength) * 4;
+ cm->cm_sge = &request->PageBufferSGE;
+ cm->cm_sglsize = sizeof(MPI2_SGE_IO_UNION);
+ cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE | MPS_CM_FLAGS_DATAOUT;
+ cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+ page = malloc(cm->cm_length, M_MPT2, M_ZERO | M_NOWAIT);
+ if (!page) {
+ printf("%s: page alloc failed\n", __func__);
+ error = ENOMEM;
+ goto out;
+ }
+ bcopy(config_page, page, MIN(cm->cm_length,
+ (sizeof(Mpi2DriverMappingPage0_t))));
+ cm->cm_data = page;
+ error = mps_request_polled(sc, cm);
+ reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
+ if (error || (reply == NULL)) {
+ /* FIXME */
+ /* If the poll returns error then we need to do diag reset */
+ printf("%s: poll for page completed with error %d",
+ __func__, error);
+ error = ENXIO;
+ goto out;
+ }
+ ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+ bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ /* FIXME */
+ /* If the poll returns error then we need to do diag reset */
+ printf("%s: page written with error; iocstatus = 0x%x\n",
+ __func__, ioc_status);
+ error = ENXIO;
+ goto out;
+ }
+out:
+ free(page, M_MPT2);
+ if (cm)
+ mps_free_command(sc, cm);
+ return (error);
+}
+
+/**
+ * mps_config_get_sas_device_pg0 - obtain sas device page 0
+ * @sc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @form: GET_NEXT_HANDLE or HANDLE
+ * @handle: device handle
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mps_config_get_sas_device_pg0(struct mps_softc *sc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2SasDevicePage0_t *config_page, u32 form, u16 handle)
+{
+ MPI2_CONFIG_REQUEST *request;
+ MPI2_CONFIG_REPLY *reply;
+ struct mps_command *cm;
+ Mpi2SasDevicePage0_t *page = NULL;
+ int error = 0;
+ u16 ioc_status;
+
+ mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+
+ if ((cm = mps_alloc_command(sc)) == NULL) {
+ printf("%s: command alloc failed @ line %d\n", __func__,
+ __LINE__);
+ error = EBUSY;
+ goto out;
+ }
+ request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
+ bzero(request, sizeof(MPI2_CONFIG_REQUEST));
+ request->Function = MPI2_FUNCTION_CONFIG;
+ request->Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ request->Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ request->ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE;
+ request->Header.PageNumber = 0;
+ request->Header.PageVersion = MPI2_SASDEVICE0_PAGEVERSION;
+ cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+ cm->cm_data = NULL;
+ error = mps_request_polled(sc, cm);
+ reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
+ if (error || (reply == NULL)) {
+ /* FIXME */
+ /* If the poll returns error then we need to do diag reset */
+ printf("%s: poll for header completed with error %d",
+ __func__, error);
+ error = ENXIO;
+ goto out;
+ }
+ ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+ bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ /* FIXME */
+ /* If the poll returns error then we need to do diag reset */
+ printf("%s: header read with error; iocstatus = 0x%x\n",
+ __func__, ioc_status);
+ error = ENXIO;
+ goto out;
+ }
+ /* We have to do free and alloc for the reply-free and reply-post
+ * counters to match - Need to review the reply FIFO handling.
+ */
+ mps_free_command(sc, cm);
+
+ if ((cm = mps_alloc_command(sc)) == NULL) {
+ printf("%s: command alloc failed @ line %d\n", __func__,
+ __LINE__);
+ error = EBUSY;
+ goto out;
+ }
+ request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
+ bzero(request, sizeof(MPI2_CONFIG_REQUEST));
+ request->Function = MPI2_FUNCTION_CONFIG;
+ request->Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ request->Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ request->ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE;
+ request->Header.PageNumber = 0;
+ request->Header.PageVersion = MPI2_SASDEVICE0_PAGEVERSION;
+ request->ExtPageLength = mpi_reply->ExtPageLength;
+ request->PageAddress = htole32(form | handle);
+ cm->cm_length = le16toh(mpi_reply->ExtPageLength) * 4;
+ cm->cm_sge = &request->PageBufferSGE;
+ cm->cm_sglsize = sizeof(MPI2_SGE_IO_UNION);
+ cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE | MPS_CM_FLAGS_DATAIN;
+ cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+ page = malloc(cm->cm_length, M_MPT2, M_ZERO | M_NOWAIT);
+ if (!page) {
+ printf("%s: page alloc failed\n", __func__);
+ error = ENOMEM;
+ goto out;
+ }
+ cm->cm_data = page;
+
+ error = mps_request_polled(sc, cm);
+ reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
+ if (error || (reply == NULL)) {
+ /* FIXME */
+ /* If the poll returns error then we need to do diag reset */
+ printf("%s: poll for page completed with error %d",
+ __func__, error);
+ error = ENXIO;
+ goto out;
+ }
+ ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+ bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ /* FIXME */
+ /* If the poll returns error then we need to do diag reset */
+ printf("%s: page read with error; iocstatus = 0x%x\n",
+ __func__, ioc_status);
+ error = ENXIO;
+ goto out;
+ }
+ bcopy(page, config_page, MIN(cm->cm_length,
+ sizeof(Mpi2SasDevicePage0_t)));
+out:
+ free(page, M_MPT2);
+ if (cm)
+ mps_free_command(sc, cm);
+ return (error);
+}
+
+/**
+ * mps_config_get_bios_pg3 - obtain BIOS page 3
+ * @sc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mps_config_get_bios_pg3(struct mps_softc *sc, Mpi2ConfigReply_t *mpi_reply,
+ Mpi2BiosPage3_t *config_page)
+{
+ MPI2_CONFIG_REQUEST *request;
+ MPI2_CONFIG_REPLY *reply;
+ struct mps_command *cm;
+ Mpi2BiosPage3_t *page = NULL;
+ int error = 0;
+ u16 ioc_status;
+
+ mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+
+ if ((cm = mps_alloc_command(sc)) == NULL) {
+ printf("%s: command alloc failed @ line %d\n", __func__,
+ __LINE__);
+ error = EBUSY;
+ goto out;
+ }
+ request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
+ bzero(request, sizeof(MPI2_CONFIG_REQUEST));
+ request->Function = MPI2_FUNCTION_CONFIG;
+ request->Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ request->Header.PageType = MPI2_CONFIG_PAGETYPE_BIOS;
+ request->Header.PageNumber = 3;
+ request->Header.PageVersion = MPI2_BIOSPAGE3_PAGEVERSION;
+ cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+ cm->cm_data = NULL;
+ error = mps_request_polled(sc, cm);
+ reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
+ if (error || (reply == NULL)) {
+ /* FIXME */
+ /* If the poll returns error then we need to do diag reset */
+ printf("%s: poll for header completed with error %d",
+ __func__, error);
+ error = ENXIO;
+ goto out;
+ }
+ ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+ bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ /* FIXME */
+ /* If the poll returns error then we need to do diag reset */
+ printf("%s: header read with error; iocstatus = 0x%x\n",
+ __func__, ioc_status);
+ error = ENXIO;
+ goto out;
+ }
+ /* We have to do free and alloc for the reply-free and reply-post
+ * counters to match - Need to review the reply FIFO handling.
+ */
+ mps_free_command(sc, cm);
+
+ if ((cm = mps_alloc_command(sc)) == NULL) {
+ printf("%s: command alloc failed @ line %d\n", __func__,
+ __LINE__);
+ error = EBUSY;
+ goto out;
+ }
+ request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
+ bzero(request, sizeof(MPI2_CONFIG_REQUEST));
+ request->Function = MPI2_FUNCTION_CONFIG;
+ request->Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ request->Header.PageType = MPI2_CONFIG_PAGETYPE_BIOS;
+ request->Header.PageNumber = 3;
+ request->Header.PageVersion = MPI2_BIOSPAGE3_PAGEVERSION;
+ request->Header.PageLength = mpi_reply->Header.PageLength;
+ cm->cm_length = le16toh(mpi_reply->Header.PageLength) * 4;
+ cm->cm_sge = &request->PageBufferSGE;
+ cm->cm_sglsize = sizeof(MPI2_SGE_IO_UNION);
+ cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE | MPS_CM_FLAGS_DATAIN;
+ cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+ page = malloc(cm->cm_length, M_MPT2, M_ZERO | M_NOWAIT);
+ if (!page) {
+ printf("%s: page alloc failed\n", __func__);
+ error = ENOMEM;
+ goto out;
+ }
+ cm->cm_data = page;
+
+ error = mps_request_polled(sc, cm);
+ reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
+ if (error || (reply == NULL)) {
+ /* FIXME */
+ /* If the poll returns error then we need to do diag reset */
+ printf("%s: poll for page completed with error %d",
+ __func__, error);
+ error = ENXIO;
+ goto out;
+ }
+ ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+ bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ /* FIXME */
+ /* If the poll returns error then we need to do diag reset */
+ printf("%s: page read with error; iocstatus = 0x%x\n",
+ __func__, ioc_status);
+ error = ENXIO;
+ goto out;
+ }
+ bcopy(page, config_page, MIN(cm->cm_length, sizeof(Mpi2BiosPage3_t)));
+out:
+ free(page, M_MPT2);
+ if (cm)
+ mps_free_command(sc, cm);
+ return (error);
+}
+
+/**
+ * mps_config_get_raid_volume_pg0 - obtain raid volume page 0
+ * @sc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @page_address: form and handle value used to get page
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mps_config_get_raid_volume_pg0(struct mps_softc *sc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2RaidVolPage0_t *config_page, u32 page_address)
+{
+ MPI2_CONFIG_REQUEST *request;
+ MPI2_CONFIG_REPLY *reply;
+ struct mps_command *cm;
+ Mpi2RaidVolPage0_t *page = NULL;
+ int error = 0;
+ u16 ioc_status;
+
+ mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+
+ if ((cm = mps_alloc_command(sc)) == NULL) {
+ printf("%s: command alloc failed @ line %d\n", __func__,
+ __LINE__);
+ error = EBUSY;
+ goto out;
+ }
+ request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
+ bzero(request, sizeof(MPI2_CONFIG_REQUEST));
+ request->Function = MPI2_FUNCTION_CONFIG;
+ request->Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ request->Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_VOLUME;
+ request->Header.PageNumber = 0;
+ request->Header.PageVersion = MPI2_RAIDVOLPAGE0_PAGEVERSION;
+ cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+ cm->cm_data = NULL;
+ error = mps_request_polled(sc, cm);
+ reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
+ if (error || (reply == NULL)) {
+ /* FIXME */
+ /* If the poll returns error then we need to do diag reset */
+ printf("%s: poll for header completed with error %d",
+ __func__, error);
+ error = ENXIO;
+ goto out;
+ }
+ ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+ bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ /* FIXME */
+ /* If the poll returns error then we need to do diag reset */
+ printf("%s: header read with error; iocstatus = 0x%x\n",
+ __func__, ioc_status);
+ error = ENXIO;
+ goto out;
+ }
+ /* We have to do free and alloc for the reply-free and reply-post
+ * counters to match - Need to review the reply FIFO handling.
+ */
+ mps_free_command(sc, cm);
+
+ if ((cm = mps_alloc_command(sc)) == NULL) {
+ printf("%s: command alloc failed @ line %d\n", __func__,
+ __LINE__);
+ error = EBUSY;
+ goto out;
+ }
+ request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
+ bzero(request, sizeof(MPI2_CONFIG_REQUEST));
+ request->Function = MPI2_FUNCTION_CONFIG;
+ request->Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ request->Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_VOLUME;
+ request->Header.PageNumber = 0;
+ request->Header.PageLength = mpi_reply->Header.PageLength;
+ request->Header.PageVersion = mpi_reply->Header.PageVersion;
+ request->PageAddress = page_address;
+ cm->cm_length = le16toh(mpi_reply->Header.PageLength) * 4;
+ cm->cm_sge = &request->PageBufferSGE;
+ cm->cm_sglsize = sizeof(MPI2_SGE_IO_UNION);
+ cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE | MPS_CM_FLAGS_DATAIN;
+ cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+ page = malloc(cm->cm_length, M_MPT2, M_ZERO | M_NOWAIT);
+ if (!page) {
+ printf("%s: page alloc failed\n", __func__);
+ error = ENOMEM;
+ goto out;
+ }
+ cm->cm_data = page;
+
+ error = mps_request_polled(sc, cm);
+ reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
+ if (error || (reply == NULL)) {
+ /* FIXME */
+ /* If the poll returns error then we need to do diag reset */
+ printf("%s: poll for page completed with error %d",
+ __func__, error);
+ error = ENXIO;
+ goto out;
+ }
+ ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+ bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ /* FIXME */
+ /* If the poll returns error then we need to do diag reset */
+ printf("%s: page read with error; iocstatus = 0x%x\n",
+ __func__, ioc_status);
+ error = ENXIO;
+ goto out;
+ }
+ bcopy(page, config_page, cm->cm_length);
+out:
+ free(page, M_MPT2);
+ if (cm)
+ mps_free_command(sc, cm);
+ return (error);
+}
+
+/**
+ * mps_config_get_raid_volume_pg1 - obtain raid volume page 1
+ * @sc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @form: GET_NEXT_HANDLE or HANDLE
+ * @handle: volume handle
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mps_config_get_raid_volume_pg1(struct mps_softc *sc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2RaidVolPage1_t *config_page, u32 form, u16 handle)
+{
+ MPI2_CONFIG_REQUEST *request;
+ MPI2_CONFIG_REPLY *reply;
+ struct mps_command *cm;
+ Mpi2RaidVolPage1_t *page = NULL;
+ int error = 0;
+ u16 ioc_status;
+
+ mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+
+ if ((cm = mps_alloc_command(sc)) == NULL) {
+ printf("%s: command alloc failed @ line %d\n", __func__,
+ __LINE__);
+ error = EBUSY;
+ goto out;
+ }
+ request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
+ bzero(request, sizeof(MPI2_CONFIG_REQUEST));
+ request->Function = MPI2_FUNCTION_CONFIG;
+ request->Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ request->Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_VOLUME;
+ request->Header.PageNumber = 1;
+ request->Header.PageVersion = MPI2_RAIDVOLPAGE1_PAGEVERSION;
+ cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+ cm->cm_data = NULL;
+ error = mps_request_polled(sc, cm);
+ reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
+ if (error || (reply == NULL)) {
+ /* FIXME */
+ /* If the poll returns error then we need to do diag reset */
+ printf("%s: poll for header completed with error %d",
+ __func__, error);
+ error = ENXIO;
+ goto out;
+ }
+ ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+ bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ /* FIXME */
+ /* If the poll returns error then we need to do diag reset */
+ printf("%s: header read with error; iocstatus = 0x%x\n",
+ __func__, ioc_status);
+ error = ENXIO;
+ goto out;
+ }
+ /* We have to do free and alloc for the reply-free and reply-post
+ * counters to match - Need to review the reply FIFO handling.
+ */
+ mps_free_command(sc, cm);
+
+ if ((cm = mps_alloc_command(sc)) == NULL) {
+ printf("%s: command alloc failed @ line %d\n", __func__,
+ __LINE__);
+ error = EBUSY;
+ goto out;
+ }
+ request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
+ bzero(request, sizeof(MPI2_CONFIG_REQUEST));
+ request->Function = MPI2_FUNCTION_CONFIG;
+ request->Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ request->Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_VOLUME;
+ request->Header.PageNumber = 1;
+ request->Header.PageLength = mpi_reply->Header.PageLength;
+ request->Header.PageVersion = mpi_reply->Header.PageVersion;
+ request->PageAddress = htole32(form | handle);
+ cm->cm_length = le16toh(mpi_reply->Header.PageLength) * 4;
+ cm->cm_sge = &request->PageBufferSGE;
+ cm->cm_sglsize = sizeof(MPI2_SGE_IO_UNION);
+ cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE | MPS_CM_FLAGS_DATAIN;
+ cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+ page = malloc(cm->cm_length, M_MPT2, M_ZERO | M_NOWAIT);
+ if (!page) {
+ printf("%s: page alloc failed\n", __func__);
+ error = ENOMEM;
+ goto out;
+ }
+ cm->cm_data = page;
+
+ error = mps_request_polled(sc, cm);
+ reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
+ if (error || (reply == NULL)) {
+ /* FIXME */
+ /* If the poll returns error then we need to do diag reset */
+ printf("%s: poll for page completed with error %d",
+ __func__, error);
+ error = ENXIO;
+ goto out;
+ }
+ ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+ bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ /* FIXME */
+ /* If the poll returns error then we need to do diag reset */
+ printf("%s: page read with error; iocstatus = 0x%x\n",
+ __func__, ioc_status);
+ error = ENXIO;
+ goto out;
+ }
+ bcopy(page, config_page, MIN(cm->cm_length,
+ sizeof(Mpi2RaidVolPage1_t)));
+out:
+ free(page, M_MPT2);
+ if (cm)
+ mps_free_command(sc, cm);
+ return (error);
+}
+
+/**
+ * mps_config_get_volume_wwid - returns wwid given the volume handle
+ * @sc: per adapter object
+ * @volume_handle: volume handle
+ * @wwid: volume wwid
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mps_config_get_volume_wwid(struct mps_softc *sc, u16 volume_handle, u64 *wwid)
+{
+ Mpi2ConfigReply_t mpi_reply;
+ Mpi2RaidVolPage1_t raid_vol_pg1;
+
+ *wwid = 0;
+ if (!(mps_config_get_raid_volume_pg1(sc, &mpi_reply, &raid_vol_pg1,
+ MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, volume_handle))) {
+ *wwid = le64toh((u64)raid_vol_pg1.WWID.High << 32 |
+ raid_vol_pg1.WWID.Low);
+ return 0;
+ } else
+ return -1;
+}
+
+/**
+ * mps_config_get_pd_pg0 - obtain raid phys disk page 0
+ * @sc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @page_address: form and handle value used to get page
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mps_config_get_raid_pd_pg0(struct mps_softc *sc, Mpi2ConfigReply_t *mpi_reply,
+ Mpi2RaidPhysDiskPage0_t *config_page, u32 page_address)
+{
+ MPI2_CONFIG_REQUEST *request;
+ MPI2_CONFIG_REPLY *reply;
+ struct mps_command *cm;
+ Mpi2RaidPhysDiskPage0_t *page = NULL;
+ int error = 0;
+ u16 ioc_status;
+
+ mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+
+ if ((cm = mps_alloc_command(sc)) == NULL) {
+ printf("%s: command alloc failed @ line %d\n", __func__,
+ __LINE__);
+ error = EBUSY;
+ goto out;
+ }
+ request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
+ bzero(request, sizeof(MPI2_CONFIG_REQUEST));
+ request->Function = MPI2_FUNCTION_CONFIG;
+ request->Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ request->Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_PHYSDISK;
+ request->Header.PageNumber = 0;
+ request->Header.PageVersion = MPI2_RAIDPHYSDISKPAGE0_PAGEVERSION;
+ cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+ cm->cm_data = NULL;
+ error = mps_request_polled(sc, cm);
+ reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
+ if (error || (reply == NULL)) {
+ /* FIXME */
+ /* If the poll returns error then we need to do diag reset */
+ printf("%s: poll for header completed with error %d",
+ __func__, error);
+ error = ENXIO;
+ goto out;
+ }
+ ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+ bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ /* FIXME */
+ /* If the poll returns error then we need to do diag reset */
+ printf("%s: header read with error; iocstatus = 0x%x\n",
+ __func__, ioc_status);
+ error = ENXIO;
+ goto out;
+ }
+ /* We have to do free and alloc for the reply-free and reply-post
+ * counters to match - Need to review the reply FIFO handling.
+ */
+ mps_free_command(sc, cm);
+
+ if ((cm = mps_alloc_command(sc)) == NULL) {
+ printf("%s: command alloc failed @ line %d\n", __func__,
+ __LINE__);
+ error = EBUSY;
+ goto out;
+ }
+ request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
+ bzero(request, sizeof(MPI2_CONFIG_REQUEST));
+ request->Function = MPI2_FUNCTION_CONFIG;
+ request->Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ request->Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_PHYSDISK;
+ request->Header.PageNumber = 0;
+ request->Header.PageLength = mpi_reply->Header.PageLength;
+ request->Header.PageVersion = mpi_reply->Header.PageVersion;
+ request->PageAddress = page_address;
+ cm->cm_length = le16toh(mpi_reply->Header.PageLength) * 4;
+ cm->cm_sge = &request->PageBufferSGE;
+ cm->cm_sglsize = sizeof(MPI2_SGE_IO_UNION);
+ cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE | MPS_CM_FLAGS_DATAIN;
+ cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+ page = malloc(cm->cm_length, M_MPT2, M_ZERO | M_NOWAIT);
+ if (!page) {
+ printf("%s: page alloc failed\n", __func__);
+ error = ENOMEM;
+ goto out;
+ }
+ cm->cm_data = page;
+
+ error = mps_request_polled(sc, cm);
+ reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
+ if (error || (reply == NULL)) {
+ /* FIXME */
+ /* If the poll returns error then we need to do diag reset */
+ printf("%s: poll for page completed with error %d",
+ __func__, error);
+ error = ENXIO;
+ goto out;
+ }
+ ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+ bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ /* FIXME */
+ /* If the poll returns error then we need to do diag reset */
+ printf("%s: page read with error; iocstatus = 0x%x\n",
+ __func__, ioc_status);
+ error = ENXIO;
+ goto out;
+ }
+ bcopy(page, config_page, MIN(cm->cm_length,
+ sizeof(Mpi2RaidPhysDiskPage0_t)));
+out:
+ free(page, M_MPT2);
+ if (cm)
+ mps_free_command(sc, cm);
+ return (error);
+}
diff --git a/sys/dev/mps/mps_ioctl.h b/sys/dev/mps/mps_ioctl.h
index 811b13e..cc28337 100644
--- a/sys/dev/mps/mps_ioctl.h
+++ b/sys/dev/mps/mps_ioctl.h
@@ -31,6 +31,35 @@
*
* $FreeBSD$
*/
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ *
+ * $FreeBSD$
+ */
#ifndef _MPS_IOCTL_H_
#define _MPS_IOCTL_H_
@@ -93,8 +122,237 @@ struct mps_usr_command {
uint32_t flags;
};
-#define MPSIO_MPS_COMMAND_FLAG_VERBOSE 0x01
-#define MPSIO_MPS_COMMAND_FLAG_DEBUG 0x02
+typedef struct mps_pci_bits
+{
+ union {
+ struct {
+ uint32_t DeviceNumber :5;
+ uint32_t FunctionNumber :3;
+ uint32_t BusNumber :24;
+ } bits;
+ uint32_t AsDWORD;
+ } u;
+ uint32_t PciSegmentId;
+} mps_pci_bits_t;
+
+/*
+ * The following is the MPSIOCTL_GET_ADAPTER_DATA data structure. This data
+ * structure is setup so that we hopefully are properly aligned for both
+ * 32-bit and 64-bit mode applications.
+ *
+ * Adapter Type - Value = 4 = SCSI Protocol through SAS-2 adapter
+ *
+ * MPI Port Number - The PCI Function number for this device
+ *
+ * PCI Device HW Id - The PCI device number for this device
+ *
+ */
+#define MPSIOCTL_ADAPTER_TYPE_SAS2 4
+#define MPSIOCTL_ADAPTER_TYPE_SAS2_SSS6200 5
+typedef struct mps_adapter_data
+{
+ uint32_t StructureLength;
+ uint32_t AdapterType;
+ uint32_t MpiPortNumber;
+ uint32_t PCIDeviceHwId;
+ uint32_t PCIDeviceHwRev;
+ uint32_t SubSystemId;
+ uint32_t SubsystemVendorId;
+ uint32_t Reserved1;
+ uint32_t MpiFirmwareVersion;
+ uint32_t BiosVersion;
+ uint8_t DriverVersion[32];
+ uint8_t Reserved2;
+ uint8_t ScsiId;
+ uint16_t Reserved3;
+ mps_pci_bits_t PciInformation;
+} mps_adapter_data_t;
+
+
+typedef struct mps_update_flash
+{
+ uint64_t PtrBuffer;
+ uint32_t ImageChecksum;
+ uint32_t ImageOffset;
+ uint32_t ImageSize;
+ uint32_t ImageType;
+} mps_update_flash_t;
+
+
+#define MPS_PASS_THRU_DIRECTION_NONE 0
+#define MPS_PASS_THRU_DIRECTION_READ 1
+#define MPS_PASS_THRU_DIRECTION_WRITE 2
+#define MPS_PASS_THRU_DIRECTION_BOTH 3
+
+typedef struct mps_pass_thru
+{
+ uint64_t PtrRequest;
+ uint64_t PtrReply;
+ uint64_t PtrData;
+ uint32_t RequestSize;
+ uint32_t ReplySize;
+ uint32_t DataSize;
+ uint32_t DataDirection;
+ uint64_t PtrDataOut;
+ uint32_t DataOutSize;
+ uint32_t Timeout;
+} mps_pass_thru_t;
+
+
+/*
+ * Event queue defines
+ */
+#define MPS_EVENT_QUEUE_SIZE (50) /* Max Events stored in driver */
+#define MPS_MAX_EVENT_DATA_LENGTH (48) /* Size of each event in Dwords */
+
+typedef struct mps_event_query
+{
+ uint16_t Entries;
+ uint16_t Reserved;
+ uint32_t Types[4];
+} mps_event_query_t;
+
+typedef struct mps_event_enable
+{
+ uint32_t Types[4];
+} mps_event_enable_t;
+
+/*
+ * Event record entry for ioctl.
+ */
+typedef struct mps_event_entry
+{
+ uint32_t Type;
+ uint32_t Number;
+ uint32_t Data[MPS_MAX_EVENT_DATA_LENGTH];
+} mps_event_entry_t;
+
+typedef struct mps_event_report
+{
+ uint32_t Size;
+ uint64_t PtrEvents;
+} mps_event_report_t;
+
+
+typedef struct mps_pci_info
+{
+ uint32_t BusNumber;
+ uint8_t DeviceNumber;
+ uint8_t FunctionNumber;
+ uint16_t InterruptVector;
+ uint8_t PciHeader[256];
+} mps_pci_info_t;
+
+
+typedef struct mps_diag_action
+{
+ uint32_t Action;
+ uint32_t Length;
+ uint64_t PtrDiagAction;
+ uint32_t ReturnCode;
+} mps_diag_action_t;
+
+#define MPS_FW_DIAGNOSTIC_UID_NOT_FOUND (0xFF)
+
+#define MPS_FW_DIAG_NEW (0x806E6577)
+
+#define MPS_FW_DIAG_TYPE_REGISTER (0x00000001)
+#define MPS_FW_DIAG_TYPE_UNREGISTER (0x00000002)
+#define MPS_FW_DIAG_TYPE_QUERY (0x00000003)
+#define MPS_FW_DIAG_TYPE_READ_BUFFER (0x00000004)
+#define MPS_FW_DIAG_TYPE_RELEASE (0x00000005)
+
+#define MPS_FW_DIAG_INVALID_UID (0x00000000)
+
+#define MPS_DIAG_SUCCESS 0
+#define MPS_DIAG_FAILURE 1
+
+#define MPS_FW_DIAG_ERROR_SUCCESS (0x00000000)
+#define MPS_FW_DIAG_ERROR_FAILURE (0x00000001)
+#define MPS_FW_DIAG_ERROR_INVALID_PARAMETER (0x00000002)
+#define MPS_FW_DIAG_ERROR_POST_FAILED (0x00000010)
+#define MPS_FW_DIAG_ERROR_INVALID_UID (0x00000011)
+#define MPS_FW_DIAG_ERROR_RELEASE_FAILED (0x00000012)
+#define MPS_FW_DIAG_ERROR_NO_BUFFER (0x00000013)
+#define MPS_FW_DIAG_ERROR_ALREADY_RELEASED (0x00000014)
+
+
+typedef struct mps_fw_diag_register
+{
+ uint8_t ExtendedType;
+ uint8_t BufferType;
+ uint16_t ApplicationFlags;
+ uint32_t DiagnosticFlags;
+ uint32_t ProductSpecific[23];
+ uint32_t RequestedBufferSize;
+ uint32_t UniqueId;
+} mps_fw_diag_register_t;
+
+typedef struct mps_fw_diag_unregister
+{
+ uint32_t UniqueId;
+} mps_fw_diag_unregister_t;
+
+#define MPS_FW_DIAG_FLAG_APP_OWNED (0x0001)
+#define MPS_FW_DIAG_FLAG_BUFFER_VALID (0x0002)
+#define MPS_FW_DIAG_FLAG_FW_BUFFER_ACCESS (0x0004)
+
+typedef struct mps_fw_diag_query
+{
+ uint8_t ExtendedType;
+ uint8_t BufferType;
+ uint16_t ApplicationFlags;
+ uint32_t DiagnosticFlags;
+ uint32_t ProductSpecific[23];
+ uint32_t TotalBufferSize;
+ uint32_t DriverAddedBufferSize;
+ uint32_t UniqueId;
+} mps_fw_diag_query_t;
+
+typedef struct mps_fw_diag_release
+{
+ uint32_t UniqueId;
+} mps_fw_diag_release_t;
+
+#define MPS_FW_DIAG_FLAG_REREGISTER (0x0001)
+#define MPS_FW_DIAG_FLAG_FORCE_RELEASE (0x0002)
+
+typedef struct mps_diag_read_buffer
+{
+ uint8_t Status;
+ uint8_t Reserved;
+ uint16_t Flags;
+ uint32_t StartingOffset;
+ uint32_t BytesToRead;
+ uint32_t UniqueId;
+ uint64_t PtrDataBuffer;
+} mps_diag_read_buffer_t;
+
+/*
+ * Register Access
+ */
+#define REG_IO_READ 1
+#define REG_IO_WRITE 2
+#define REG_MEM_READ 3
+#define REG_MEM_WRITE 4
+
+typedef struct mps_reg_access
+{
+ uint32_t Command;
+ uint32_t RegOffset;
+ uint32_t RegData;
+} mps_reg_access_t;
+
+typedef struct mps_btdh_mapping
+{
+ uint16_t TargetID;
+ uint16_t Bus;
+ uint16_t DevHandle;
+ uint16_t Reserved;
+} mps_btdh_mapping_t;
+
+#define MPSIO_MPS_COMMAND_FLAG_VERBOSE 0x01
+#define MPSIO_MPS_COMMAND_FLAG_DEBUG 0x02
#define MPSIO_READ_CFG_HEADER _IOWR('M', 200, struct mps_cfg_page_req)
#define MPSIO_READ_CFG_PAGE _IOWR('M', 201, struct mps_cfg_page_req)
#define MPSIO_READ_EXT_CFG_HEADER _IOWR('M', 202, struct mps_ext_cfg_page_req)
@@ -103,4 +361,27 @@ struct mps_usr_command {
#define MPSIO_RAID_ACTION _IOWR('M', 205, struct mps_raid_action)
#define MPSIO_MPS_COMMAND _IOWR('M', 210, struct mps_usr_command)
+#define MPTIOCTL ('I')
+#define MPTIOCTL_GET_ADAPTER_DATA _IOWR(MPTIOCTL, 1,\
+ struct mps_adapter_data)
+#define MPTIOCTL_UPDATE_FLASH _IOWR(MPTIOCTL, 2,\
+ struct mps_update_flash)
+#define MPTIOCTL_RESET_ADAPTER _IO(MPTIOCTL, 3)
+#define MPTIOCTL_PASS_THRU _IOWR(MPTIOCTL, 4,\
+ struct mps_pass_thru)
+#define MPTIOCTL_EVENT_QUERY _IOWR(MPTIOCTL, 5,\
+ struct mps_event_query)
+#define MPTIOCTL_EVENT_ENABLE _IOWR(MPTIOCTL, 6,\
+ struct mps_event_enable)
+#define MPTIOCTL_EVENT_REPORT _IOWR(MPTIOCTL, 7,\
+ struct mps_event_report)
+#define MPTIOCTL_GET_PCI_INFO _IOWR(MPTIOCTL, 8,\
+ struct mps_pci_info)
+#define MPTIOCTL_DIAG_ACTION _IOWR(MPTIOCTL, 9,\
+ struct mps_diag_action)
+#define MPTIOCTL_REG_ACCESS _IOWR(MPTIOCTL, 10,\
+ struct mps_reg_access)
+#define MPTIOCTL_BTDH_MAPPING _IOWR(MPTIOCTL, 11,\
+ struct mps_btdh_mapping)
+
#endif /* !_MPS_IOCTL_H_ */
diff --git a/sys/dev/mps/mps_mapping.c b/sys/dev/mps/mps_mapping.c
new file mode 100644
index 0000000..e897dd3
--- /dev/null
+++ b/sys/dev/mps/mps_mapping.c
@@ -0,0 +1,2268 @@
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/* TODO Move headers to mpsvar */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/kthread.h>
+#include <sys/taskqueue.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/sysctl.h>
+#include <sys/eventhandler.h>
+#include <sys/uio.h>
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <dev/mps/mpi/mpi2_type.h>
+#include <dev/mps/mpi/mpi2.h>
+#include <dev/mps/mpi/mpi2_ioc.h>
+#include <dev/mps/mpi/mpi2_sas.h>
+#include <dev/mps/mpi/mpi2_cnfg.h>
+#include <dev/mps/mpi/mpi2_init.h>
+#include <dev/mps/mpi/mpi2_tool.h>
+#include <dev/mps/mps_ioctl.h>
+#include <dev/mps/mpsvar.h>
+#include <dev/mps/mps_mapping.h>
+
+/**
+ * _mapping_clear_entry - Clear a particular mapping entry.
+ * @map_entry: map table entry
+ *
+ * Returns nothing.
+ */
+static inline void
+_mapping_clear_map_entry(struct dev_mapping_table *map_entry)
+{
+ map_entry->physical_id = 0;
+ map_entry->device_info = 0;
+ map_entry->phy_bits = 0;
+ map_entry->dpm_entry_num = MPS_DPM_BAD_IDX;
+ map_entry->dev_handle = 0;
+ map_entry->channel = -1;
+ map_entry->id = -1;
+ map_entry->missing_count = 0;
+ map_entry->init_complete = 0;
+ map_entry->TLR_bits = (u8)MPI2_SCSIIO_CONTROL_NO_TLR;
+}
+
+/**
+ * _mapping_clear_enc_entry - Clear a particular enclosure table entry.
+ * @enc_entry: enclosure table entry
+ *
+ * Returns nothing.
+ */
+static inline void
+_mapping_clear_enc_entry(struct enc_mapping_table *enc_entry)
+{
+ enc_entry->enclosure_id = 0;
+ enc_entry->start_index = MPS_MAPTABLE_BAD_IDX;
+ enc_entry->phy_bits = 0;
+ enc_entry->dpm_entry_num = MPS_DPM_BAD_IDX;
+ enc_entry->enc_handle = 0;
+ enc_entry->num_slots = 0;
+ enc_entry->start_slot = 0;
+ enc_entry->missing_count = 0;
+ enc_entry->removal_flag = 0;
+ enc_entry->skip_search = 0;
+ enc_entry->init_complete = 0;
+}
+
+/**
+ * _mapping_commit_enc_entry - write a particular enc entry in DPM page0.
+ * @sc: per adapter object
+ * @enc_entry: enclosure table entry
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_mapping_commit_enc_entry(struct mps_softc *sc,
+ struct enc_mapping_table *et_entry)
+{
+ Mpi2DriverMap0Entry_t *dpm_entry;
+ struct dev_mapping_table *mt_entry;
+ Mpi2ConfigReply_t mpi_reply;
+ Mpi2DriverMappingPage0_t config_page;
+
+ if (!sc->is_dpm_enable)
+ return 0;
+
+ memset(&config_page, 0, sizeof(Mpi2DriverMappingPage0_t));
+ memcpy(&config_page.Header, (u8 *) sc->dpm_pg0,
+ sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER));
+ dpm_entry = (Mpi2DriverMap0Entry_t *)((u8 *)sc->dpm_pg0 +
+ sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER));
+ dpm_entry += et_entry->dpm_entry_num;
+ dpm_entry->PhysicalIdentifier.Low =
+ ( 0xFFFFFFFF & et_entry->enclosure_id);
+ dpm_entry->PhysicalIdentifier.High =
+ ( et_entry->enclosure_id >> 32);
+ mt_entry = &sc->mapping_table[et_entry->start_index];
+ dpm_entry->DeviceIndex = htole16(mt_entry->id);
+ dpm_entry->MappingInformation = et_entry->num_slots;
+ dpm_entry->MappingInformation <<= MPI2_DRVMAP0_MAPINFO_SLOT_SHIFT;
+ dpm_entry->MappingInformation |= et_entry->missing_count;
+ dpm_entry->MappingInformation = htole16(dpm_entry->MappingInformation);
+ dpm_entry->PhysicalBitsMapping = htole32(et_entry->phy_bits);
+ dpm_entry->Reserved1 = 0;
+
+ memcpy(&config_page.Entry, (u8 *)dpm_entry,
+ sizeof(Mpi2DriverMap0Entry_t));
+ if (mps_config_set_dpm_pg0(sc, &mpi_reply, &config_page,
+ et_entry->dpm_entry_num)) {
+ printf("%s: write of dpm entry %d for enclosure failed\n",
+ __func__, et_entry->dpm_entry_num);
+ dpm_entry->MappingInformation = le16toh(dpm_entry->
+ MappingInformation);
+ dpm_entry->DeviceIndex = le16toh(dpm_entry->DeviceIndex);
+ dpm_entry->PhysicalBitsMapping =
+ le32toh(dpm_entry->PhysicalBitsMapping);
+ return -1;
+ }
+ dpm_entry->MappingInformation = le16toh(dpm_entry->
+ MappingInformation);
+ dpm_entry->DeviceIndex = le16toh(dpm_entry->DeviceIndex);
+ dpm_entry->PhysicalBitsMapping =
+ le32toh(dpm_entry->PhysicalBitsMapping);
+ return 0;
+}
+
+/**
+ * _mapping_commit_map_entry - write a particular map table entry in DPM page0.
+ * @sc: per adapter object
+ * @enc_entry: enclosure table entry
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+
+static int
+_mapping_commit_map_entry(struct mps_softc *sc,
+ struct dev_mapping_table *mt_entry)
+{
+ Mpi2DriverMap0Entry_t *dpm_entry;
+ Mpi2ConfigReply_t mpi_reply;
+ Mpi2DriverMappingPage0_t config_page;
+
+ if (!sc->is_dpm_enable)
+ return 0;
+
+ memset(&config_page, 0, sizeof(Mpi2DriverMappingPage0_t));
+ memcpy(&config_page.Header, (u8 *)sc->dpm_pg0,
+ sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER));
+ dpm_entry = (Mpi2DriverMap0Entry_t *)((u8 *) sc->dpm_pg0 +
+ sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER));
+ dpm_entry = dpm_entry + mt_entry->dpm_entry_num;
+ dpm_entry->PhysicalIdentifier.Low = (0xFFFFFFFF &
+ mt_entry->physical_id);
+ dpm_entry->PhysicalIdentifier.High = (mt_entry->physical_id >> 32);
+ dpm_entry->DeviceIndex = htole16(mt_entry->id);
+ dpm_entry->MappingInformation = htole16(mt_entry->missing_count);
+ dpm_entry->PhysicalBitsMapping = 0;
+ dpm_entry->Reserved1 = 0;
+ dpm_entry->MappingInformation = htole16(dpm_entry->MappingInformation);
+ memcpy(&config_page.Entry, (u8 *)dpm_entry,
+ sizeof(Mpi2DriverMap0Entry_t));
+ if (mps_config_set_dpm_pg0(sc, &mpi_reply, &config_page,
+ mt_entry->dpm_entry_num)) {
+ printf("%s: write of dpm entry %d for device failed\n",
+ __func__, mt_entry->dpm_entry_num);
+ dpm_entry->MappingInformation = le16toh(dpm_entry->
+ MappingInformation);
+ dpm_entry->DeviceIndex = le16toh(dpm_entry->DeviceIndex);
+ return -1;
+ }
+
+ dpm_entry->MappingInformation = le16toh(dpm_entry->MappingInformation);
+ dpm_entry->DeviceIndex = le16toh(dpm_entry->DeviceIndex);
+ return 0;
+}
+
+/**
+ * _mapping_get_ir_maprange - get start and end index for IR map range.
+ * @sc: per adapter object
+ * @start_idx: place holder for start index
+ * @end_idx: place holder for end index
+ *
+ * The IR volumes can be mapped either at start or end of the mapping table
+ * this function gets the detail of where IR volume mapping starts and ends
+ * in the device mapping table
+ *
+ * Returns nothing.
+ */
+static void
+_mapping_get_ir_maprange(struct mps_softc *sc, u32 *start_idx, u32 *end_idx)
+{
+ u16 volume_mapping_flags;
+ u16 ioc_pg8_flags = le16toh(sc->ioc_pg8.Flags);
+
+ volume_mapping_flags = le16toh(sc->ioc_pg8.IRVolumeMappingFlags) &
+ MPI2_IOCPAGE8_IRFLAGS_MASK_VOLUME_MAPPING_MODE;
+ if (volume_mapping_flags == MPI2_IOCPAGE8_IRFLAGS_LOW_VOLUME_MAPPING) {
+ *start_idx = 0;
+ if (ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_RESERVED_TARGETID_0)
+ *start_idx = 1;
+ } else
+ *start_idx = sc->max_devices - sc->max_volumes;
+ *end_idx = *start_idx + sc->max_volumes - 1;
+}
+
+/**
+ * _mapping_get_enc_idx_from_id - get enclosure index from enclosure ID
+ * @sc: per adapter object
+ * @enc_id: enclosure logical identifier
+ *
+ * Returns the index of enclosure entry on success or bad index.
+ */
+static u8
+_mapping_get_enc_idx_from_id(struct mps_softc *sc, u64 enc_id,
+ u64 phy_bits)
+{
+ struct enc_mapping_table *et_entry;
+ u8 enc_idx = 0;
+
+ for (enc_idx = 0; enc_idx < sc->num_enc_table_entries; enc_idx++) {
+ et_entry = &sc->enclosure_table[enc_idx];
+ if ((et_entry->enclosure_id == le64toh(enc_id)) &&
+ (!et_entry->phy_bits || (et_entry->phy_bits &
+ le32toh(phy_bits))))
+ return enc_idx;
+ }
+ return MPS_ENCTABLE_BAD_IDX;
+}
+
+/**
+ * _mapping_get_enc_idx_from_handle - get enclosure index from handle
+ * @sc: per adapter object
+ * @enc_id: enclosure handle
+ *
+ * Returns the index of enclosure entry on success or bad index.
+ */
+static u8
+_mapping_get_enc_idx_from_handle(struct mps_softc *sc, u16 handle)
+{
+ struct enc_mapping_table *et_entry;
+ u8 enc_idx = 0;
+
+ for (enc_idx = 0; enc_idx < sc->num_enc_table_entries; enc_idx++) {
+ et_entry = &sc->enclosure_table[enc_idx];
+ if (et_entry->missing_count)
+ continue;
+ if (et_entry->enc_handle == handle)
+ return enc_idx;
+ }
+ return MPS_ENCTABLE_BAD_IDX;
+}
+
+/**
+ * _mapping_get_high_missing_et_idx - get missing enclosure index
+ * @sc: per adapter object
+ *
+ * Search through the enclosure table and identifies the enclosure entry
+ * with high missing count and returns it's index
+ *
+ * Returns the index of enclosure entry on success or bad index.
+ */
+static u8
+_mapping_get_high_missing_et_idx(struct mps_softc *sc)
+{
+ struct enc_mapping_table *et_entry;
+ u8 high_missing_count = 0;
+ u8 enc_idx, high_idx = MPS_ENCTABLE_BAD_IDX;
+
+ for (enc_idx = 0; enc_idx < sc->num_enc_table_entries; enc_idx++) {
+ et_entry = &sc->enclosure_table[enc_idx];
+ if ((et_entry->missing_count > high_missing_count) &&
+ !et_entry->skip_search) {
+ high_missing_count = et_entry->missing_count;
+ high_idx = enc_idx;
+ }
+ }
+ return high_idx;
+}
+
+/**
+ * _mapping_get_high_missing_mt_idx - get missing map table index
+ * @sc: per adapter object
+ *
+ * Search through the map table and identifies the device entry
+ * with high missing count and returns it's index
+ *
+ * Returns the index of map table entry on success or bad index.
+ */
+static u32
+_mapping_get_high_missing_mt_idx(struct mps_softc *sc)
+{
+ u32 map_idx, high_idx = MPS_ENCTABLE_BAD_IDX;
+ u8 high_missing_count = 0;
+ u32 start_idx, end_idx, start_idx_ir, end_idx_ir;
+ struct dev_mapping_table *mt_entry;
+ u16 ioc_pg8_flags = le16toh(sc->ioc_pg8.Flags);
+
+ start_idx = 0;
+ end_idx = sc->max_devices;
+ if (ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_RESERVED_TARGETID_0)
+ start_idx = 1;
+ if (sc->ir_firmware)
+ _mapping_get_ir_maprange(sc, &start_idx_ir, &end_idx_ir);
+ if (start_idx == start_idx_ir)
+ start_idx = end_idx_ir + 1;
+ else
+ end_idx = start_idx_ir;
+ mt_entry = &sc->mapping_table[start_idx];
+ for (map_idx = start_idx; map_idx < end_idx; map_idx++, mt_entry++) {
+ if (mt_entry->missing_count > high_missing_count) {
+ high_missing_count = mt_entry->missing_count;
+ high_idx = map_idx;
+ }
+ }
+ return high_idx;
+}
+
+/**
+ * _mapping_get_ir_mt_idx_from_wwid - get map table index from volume WWID
+ * @sc: per adapter object
+ * @wwid: world wide unique ID of the volume
+ *
+ * Returns the index of map table entry on success or bad index.
+ */
+static u32
+_mapping_get_ir_mt_idx_from_wwid(struct mps_softc *sc, u64 wwid)
+{
+ u32 start_idx, end_idx, map_idx;
+ struct dev_mapping_table *mt_entry;
+
+ _mapping_get_ir_maprange(sc, &start_idx, &end_idx);
+ mt_entry = &sc->mapping_table[start_idx];
+ for (map_idx = start_idx; map_idx <= end_idx; map_idx++, mt_entry++)
+ if (mt_entry->physical_id == wwid)
+ return map_idx;
+
+ return MPS_MAPTABLE_BAD_IDX;
+}
+
+/**
+ * _mapping_get_mt_idx_from_id - get map table index from a device ID
+ * @sc: per adapter object
+ * @dev_id: device identifer (SAS Address)
+ *
+ * Returns the index of map table entry on success or bad index.
+ */
+static u32
+_mapping_get_mt_idx_from_id(struct mps_softc *sc, u64 dev_id)
+{
+ u32 map_idx;
+ struct dev_mapping_table *mt_entry;
+
+ for (map_idx = 0; map_idx < sc->max_devices; map_idx++) {
+ mt_entry = &sc->mapping_table[map_idx];
+ if (mt_entry->physical_id == dev_id)
+ return map_idx;
+ }
+ return MPS_MAPTABLE_BAD_IDX;
+}
+
+/**
+ * _mapping_get_ir_mt_idx_from_handle - get map table index from volume handle
+ * @sc: per adapter object
+ * @wwid: volume device handle
+ *
+ * Returns the index of map table entry on success or bad index.
+ */
+static u32
+_mapping_get_ir_mt_idx_from_handle(struct mps_softc *sc, u16 volHandle)
+{
+ u32 start_idx, end_idx, map_idx;
+ struct dev_mapping_table *mt_entry;
+
+ _mapping_get_ir_maprange(sc, &start_idx, &end_idx);
+ mt_entry = &sc->mapping_table[start_idx];
+ for (map_idx = start_idx; map_idx <= end_idx; map_idx++, mt_entry++)
+ if (mt_entry->dev_handle == volHandle)
+ return map_idx;
+
+ return MPS_MAPTABLE_BAD_IDX;
+}
+
+/**
+ * _mapping_get_mt_idx_from_handle - get map table index from handle
+ * @sc: per adapter object
+ * @dev_id: device handle
+ *
+ * Returns the index of map table entry on success or bad index.
+ */
+static u32
+_mapping_get_mt_idx_from_handle(struct mps_softc *sc, u16 handle)
+{
+ u32 map_idx;
+ struct dev_mapping_table *mt_entry;
+
+ for (map_idx = 0; map_idx < sc->max_devices; map_idx++) {
+ mt_entry = &sc->mapping_table[map_idx];
+ if (mt_entry->dev_handle == handle)
+ return map_idx;
+ }
+ return MPS_MAPTABLE_BAD_IDX;
+}
+
+/**
+ * _mapping_get_free_ir_mt_idx - get first free index for a volume
+ * @sc: per adapter object
+ *
+ * Search through mapping table for free index for a volume and if no free
+ * index then looks for a volume with high mapping index
+ *
+ * Returns the index of map table entry on success or bad index.
+ */
+static u32
+_mapping_get_free_ir_mt_idx(struct mps_softc *sc)
+{
+ u8 high_missing_count = 0;
+ u32 start_idx, end_idx, map_idx;
+ u32 high_idx = MPS_MAPTABLE_BAD_IDX;
+ struct dev_mapping_table *mt_entry;
+
+ _mapping_get_ir_maprange(sc, &start_idx, &end_idx);
+
+ mt_entry = &sc->mapping_table[start_idx];
+ for (map_idx = start_idx; map_idx <= end_idx; map_idx++, mt_entry++)
+ if (!(mt_entry->device_info & MPS_MAP_IN_USE))
+ return map_idx;
+
+ mt_entry = &sc->mapping_table[start_idx];
+ for (map_idx = start_idx; map_idx <= end_idx; map_idx++, mt_entry++) {
+ if (mt_entry->missing_count > high_missing_count) {
+ high_missing_count = mt_entry->missing_count;
+ high_idx = map_idx;
+ }
+ }
+ return high_idx;
+}
+
+/**
+ * _mapping_get_free_mt_idx - get first free index for a device
+ * @sc: per adapter object
+ * @start_idx: offset in the table to start search
+ *
+ * Returns the index of map table entry on success or bad index.
+ */
+static u32
+_mapping_get_free_mt_idx(struct mps_softc *sc, u32 start_idx)
+{
+ u32 map_idx, max_idx = sc->max_devices;
+ struct dev_mapping_table *mt_entry = &sc->mapping_table[start_idx];
+ u16 volume_mapping_flags;
+
+ volume_mapping_flags = le16toh(sc->ioc_pg8.IRVolumeMappingFlags) &
+ MPI2_IOCPAGE8_IRFLAGS_MASK_VOLUME_MAPPING_MODE;
+ if (sc->ir_firmware && (volume_mapping_flags ==
+ MPI2_IOCPAGE8_IRFLAGS_HIGH_VOLUME_MAPPING))
+ max_idx -= sc->max_volumes;
+ for (map_idx = start_idx; map_idx < max_idx; map_idx++, mt_entry++)
+ if (!(mt_entry->device_info & (MPS_MAP_IN_USE |
+ MPS_DEV_RESERVED)))
+ return map_idx;
+
+ return MPS_MAPTABLE_BAD_IDX;
+}
+
+/**
+ * _mapping_get_dpm_idx_from_id - get DPM index from ID
+ * @sc: per adapter object
+ * @id: volume WWID or enclosure ID or device ID
+ *
+ * Returns the index of DPM entry on success or bad index.
+ */
+static u16
+_mapping_get_dpm_idx_from_id(struct mps_softc *sc, u64 id, u32 phy_bits)
+{
+ u16 entry_num;
+ uint64_t PhysicalIdentifier;
+ Mpi2DriverMap0Entry_t *dpm_entry;
+
+ dpm_entry = (Mpi2DriverMap0Entry_t *)((u8 *)sc->dpm_pg0 +
+ sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER));
+ PhysicalIdentifier = dpm_entry->PhysicalIdentifier.High;
+ PhysicalIdentifier = (PhysicalIdentifier << 32) |
+ dpm_entry->PhysicalIdentifier.Low;
+ for (entry_num = 0; entry_num < sc->max_dpm_entries; entry_num++,
+ dpm_entry++)
+ if ((id == PhysicalIdentifier) &&
+ (!phy_bits || !dpm_entry->PhysicalBitsMapping ||
+ (phy_bits & dpm_entry->PhysicalBitsMapping)))
+ return entry_num;
+
+ return MPS_DPM_BAD_IDX;
+}
+
+
+/**
+ * _mapping_get_free_dpm_idx - get first available DPM index
+ * @sc: per adapter object
+ *
+ * Returns the index of DPM entry on success or bad index.
+ */
+static u32
+_mapping_get_free_dpm_idx(struct mps_softc *sc)
+{
+ u16 entry_num;
+
+ for (entry_num = 0; entry_num < sc->max_dpm_entries; entry_num++) {
+ if (!sc->dpm_entry_used[entry_num])
+ return entry_num;
+ }
+ return MPS_DPM_BAD_IDX;
+}
+
+/**
+ * _mapping_update_ir_missing_cnt - Updates missing count for a volume
+ * @sc: per adapter object
+ * @map_idx: map table index of the volume
+ * @element: IR configuration change element
+ * @wwid: IR volume ID.
+ *
+ * Updates the missing count in the map table and in the DPM entry for a volume
+ *
+ * Returns nothing.
+ */
+static void
+_mapping_update_ir_missing_cnt(struct mps_softc *sc, u32 map_idx,
+ Mpi2EventIrConfigElement_t *element, u64 wwid)
+{
+ struct dev_mapping_table *mt_entry;
+ u8 missing_cnt, reason = element->ReasonCode;
+ u16 dpm_idx;
+ Mpi2DriverMap0Entry_t *dpm_entry;
+
+ if (!sc->is_dpm_enable)
+ return;
+ mt_entry = &sc->mapping_table[map_idx];
+ if (reason == MPI2_EVENT_IR_CHANGE_RC_ADDED) {
+ mt_entry->missing_count = 0;
+ } else if (reason == MPI2_EVENT_IR_CHANGE_RC_VOLUME_CREATED) {
+ mt_entry->missing_count = 0;
+ mt_entry->init_complete = 0;
+ } else if ((reason == MPI2_EVENT_IR_CHANGE_RC_REMOVED) ||
+ (reason == MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED)) {
+ if (!mt_entry->init_complete) {
+ if (mt_entry->missing_count < MPS_MAX_MISSING_COUNT)
+ mt_entry->missing_count++;
+ else
+ mt_entry->init_complete = 1;
+ }
+ if (!mt_entry->missing_count)
+ mt_entry->missing_count++;
+ mt_entry->dev_handle = 0;
+ }
+
+ dpm_idx = mt_entry->dpm_entry_num;
+ if (dpm_idx == MPS_DPM_BAD_IDX) {
+ if ((reason == MPI2_EVENT_IR_CHANGE_RC_ADDED) ||
+ (reason == MPI2_EVENT_IR_CHANGE_RC_REMOVED))
+ dpm_idx = _mapping_get_dpm_idx_from_id(sc,
+ mt_entry->physical_id, 0);
+ else if (reason == MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED)
+ return;
+ }
+ if (dpm_idx != MPS_DPM_BAD_IDX) {
+ dpm_entry = (Mpi2DriverMap0Entry_t *)((u8 *)sc->dpm_pg0 +
+ sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER));
+ dpm_entry += dpm_idx;
+ missing_cnt = dpm_entry->MappingInformation &
+ MPI2_DRVMAP0_MAPINFO_MISSING_MASK;
+ if ((mt_entry->physical_id ==
+ le64toh((u64)dpm_entry->PhysicalIdentifier.High |
+ dpm_entry->PhysicalIdentifier.Low)) && (missing_cnt ==
+ mt_entry->missing_count))
+ mt_entry->init_complete = 1;
+ } else {
+ dpm_idx = _mapping_get_free_dpm_idx(sc);
+ mt_entry->init_complete = 0;
+ }
+
+ if ((dpm_idx != MPS_DPM_BAD_IDX) && !mt_entry->init_complete) {
+ mt_entry->init_complete = 1;
+ mt_entry->dpm_entry_num = dpm_idx;
+ dpm_entry = (Mpi2DriverMap0Entry_t *)((u8 *)sc->dpm_pg0 +
+ sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER));
+ dpm_entry += dpm_idx;
+ dpm_entry->PhysicalIdentifier.Low =
+ (0xFFFFFFFF & mt_entry->physical_id);
+ dpm_entry->PhysicalIdentifier.High =
+ (mt_entry->physical_id >> 32);
+ dpm_entry->DeviceIndex = map_idx;
+ dpm_entry->MappingInformation = mt_entry->missing_count;
+ dpm_entry->PhysicalBitsMapping = 0;
+ dpm_entry->Reserved1 = 0;
+ sc->dpm_flush_entry[dpm_idx] = 1;
+ sc->dpm_entry_used[dpm_idx] = 1;
+ } else if (dpm_idx == MPS_DPM_BAD_IDX) {
+ printf("%s: no space to add entry in DPM table\n", __func__);
+ mt_entry->init_complete = 1;
+ }
+}
+
+/**
+ * _mapping_add_to_removal_table - mark an entry for removal
+ * @sc: per adapter object
+ * @handle: Handle of enclosures/device/volume
+ *
+ * Adds the handle or DPM entry number in removal table.
+ *
+ * Returns nothing.
+ */
+static void
+_mapping_add_to_removal_table(struct mps_softc *sc, u16 handle,
+ u16 dpm_idx)
+{
+ struct map_removal_table *remove_entry;
+ u32 i;
+ u16 ioc_pg8_flags = le16toh(sc->ioc_pg8.Flags);
+
+ remove_entry = sc->removal_table;
+
+ for (i = 0; i < sc->max_devices; i++, remove_entry++) {
+ if (remove_entry->dev_handle || remove_entry->dpm_entry_num !=
+ MPS_DPM_BAD_IDX)
+ continue;
+ if ((ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) ==
+ MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING) {
+ if (dpm_idx)
+ remove_entry->dpm_entry_num = dpm_idx;
+ if (remove_entry->dpm_entry_num == MPS_DPM_BAD_IDX)
+ remove_entry->dev_handle = handle;
+ } else if ((ioc_pg8_flags &
+ MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) ==
+ MPI2_IOCPAGE8_FLAGS_DEVICE_PERSISTENCE_MAPPING)
+ remove_entry->dev_handle = handle;
+ break;
+ }
+
+}
+
+/**
+ * _mapping_update_missing_count - Update missing count for a device
+ * @sc: per adapter object
+ * @topo_change: Topology change event entry
+ *
+ * Search through the topology change list and if any device is found not
+ * responding it's associated map table entry and DPM entry is updated
+ *
+ * Returns nothing.
+ */
+static void
+_mapping_update_missing_count(struct mps_softc *sc,
+ struct _map_topology_change *topo_change)
+{
+ u16 ioc_pg8_flags = le16toh(sc->ioc_pg8.Flags);
+ u8 entry;
+ struct _map_phy_change *phy_change;
+ u32 map_idx;
+ struct dev_mapping_table *mt_entry;
+ Mpi2DriverMap0Entry_t *dpm_entry;
+
+ for (entry = 0; entry < topo_change->num_entries; entry++) {
+ phy_change = &topo_change->phy_details[entry];
+ if (!phy_change->dev_handle || (phy_change->reason !=
+ MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING))
+ continue;
+ map_idx = _mapping_get_mt_idx_from_handle(sc, phy_change->
+ dev_handle);
+ phy_change->is_processed = 1;
+ if (map_idx == MPS_MAPTABLE_BAD_IDX) {
+ printf("%s: device is already removed from mapping "
+ "table\n", __func__);
+ continue;
+ }
+ mt_entry = &sc->mapping_table[map_idx];
+ if (!mt_entry->init_complete) {
+ if (mt_entry->missing_count < MPS_MAX_MISSING_COUNT)
+ mt_entry->missing_count++;
+ else
+ mt_entry->init_complete = 1;
+ }
+ if (!mt_entry->missing_count)
+ mt_entry->missing_count++;
+ _mapping_add_to_removal_table(sc, mt_entry->dev_handle, 0);
+ mt_entry->dev_handle = 0;
+
+ if (((ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) ==
+ MPI2_IOCPAGE8_FLAGS_DEVICE_PERSISTENCE_MAPPING) &&
+ sc->is_dpm_enable && !mt_entry->init_complete &&
+ mt_entry->dpm_entry_num != MPS_DPM_BAD_IDX) {
+ dpm_entry =
+ (Mpi2DriverMap0Entry_t *) ((u8 *)sc->dpm_pg0 +
+ sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER));
+ dpm_entry += mt_entry->dpm_entry_num;
+ dpm_entry->MappingInformation = mt_entry->missing_count;
+ sc->dpm_flush_entry[mt_entry->dpm_entry_num] = 1;
+ }
+ mt_entry->init_complete = 1;
+ }
+}
+
+/**
+ * _mapping_find_enc_map_space -find map table entries for enclosure
+ * @sc: per adapter object
+ * @et_entry: enclosure entry
+ *
+ * Search through the mapping table defragment it and provide contiguous
+ * space in map table for a particular enclosure entry
+ *
+ * Returns start index in map table or bad index.
+ */
+static u32
+_mapping_find_enc_map_space(struct mps_softc *sc,
+ struct enc_mapping_table *et_entry)
+{
+ u16 vol_mapping_flags;
+ u32 skip_count, end_of_table, map_idx, enc_idx;
+ u16 num_found;
+ u32 start_idx = MPS_MAPTABLE_BAD_IDX;
+ struct dev_mapping_table *mt_entry;
+ struct enc_mapping_table *enc_entry;
+ unsigned char done_flag = 0, found_space;
+ u16 max_num_phy_ids = le16toh(sc->ioc_pg8.MaxNumPhysicalMappedIDs);
+
+ skip_count = sc->num_rsvd_entries;
+ num_found = 0;
+
+ vol_mapping_flags = le16toh(sc->ioc_pg8.IRVolumeMappingFlags) &
+ MPI2_IOCPAGE8_IRFLAGS_MASK_VOLUME_MAPPING_MODE;
+
+ if (!sc->ir_firmware)
+ end_of_table = sc->max_devices;
+ else if (vol_mapping_flags == MPI2_IOCPAGE8_IRFLAGS_LOW_VOLUME_MAPPING)
+ end_of_table = sc->max_devices;
+ else
+ end_of_table = sc->max_devices - sc->max_volumes;
+
+ for (map_idx = (max_num_phy_ids + skip_count);
+ map_idx < end_of_table; map_idx++) {
+ mt_entry = &sc->mapping_table[map_idx];
+ if ((et_entry->enclosure_id == mt_entry->physical_id) &&
+ (!mt_entry->phy_bits || (mt_entry->phy_bits &
+ et_entry->phy_bits))) {
+ num_found += 1;
+ if (num_found == et_entry->num_slots) {
+ start_idx = (map_idx - num_found) + 1;
+ return start_idx;
+ }
+ } else
+ num_found = 0;
+ }
+ for (map_idx = (max_num_phy_ids + skip_count);
+ map_idx < end_of_table; map_idx++) {
+ mt_entry = &sc->mapping_table[map_idx];
+ if (!(mt_entry->device_info & MPS_DEV_RESERVED)) {
+ num_found += 1;
+ if (num_found == et_entry->num_slots) {
+ start_idx = (map_idx - num_found) + 1;
+ return start_idx;
+ }
+ } else
+ num_found = 0;
+ }
+
+ while (!done_flag) {
+ enc_idx = _mapping_get_high_missing_et_idx(sc);
+ if (enc_idx == MPS_ENCTABLE_BAD_IDX)
+ return MPS_MAPTABLE_BAD_IDX;
+ enc_entry = &sc->enclosure_table[enc_idx];
+ /*VSP FIXME*/
+ enc_entry->skip_search = 1;
+ mt_entry = &sc->mapping_table[enc_entry->start_index];
+ for (map_idx = enc_entry->start_index; map_idx <
+ (enc_entry->start_index + enc_entry->num_slots); map_idx++,
+ mt_entry++)
+ mt_entry->device_info &= ~MPS_DEV_RESERVED;
+ found_space = 0;
+ for (map_idx = (max_num_phy_ids +
+ skip_count); map_idx < end_of_table; map_idx++) {
+ mt_entry = &sc->mapping_table[map_idx];
+ if (!(mt_entry->device_info & MPS_DEV_RESERVED)) {
+ num_found += 1;
+ if (num_found == et_entry->num_slots) {
+ start_idx = (map_idx - num_found) + 1;
+ found_space = 1;
+ }
+ } else
+ num_found = 0;
+ }
+
+ if (!found_space)
+ continue;
+ for (map_idx = start_idx; map_idx < (start_idx + num_found);
+ map_idx++) {
+ enc_entry = sc->enclosure_table;
+ for (enc_idx = 0; enc_idx < sc->num_enc_table_entries;
+ enc_idx++, enc_entry++) {
+ if (map_idx < enc_entry->start_index ||
+ map_idx > (enc_entry->start_index +
+ enc_entry->num_slots))
+ continue;
+ if (!enc_entry->removal_flag) {
+ enc_entry->removal_flag = 1;
+ _mapping_add_to_removal_table(sc, 0,
+ enc_entry->dpm_entry_num);
+ }
+ mt_entry = &sc->mapping_table[map_idx];
+ if (mt_entry->device_info &
+ MPS_MAP_IN_USE) {
+ _mapping_add_to_removal_table(sc,
+ mt_entry->dev_handle, 0);
+ _mapping_clear_map_entry(mt_entry);
+ }
+ if (map_idx == (enc_entry->start_index +
+ enc_entry->num_slots - 1))
+ _mapping_clear_enc_entry(et_entry);
+ }
+ }
+ enc_entry = sc->enclosure_table;
+ for (enc_idx = 0; enc_idx < sc->num_enc_table_entries;
+ enc_idx++, enc_entry++) {
+ if (!enc_entry->removal_flag) {
+ mt_entry = &sc->mapping_table[enc_entry->
+ start_index];
+ for (map_idx = enc_entry->start_index; map_idx <
+ (enc_entry->start_index +
+ enc_entry->num_slots); map_idx++,
+ mt_entry++)
+ mt_entry->device_info |=
+ MPS_DEV_RESERVED;
+ et_entry->skip_search = 0;
+ }
+ }
+ done_flag = 1;
+ }
+ return start_idx;
+}
+
+/**
+ * _mapping_get_dev_info -get information about newly added devices
+ * @sc: per adapter object
+ * @topo_change: Topology change event entry
+ *
+ * Search through the topology change event list and issues sas device pg0
+ * requests for the newly added device and reserved entries in tables
+ *
+ * Returns nothing
+ */
+static void
+_mapping_get_dev_info(struct mps_softc *sc,
+ struct _map_topology_change *topo_change)
+{
+ u16 ioc_pg8_flags = le16toh(sc->ioc_pg8.Flags);
+ Mpi2ConfigReply_t mpi_reply;
+ Mpi2SasDevicePage0_t sas_device_pg0;
+ u8 entry, enc_idx, phy_idx;
+ u32 map_idx, index, device_info;
+ struct _map_phy_change *phy_change, *tmp_phy_change;
+ uint64_t sas_address;
+ struct enc_mapping_table *et_entry;
+ struct dev_mapping_table *mt_entry;
+ u8 add_code = MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED;
+ int rc;
+
+ for (entry = 0; entry < topo_change->num_entries; entry++) {
+ phy_change = &topo_change->phy_details[entry];
+ if (phy_change->is_processed || !phy_change->dev_handle ||
+ phy_change->reason != MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED)
+ continue;
+ if (mps_config_get_sas_device_pg0(sc, &mpi_reply,
+ &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE,
+ phy_change->dev_handle)) {
+ phy_change->is_processed = 1;
+ continue;
+ }
+
+ device_info = le32toh(sas_device_pg0.DeviceInfo);
+ if ((ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) ==
+ MPI2_IOCPAGE8_FLAGS_DEVICE_PERSISTENCE_MAPPING) {
+ if ((device_info & MPI2_SAS_DEVICE_INFO_END_DEVICE) &&
+ (device_info & MPI2_SAS_DEVICE_INFO_SATA_DEVICE)) {
+ rc = mpssas_get_sas_address_for_sata_disk(sc,
+ &sas_address, phy_change->dev_handle,
+ device_info);
+ if (rc) {
+ printf("%s: failed to compute the "
+ "hashed SAS Address for SATA "
+ "device with handle 0x%04x\n",
+ __func__, phy_change->dev_handle);
+ sas_address =
+ sas_device_pg0.SASAddress.High;
+ sas_address = (sas_address << 32) |
+ sas_device_pg0.SASAddress.Low;
+ }
+ mps_dprint(sc, MPS_INFO, "SAS Address for SATA "
+ "device = %jx\n", sas_address);
+ } else {
+ sas_address =
+ sas_device_pg0.SASAddress.High;
+ sas_address = (sas_address << 32) |
+ sas_device_pg0.SASAddress.Low;
+ }
+ } else {
+ sas_address = sas_device_pg0.SASAddress.High;
+ sas_address = (sas_address << 32) |
+ sas_device_pg0.SASAddress.Low;
+ }
+ phy_change->physical_id = sas_address;
+ phy_change->slot = le16toh(sas_device_pg0.Slot);
+ phy_change->device_info =
+ le32toh(sas_device_pg0.DeviceInfo);
+
+ if ((ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) ==
+ MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING) {
+ enc_idx = _mapping_get_enc_idx_from_handle(sc,
+ topo_change->enc_handle);
+ if (enc_idx == MPS_ENCTABLE_BAD_IDX) {
+ phy_change->is_processed = 1;
+ printf("%s: failed to add the device with "
+ "handle 0x%04x because the enclosure is "
+ "not in the mapping table\n", __func__,
+ phy_change->dev_handle);
+ continue;
+ }
+ if (!((phy_change->device_info &
+ MPI2_SAS_DEVICE_INFO_END_DEVICE) &&
+ (phy_change->device_info &
+ (MPI2_SAS_DEVICE_INFO_SSP_TARGET |
+ MPI2_SAS_DEVICE_INFO_STP_TARGET |
+ MPI2_SAS_DEVICE_INFO_SATA_DEVICE)))) {
+ phy_change->is_processed = 1;
+ continue;
+ }
+ et_entry = &sc->enclosure_table[enc_idx];
+ if (et_entry->start_index != MPS_MAPTABLE_BAD_IDX)
+ continue;
+ if (!topo_change->exp_handle) {
+ map_idx = sc->num_rsvd_entries;
+ et_entry->start_index = map_idx;
+ } else {
+ map_idx = _mapping_find_enc_map_space(sc,
+ et_entry);
+ et_entry->start_index = map_idx;
+ if (et_entry->start_index ==
+ MPS_MAPTABLE_BAD_IDX) {
+ phy_change->is_processed = 1;
+ for (phy_idx = 0; phy_idx <
+ topo_change->num_entries;
+ phy_idx++) {
+ tmp_phy_change =
+ &topo_change->phy_details
+ [phy_idx];
+ if (tmp_phy_change->reason ==
+ add_code)
+ tmp_phy_change->
+ is_processed = 1;
+ }
+ break;
+ }
+ }
+ mt_entry = &sc->mapping_table[map_idx];
+ for (index = map_idx; index < (et_entry->num_slots
+ + map_idx); index++, mt_entry++) {
+ mt_entry->device_info = MPS_DEV_RESERVED;
+ mt_entry->physical_id = et_entry->enclosure_id;
+ mt_entry->phy_bits = et_entry->phy_bits;
+ }
+ }
+ }
+}
+
+/**
+ * _mapping_set_mid_to_eid -set map table data from enclosure table
+ * @sc: per adapter object
+ * @et_entry: enclosure entry
+ *
+ * Returns nothing
+ */
+static inline void
+_mapping_set_mid_to_eid(struct mps_softc *sc,
+ struct enc_mapping_table *et_entry)
+{
+ struct dev_mapping_table *mt_entry;
+ u16 slots = et_entry->num_slots, map_idx;
+ u32 start_idx = et_entry->start_index;
+ if (start_idx != MPS_MAPTABLE_BAD_IDX) {
+ mt_entry = &sc->mapping_table[start_idx];
+ for (map_idx = 0; map_idx < slots; map_idx++, mt_entry++)
+ mt_entry->physical_id = et_entry->enclosure_id;
+ }
+}
+
+/**
+ * _mapping_clear_removed_entries - mark the entries to be cleared
+ * @sc: per adapter object
+ *
+ * Search through the removal table and mark the entries which needs to be
+ * flushed to DPM and also updates the map table and enclosure table by
+ * clearing the corresponding entries.
+ *
+ * Returns nothing
+ */
+static void
+_mapping_clear_removed_entries(struct mps_softc *sc)
+{
+ u32 remove_idx;
+ struct map_removal_table *remove_entry;
+ Mpi2DriverMap0Entry_t *dpm_entry;
+ u8 done_flag = 0, num_entries, m, i;
+ struct enc_mapping_table *et_entry, *from, *to;
+ u16 ioc_pg8_flags = le16toh(sc->ioc_pg8.Flags);
+
+ if (sc->is_dpm_enable) {
+ remove_entry = sc->removal_table;
+ for (remove_idx = 0; remove_idx < sc->max_devices;
+ remove_idx++, remove_entry++) {
+ if (remove_entry->dpm_entry_num != MPS_DPM_BAD_IDX) {
+ dpm_entry = (Mpi2DriverMap0Entry_t *)
+ ((u8 *) sc->dpm_pg0 +
+ sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER));
+ dpm_entry += remove_entry->dpm_entry_num;
+ dpm_entry->PhysicalIdentifier.Low = 0;
+ dpm_entry->PhysicalIdentifier.High = 0;
+ dpm_entry->DeviceIndex = 0;
+ dpm_entry->MappingInformation = 0;
+ dpm_entry->PhysicalBitsMapping = 0;
+ sc->dpm_flush_entry[remove_entry->
+ dpm_entry_num] = 1;
+ sc->dpm_entry_used[remove_entry->dpm_entry_num]
+ = 0;
+ remove_entry->dpm_entry_num = MPS_DPM_BAD_IDX;
+ }
+ }
+ }
+ if ((ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) ==
+ MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING) {
+ num_entries = sc->num_enc_table_entries;
+ while (!done_flag) {
+ done_flag = 1;
+ et_entry = sc->enclosure_table;
+ for (i = 0; i < num_entries; i++, et_entry++) {
+ if (!et_entry->enc_handle && et_entry->
+ init_complete) {
+ done_flag = 0;
+ if (i != (num_entries - 1)) {
+ from = &sc->enclosure_table
+ [i+1];
+ to = &sc->enclosure_table[i];
+ for (m = i; m < (num_entries -
+ 1); m++, from++, to++) {
+ _mapping_set_mid_to_eid
+ (sc, to);
+ *to = *from;
+ }
+ _mapping_clear_enc_entry(to);
+ sc->num_enc_table_entries--;
+ num_entries =
+ sc->num_enc_table_entries;
+ } else {
+ _mapping_clear_enc_entry
+ (et_entry);
+ sc->num_enc_table_entries--;
+ num_entries =
+ sc->num_enc_table_entries;
+ }
+ }
+ }
+ }
+ }
+}
+
+/**
+ * _mapping_add_new_device -Add the new device into mapping table
+ * @sc: per adapter object
+ * @topo_change: Topology change event entry
+ *
+ * Search through the topology change event list and updates map table,
+ * enclosure table and DPM pages for for the newly added devices.
+ *
+ * Returns nothing
+ */
+static void
+_mapping_add_new_device(struct mps_softc *sc,
+ struct _map_topology_change *topo_change)
+{
+ u8 enc_idx, missing_cnt, is_removed = 0;
+ u16 dpm_idx;
+ u32 search_idx, map_idx;
+ u32 entry;
+ struct dev_mapping_table *mt_entry;
+ struct enc_mapping_table *et_entry;
+ struct _map_phy_change *phy_change;
+ u16 ioc_pg8_flags = le16toh(sc->ioc_pg8.Flags);
+ Mpi2DriverMap0Entry_t *dpm_entry;
+ uint64_t temp64_var;
+ u8 map_shift = MPI2_DRVMAP0_MAPINFO_SLOT_SHIFT;
+ u8 hdr_sz = sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER);
+ u16 max_num_phy_ids = le16toh(sc->ioc_pg8.MaxNumPhysicalMappedIDs);
+
+ for (entry = 0; entry < topo_change->num_entries; entry++) {
+ phy_change = &topo_change->phy_details[entry];
+ if (phy_change->is_processed)
+ continue;
+ if (phy_change->reason != MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED ||
+ !phy_change->dev_handle) {
+ phy_change->is_processed = 1;
+ continue;
+ }
+ if ((ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) ==
+ MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING) {
+ enc_idx = _mapping_get_enc_idx_from_handle
+ (sc, topo_change->enc_handle);
+ if (enc_idx == MPS_ENCTABLE_BAD_IDX) {
+ phy_change->is_processed = 1;
+ printf("%s: failed to add the device with "
+ "handle 0x%04x because the enclosure is "
+ "not in the mapping table\n", __func__,
+ phy_change->dev_handle);
+ continue;
+ }
+ et_entry = &sc->enclosure_table[enc_idx];
+ if (et_entry->start_index == MPS_MAPTABLE_BAD_IDX) {
+ phy_change->is_processed = 1;
+ if (!sc->mt_full_retry) {
+ sc->mt_add_device_failed = 1;
+ continue;
+ }
+ printf("%s: failed to add the device with "
+ "handle 0x%04x because there is no free "
+ "space available in the mapping table\n",
+ __func__, phy_change->dev_handle);
+ continue;
+ }
+ map_idx = et_entry->start_index + phy_change->slot -
+ et_entry->start_slot;
+ mt_entry = &sc->mapping_table[map_idx];
+ mt_entry->physical_id = phy_change->physical_id;
+ mt_entry->channel = 0;
+ mt_entry->id = map_idx;
+ mt_entry->dev_handle = phy_change->dev_handle;
+ mt_entry->missing_count = 0;
+ mt_entry->dpm_entry_num = et_entry->dpm_entry_num;
+ mt_entry->device_info = phy_change->device_info |
+ (MPS_DEV_RESERVED | MPS_MAP_IN_USE);
+ if (sc->is_dpm_enable) {
+ dpm_idx = et_entry->dpm_entry_num;
+ if (dpm_idx == MPS_DPM_BAD_IDX)
+ dpm_idx = _mapping_get_dpm_idx_from_id
+ (sc, et_entry->enclosure_id,
+ et_entry->phy_bits);
+ if (dpm_idx == MPS_DPM_BAD_IDX) {
+ dpm_idx = _mapping_get_free_dpm_idx(sc);
+ if (dpm_idx != MPS_DPM_BAD_IDX) {
+ dpm_entry =
+ (Mpi2DriverMap0Entry_t *)
+ ((u8 *) sc->dpm_pg0 +
+ hdr_sz);
+ dpm_entry += dpm_idx;
+ dpm_entry->
+ PhysicalIdentifier.Low =
+ (0xFFFFFFFF &
+ et_entry->enclosure_id);
+ dpm_entry->
+ PhysicalIdentifier.High =
+ ( et_entry->enclosure_id
+ >> 32);
+ dpm_entry->DeviceIndex =
+ (U16)et_entry->start_index;
+ dpm_entry->MappingInformation =
+ et_entry->num_slots;
+ dpm_entry->MappingInformation
+ <<= map_shift;
+ dpm_entry->PhysicalBitsMapping
+ = et_entry->phy_bits;
+ et_entry->dpm_entry_num =
+ dpm_idx;
+ /* FIXME Do I need to set the dpm_idxin mt_entry too */
+ sc->dpm_entry_used[dpm_idx] = 1;
+ sc->dpm_flush_entry[dpm_idx] =
+ 1;
+ phy_change->is_processed = 1;
+ } else {
+ phy_change->is_processed = 1;
+ printf("%s: failed to add the "
+ "device with handle 0x%04x "
+ "to persistent table "
+ "because there is no free "
+ "space available\n",
+ __func__,
+ phy_change->dev_handle);
+ }
+ } else {
+ et_entry->dpm_entry_num = dpm_idx;
+ mt_entry->dpm_entry_num = dpm_idx;
+ }
+ }
+ /* FIXME Why not mt_entry too? */
+ et_entry->init_complete = 1;
+ } else if ((ioc_pg8_flags &
+ MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) ==
+ MPI2_IOCPAGE8_FLAGS_DEVICE_PERSISTENCE_MAPPING) {
+ map_idx = _mapping_get_mt_idx_from_id
+ (sc, phy_change->physical_id);
+ if (map_idx == MPS_MAPTABLE_BAD_IDX) {
+ search_idx = sc->num_rsvd_entries;
+ if (topo_change->exp_handle)
+ search_idx += max_num_phy_ids;
+ map_idx = _mapping_get_free_mt_idx(sc,
+ search_idx);
+ }
+ if (map_idx == MPS_MAPTABLE_BAD_IDX) {
+ map_idx = _mapping_get_high_missing_mt_idx(sc);
+ if (map_idx != MPS_MAPTABLE_BAD_IDX) {
+ mt_entry = &sc->mapping_table[map_idx];
+ if (mt_entry->dev_handle) {
+ _mapping_add_to_removal_table
+ (sc, mt_entry->dev_handle,
+ 0);
+ is_removed = 1;
+ }
+ mt_entry->init_complete = 0;
+ }
+ }
+ if (map_idx != MPS_MAPTABLE_BAD_IDX) {
+ mt_entry = &sc->mapping_table[map_idx];
+ mt_entry->physical_id = phy_change->physical_id;
+ mt_entry->channel = 0;
+ mt_entry->id = map_idx;
+ mt_entry->dev_handle = phy_change->dev_handle;
+ mt_entry->missing_count = 0;
+ mt_entry->device_info = phy_change->device_info
+ | (MPS_DEV_RESERVED | MPS_MAP_IN_USE);
+ } else {
+ phy_change->is_processed = 1;
+ if (!sc->mt_full_retry) {
+ sc->mt_add_device_failed = 1;
+ continue;
+ }
+ printf("%s: failed to add the device with "
+ "handle 0x%04x because there is no free "
+ "space available in the mapping table\n",
+ __func__, phy_change->dev_handle);
+ continue;
+ }
+ if (sc->is_dpm_enable) {
+ if (mt_entry->dpm_entry_num !=
+ MPS_DPM_BAD_IDX) {
+ dpm_idx = mt_entry->dpm_entry_num;
+ dpm_entry = (Mpi2DriverMap0Entry_t *)
+ ((u8 *)sc->dpm_pg0 + hdr_sz);
+ dpm_entry += dpm_idx;
+ missing_cnt = dpm_entry->
+ MappingInformation &
+ MPI2_DRVMAP0_MAPINFO_MISSING_MASK;
+ temp64_var = dpm_entry->
+ PhysicalIdentifier.High;
+ temp64_var = (temp64_var << 32) |
+ dpm_entry->PhysicalIdentifier.Low;
+ if ((mt_entry->physical_id ==
+ temp64_var) && !missing_cnt)
+ mt_entry->init_complete = 1;
+ } else {
+ dpm_idx = _mapping_get_free_dpm_idx(sc);
+ mt_entry->init_complete = 0;
+ }
+ if (dpm_idx != MPS_DPM_BAD_IDX &&
+ !mt_entry->init_complete) {
+ mt_entry->init_complete = 1;
+ mt_entry->dpm_entry_num = dpm_idx;
+ dpm_entry = (Mpi2DriverMap0Entry_t *)
+ ((u8 *)sc->dpm_pg0 + hdr_sz);
+ dpm_entry += dpm_idx;
+ dpm_entry->PhysicalIdentifier.Low =
+ (0xFFFFFFFF &
+ mt_entry->physical_id);
+ dpm_entry->PhysicalIdentifier.High =
+ (mt_entry->physical_id >> 32);
+ dpm_entry->DeviceIndex = (U16) map_idx;
+ dpm_entry->MappingInformation = 0;
+ dpm_entry->PhysicalBitsMapping = 0;
+ sc->dpm_entry_used[dpm_idx] = 1;
+ sc->dpm_flush_entry[dpm_idx] = 1;
+ phy_change->is_processed = 1;
+ } else if (dpm_idx == MPS_DPM_BAD_IDX) {
+ phy_change->is_processed = 1;
+ printf("%s: failed to add the "
+ "device with handle 0x%04x "
+ "to persistent table "
+ "because there is no free "
+ "space available\n",
+ __func__,
+ phy_change->dev_handle);
+ }
+ }
+ mt_entry->init_complete = 1;
+ }
+
+ phy_change->is_processed = 1;
+ }
+ if (is_removed)
+ _mapping_clear_removed_entries(sc);
+}
+
+/**
+ * _mapping_flush_dpm_pages -Flush the DPM pages to NVRAM
+ * @sc: per adapter object
+ *
+ * Returns nothing
+ */
+static void
+_mapping_flush_dpm_pages(struct mps_softc *sc)
+{
+ Mpi2DriverMap0Entry_t *dpm_entry;
+ Mpi2ConfigReply_t mpi_reply;
+ Mpi2DriverMappingPage0_t config_page;
+ u16 entry_num;
+
+ for (entry_num = 0; entry_num < sc->max_dpm_entries; entry_num++) {
+ if (!sc->dpm_flush_entry[entry_num])
+ continue;
+ memset(&config_page, 0, sizeof(Mpi2DriverMappingPage0_t));
+ memcpy(&config_page.Header, (u8 *)sc->dpm_pg0,
+ sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER));
+ dpm_entry = (Mpi2DriverMap0Entry_t *) ((u8 *)sc->dpm_pg0 +
+ sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER));
+ dpm_entry += entry_num;
+ dpm_entry->MappingInformation = htole16(dpm_entry->
+ MappingInformation);
+ dpm_entry->DeviceIndex = htole16(dpm_entry->DeviceIndex);
+ dpm_entry->PhysicalBitsMapping = htole32(dpm_entry->
+ PhysicalBitsMapping);
+ memcpy(&config_page.Entry, (u8 *)dpm_entry,
+ sizeof(Mpi2DriverMap0Entry_t));
+ /* TODO-How to handle failed writes? */
+ if (mps_config_set_dpm_pg0(sc, &mpi_reply, &config_page,
+ entry_num)) {
+ printf("%s: write of dpm entry %d for device failed\n",
+ __func__, entry_num);
+ } else
+ sc->dpm_flush_entry[entry_num] = 0;
+ dpm_entry->MappingInformation = le16toh(dpm_entry->
+ MappingInformation);
+ dpm_entry->DeviceIndex = le16toh(dpm_entry->DeviceIndex);
+ dpm_entry->PhysicalBitsMapping = le32toh(dpm_entry->
+ PhysicalBitsMapping);
+ }
+}
+
+/**
+ * _mapping_allocate_memory- allocates the memory required for mapping tables
+ * @sc: per adapter object
+ *
+ * Allocates the memory for all the tables required for host mapping
+ *
+ * Return 0 on success or non-zero on failure.
+ */
+int
+mps_mapping_allocate_memory(struct mps_softc *sc)
+{
+ uint32_t dpm_pg0_sz;
+
+ sc->mapping_table = malloc((sizeof(struct dev_mapping_table) *
+ sc->max_devices), M_MPT2, M_ZERO|M_NOWAIT);
+ if (!sc->mapping_table)
+ goto free_resources;
+
+ sc->removal_table = malloc((sizeof(struct map_removal_table) *
+ sc->max_devices), M_MPT2, M_ZERO|M_NOWAIT);
+ if (!sc->removal_table)
+ goto free_resources;
+
+ sc->enclosure_table = malloc((sizeof(struct enc_mapping_table) *
+ sc->max_enclosures), M_MPT2, M_ZERO|M_NOWAIT);
+ if (!sc->enclosure_table)
+ goto free_resources;
+
+ sc->dpm_entry_used = malloc((sizeof(u8) * sc->max_dpm_entries),
+ M_MPT2, M_ZERO|M_NOWAIT);
+ if (!sc->dpm_entry_used)
+ goto free_resources;
+
+ sc->dpm_flush_entry = malloc((sizeof(u8) * sc->max_dpm_entries),
+ M_MPT2, M_ZERO|M_NOWAIT);
+ if (!sc->dpm_flush_entry)
+ goto free_resources;
+
+ dpm_pg0_sz = sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER) +
+ (sc->max_dpm_entries * sizeof(MPI2_CONFIG_PAGE_DRIVER_MAP0_ENTRY));
+
+ sc->dpm_pg0 = malloc(dpm_pg0_sz, M_MPT2, M_ZERO|M_NOWAIT);
+ if (!sc->dpm_pg0) {
+ printf("%s: memory alloc failed for dpm page; disabling dpm\n",
+ __func__);
+ sc->is_dpm_enable = 0;
+ }
+
+ return 0;
+
+free_resources:
+ free(sc->mapping_table, M_MPT2);
+ free(sc->removal_table, M_MPT2);
+ free(sc->enclosure_table, M_MPT2);
+ free(sc->dpm_entry_used, M_MPT2);
+ free(sc->dpm_flush_entry, M_MPT2);
+ free(sc->dpm_pg0, M_MPT2);
+ printf("%s: device initialization failed due to failure in mapping "
+ "table memory allocation\n", __func__);
+ return -1;
+}
+
+/**
+ * mps_mapping_free_memory- frees the memory allocated for mapping tables
+ * @sc: per adapter object
+ *
+ * Returns nothing.
+ */
+void
+mps_mapping_free_memory(struct mps_softc *sc)
+{
+ free(sc->mapping_table, M_MPT2);
+ free(sc->removal_table, M_MPT2);
+ free(sc->enclosure_table, M_MPT2);
+ free(sc->dpm_entry_used, M_MPT2);
+ free(sc->dpm_flush_entry, M_MPT2);
+ free(sc->dpm_pg0, M_MPT2);
+}
+
+
+static void
+_mapping_process_dpm_pg0(struct mps_softc *sc)
+{
+ u8 missing_cnt, enc_idx;
+ u16 slot_id, entry_num, num_slots;
+ u32 map_idx, dev_idx, start_idx, end_idx;
+ struct dev_mapping_table *mt_entry;
+ Mpi2DriverMap0Entry_t *dpm_entry;
+ u16 ioc_pg8_flags = le16toh(sc->ioc_pg8.Flags);
+ u16 max_num_phy_ids = le16toh(sc->ioc_pg8.MaxNumPhysicalMappedIDs);
+ struct enc_mapping_table *et_entry;
+ u64 physical_id;
+ u32 phy_bits = 0;
+
+ if (sc->ir_firmware)
+ _mapping_get_ir_maprange(sc, &start_idx, &end_idx);
+
+ dpm_entry = (Mpi2DriverMap0Entry_t *) ((uint8_t *) sc->dpm_pg0 +
+ sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER));
+ for (entry_num = 0; entry_num < sc->max_dpm_entries; entry_num++,
+ dpm_entry++) {
+ physical_id = dpm_entry->PhysicalIdentifier.High;
+ physical_id = (physical_id << 32) |
+ dpm_entry->PhysicalIdentifier.Low;
+ if (!physical_id) {
+ sc->dpm_entry_used[entry_num] = 0;
+ continue;
+ }
+ sc->dpm_entry_used[entry_num] = 1;
+ dpm_entry->MappingInformation = le16toh(dpm_entry->
+ MappingInformation);
+ missing_cnt = dpm_entry->MappingInformation &
+ MPI2_DRVMAP0_MAPINFO_MISSING_MASK;
+ dev_idx = le16toh(dpm_entry->DeviceIndex);
+ phy_bits = le32toh(dpm_entry->PhysicalBitsMapping);
+ if (sc->ir_firmware && (dev_idx >= start_idx) &&
+ (dev_idx <= end_idx)) {
+ mt_entry = &sc->mapping_table[dev_idx];
+ mt_entry->physical_id = dpm_entry->PhysicalIdentifier.High;
+ mt_entry->physical_id = (mt_entry->physical_id << 32) |
+ dpm_entry->PhysicalIdentifier.Low;
+ mt_entry->channel = MPS_RAID_CHANNEL;
+ mt_entry->id = dev_idx;
+ mt_entry->missing_count = missing_cnt;
+ mt_entry->dpm_entry_num = entry_num;
+ mt_entry->device_info = MPS_DEV_RESERVED;
+ continue;
+ }
+ if ((ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) ==
+ MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING) {
+ if (dev_idx < (sc->num_rsvd_entries +
+ max_num_phy_ids)) {
+ slot_id = 0;
+ if (ioc_pg8_flags &
+ MPI2_IOCPAGE8_FLAGS_DA_START_SLOT_1)
+ slot_id = 1;
+ num_slots = max_num_phy_ids;
+ } else {
+ slot_id = 0;
+ num_slots = dpm_entry->MappingInformation &
+ MPI2_DRVMAP0_MAPINFO_SLOT_MASK;
+ num_slots >>= MPI2_DRVMAP0_MAPINFO_SLOT_SHIFT;
+ }
+ enc_idx = sc->num_enc_table_entries;
+ if (enc_idx >= sc->max_enclosures) {
+ printf("%s: enclosure entries exceed max "
+ "enclosures of %d\n", __func__,
+ sc->max_enclosures);
+ break;
+ }
+ sc->num_enc_table_entries++;
+ et_entry = &sc->enclosure_table[enc_idx];
+ physical_id = dpm_entry->PhysicalIdentifier.High;
+ et_entry->enclosure_id = (physical_id << 32) |
+ dpm_entry->PhysicalIdentifier.Low;
+ et_entry->start_index = dev_idx;
+ et_entry->dpm_entry_num = entry_num;
+ et_entry->num_slots = num_slots;
+ et_entry->start_slot = slot_id;
+ et_entry->missing_count = missing_cnt;
+ et_entry->phy_bits = phy_bits;
+
+ mt_entry = &sc->mapping_table[dev_idx];
+ for (map_idx = dev_idx; map_idx < (dev_idx + num_slots);
+ map_idx++, mt_entry++) {
+ if (mt_entry->dpm_entry_num !=
+ MPS_DPM_BAD_IDX) {
+ printf("%s: conflict in mapping table "
+ "for enclosure %d\n", __func__,
+ enc_idx);
+ break;
+ }
+ physical_id = dpm_entry->PhysicalIdentifier.High;
+ mt_entry->physical_id = (physical_id << 32) |
+ dpm_entry->PhysicalIdentifier.Low;
+ mt_entry->phy_bits = phy_bits;
+ mt_entry->channel = 0;
+ mt_entry->id = dev_idx;
+ mt_entry->dpm_entry_num = entry_num;
+ mt_entry->missing_count = missing_cnt;
+ mt_entry->device_info = MPS_DEV_RESERVED;
+ }
+ } else if ((ioc_pg8_flags &
+ MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) ==
+ MPI2_IOCPAGE8_FLAGS_DEVICE_PERSISTENCE_MAPPING) {
+ map_idx = dev_idx;
+ mt_entry = &sc->mapping_table[map_idx];
+ if (mt_entry->dpm_entry_num != MPS_DPM_BAD_IDX) {
+ printf("%s: conflict in mapping table for "
+ "device %d\n", __func__, map_idx);
+ break;
+ }
+ physical_id = dpm_entry->PhysicalIdentifier.High;
+ mt_entry->physical_id = (physical_id << 32) |
+ dpm_entry->PhysicalIdentifier.Low;
+ mt_entry->phy_bits = phy_bits;
+ mt_entry->channel = 0;
+ mt_entry->id = dev_idx;
+ mt_entry->missing_count = missing_cnt;
+ mt_entry->dpm_entry_num = entry_num;
+ mt_entry->device_info = MPS_DEV_RESERVED;
+ }
+ } /*close the loop for DPM table */
+}
+
+/*
+ * mps_mapping_check_devices - start of the day check for device availabilty
+ * @sc: per adapter object
+ * @sleep_flag: Flag indicating whether this function can sleep or not
+ *
+ * Returns nothing.
+ */
+void
+mps_mapping_check_devices(struct mps_softc *sc, int sleep_flag)
+{
+ u32 i;
+/* u32 cntdn, i;
+ u32 timeout = 60;*/
+ struct dev_mapping_table *mt_entry;
+ u16 ioc_pg8_flags = le16toh(sc->ioc_pg8.Flags);
+ struct enc_mapping_table *et_entry;
+ u32 start_idx, end_idx;
+
+ /* We need to ucomment this when this function is called
+ * from the port enable complete */
+#if 0
+ sc->track_mapping_events = 0;
+ cntdn = (sleep_flag == CAN_SLEEP) ? 1000*timeout : 2000*timeout;
+ do {
+ if (!sc->pending_map_events)
+ break;
+ if (sleep_flag == CAN_SLEEP)
+ pause("mps_pause", (hz/1000));/* 1msec sleep */
+ else
+ DELAY(500); /* 500 useconds delay */
+ } while (--cntdn);
+
+
+ if (!cntdn)
+ printf("%s: there are %d"
+ " pending events after %d seconds of delay\n",
+ __func__, sc->pending_map_events, timeout);
+#endif
+ sc->pending_map_events = 0;
+
+ if ((ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) ==
+ MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING) {
+ et_entry = sc->enclosure_table;
+ for (i = 0; i < sc->num_enc_table_entries; i++, et_entry++) {
+ if (!et_entry->init_complete) {
+ if (et_entry->missing_count <
+ MPS_MAX_MISSING_COUNT) {
+ et_entry->missing_count++;
+ if (et_entry->dpm_entry_num !=
+ MPS_DPM_BAD_IDX)
+ _mapping_commit_enc_entry(sc,
+ et_entry);
+ }
+ et_entry->init_complete = 1;
+ }
+ }
+ if (!sc->ir_firmware)
+ return;
+ _mapping_get_ir_maprange(sc, &start_idx, &end_idx);
+ mt_entry = &sc->mapping_table[start_idx];
+ for (i = start_idx; i < (end_idx + 1); i++, mt_entry++) {
+ if (mt_entry->device_info & MPS_DEV_RESERVED
+ && !mt_entry->physical_id)
+ mt_entry->init_complete = 1;
+ else if (mt_entry->device_info & MPS_DEV_RESERVED) {
+ if (!mt_entry->init_complete) {
+ if (mt_entry->missing_count <
+ MPS_MAX_MISSING_COUNT) {
+ mt_entry->missing_count++;
+ if (mt_entry->dpm_entry_num !=
+ MPS_DPM_BAD_IDX)
+ _mapping_commit_map_entry(sc,
+ mt_entry);
+ }
+ mt_entry->init_complete = 1;
+ }
+ }
+ }
+ } else if ((ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) ==
+ MPI2_IOCPAGE8_FLAGS_DEVICE_PERSISTENCE_MAPPING) {
+ mt_entry = sc->mapping_table;
+ for (i = 0; i < sc->max_devices; i++, mt_entry++) {
+ if (mt_entry->device_info & MPS_DEV_RESERVED
+ && !mt_entry->physical_id)
+ mt_entry->init_complete = 1;
+ else if (mt_entry->device_info & MPS_DEV_RESERVED) {
+ if (!mt_entry->init_complete) {
+ if (mt_entry->missing_count <
+ MPS_MAX_MISSING_COUNT) {
+ mt_entry->missing_count++;
+ if (mt_entry->dpm_entry_num !=
+ MPS_DPM_BAD_IDX)
+ _mapping_commit_map_entry(sc,
+ mt_entry);
+ }
+ mt_entry->init_complete = 1;
+ }
+ }
+ }
+ }
+}
+
+
+/**
+ * mps_mapping_is_reinit_required - check whether event replay required
+ * @sc: per adapter object
+ *
+ * Checks the per ioc flags and decide whether reinit of events required
+ *
+ * Returns 1 for reinit of ioc 0 for not.
+ */
+int mps_mapping_is_reinit_required(struct mps_softc *sc)
+{
+ if (!sc->mt_full_retry && sc->mt_add_device_failed) {
+ sc->mt_full_retry = 1;
+ sc->mt_add_device_failed = 0;
+ _mapping_flush_dpm_pages(sc);
+ return 1;
+ }
+ sc->mt_full_retry = 1;
+ return 0;
+}
+
+/**
+ * mps_mapping_initialize - initialize mapping tables
+ * @sc: per adapter object
+ *
+ * Read controller persitant mapping tables into internal data area.
+ *
+ * Return 0 for success or non-zero for failure.
+ */
+int
+mps_mapping_initialize(struct mps_softc *sc)
+{
+ uint16_t volume_mapping_flags, dpm_pg0_sz;
+ uint32_t i;
+ Mpi2ConfigReply_t mpi_reply;
+ int error;
+ uint8_t retry_count;
+ uint16_t ioc_pg8_flags = le16toh(sc->ioc_pg8.Flags);
+
+ /* The additional 1 accounts for the virtual enclosure
+ * created for the controller
+ */
+ sc->max_enclosures = sc->facts->MaxEnclosures + 1;
+ sc->max_expanders = sc->facts->MaxSasExpanders;
+ sc->max_volumes = sc->facts->MaxVolumes;
+ sc->max_devices = sc->facts->MaxTargets + sc->max_volumes;
+ sc->pending_map_events = 0;
+ sc->num_enc_table_entries = 0;
+ sc->num_rsvd_entries = 0;
+ sc->num_channels = 1;
+ sc->max_dpm_entries = sc->ioc_pg8.MaxPersistentEntries;
+ sc->is_dpm_enable = (sc->max_dpm_entries) ? 1 : 0;
+ sc->track_mapping_events = 0;
+
+ if (ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_DISABLE_PERSISTENT_MAPPING)
+ sc->is_dpm_enable = 0;
+
+ if (ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_RESERVED_TARGETID_0)
+ sc->num_rsvd_entries = 1;
+
+ volume_mapping_flags = sc->ioc_pg8.IRVolumeMappingFlags &
+ MPI2_IOCPAGE8_IRFLAGS_MASK_VOLUME_MAPPING_MODE;
+ if (sc->ir_firmware && (volume_mapping_flags ==
+ MPI2_IOCPAGE8_IRFLAGS_LOW_VOLUME_MAPPING))
+ sc->num_rsvd_entries += sc->max_volumes;
+
+ error = mps_mapping_allocate_memory(sc);
+ if (error)
+ return (error);
+
+ for (i = 0; i < sc->max_devices; i++)
+ _mapping_clear_map_entry(sc->mapping_table + i);
+
+ for (i = 0; i < sc->max_enclosures; i++)
+ _mapping_clear_enc_entry(sc->enclosure_table + i);
+
+ for (i = 0; i < sc->max_devices; i++) {
+ sc->removal_table[i].dev_handle = 0;
+ sc->removal_table[i].dpm_entry_num = MPS_DPM_BAD_IDX;
+ }
+
+ memset(sc->dpm_entry_used, 0, sc->max_dpm_entries);
+ memset(sc->dpm_flush_entry, 0, sc->max_dpm_entries);
+
+ if (sc->is_dpm_enable) {
+ dpm_pg0_sz = sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER) +
+ (sc->max_dpm_entries *
+ sizeof(MPI2_CONFIG_PAGE_DRIVER_MAP0_ENTRY));
+ retry_count = 0;
+
+retry_read_dpm:
+ if (mps_config_get_dpm_pg0(sc, &mpi_reply, sc->dpm_pg0,
+ dpm_pg0_sz)) {
+ printf("%s: dpm page read failed; disabling dpm\n",
+ __func__);
+ if (retry_count < 3) {
+ retry_count++;
+ goto retry_read_dpm;
+ }
+ sc->is_dpm_enable = 0;
+ }
+ }
+
+ if (sc->is_dpm_enable)
+ _mapping_process_dpm_pg0(sc);
+
+ sc->track_mapping_events = 1;
+ return 0;
+}
+
+/**
+ * mps_mapping_exit - clear mapping table and associated memory
+ * @sc: per adapter object
+ *
+ * Returns nothing.
+ */
+void
+mps_mapping_exit(struct mps_softc *sc)
+{
+ _mapping_flush_dpm_pages(sc);
+ mps_mapping_free_memory(sc);
+}
+
+/**
+ * mps_mapping_get_sas_id - assign a target id for sas device
+ * @sc: per adapter object
+ * @sas_address: sas address of the device
+ * @handle: device handle
+ *
+ * Returns valid ID on success or BAD_ID.
+ */
+unsigned int
+mps_mapping_get_sas_id(struct mps_softc *sc, uint64_t sas_address, u16 handle)
+{
+ u32 map_idx;
+ struct dev_mapping_table *mt_entry;
+
+ for (map_idx = 0; map_idx < sc->max_devices; map_idx++) {
+ mt_entry = &sc->mapping_table[map_idx];
+ if (mt_entry->dev_handle == handle && mt_entry->physical_id ==
+ sas_address)
+ return mt_entry->id;
+ }
+
+ return MPS_MAP_BAD_ID;
+}
+
+/**
+ * mps_mapping_get_sas_id_from_handle - find a target id in mapping table using
+ * only the dev handle. This is just a wrapper function for the local function
+ * _mapping_get_mt_idx_from_handle.
+ * @sc: per adapter object
+ * @handle: device handle
+ *
+ * Returns valid ID on success or BAD_ID.
+ */
+unsigned int
+mps_mapping_get_sas_id_from_handle(struct mps_softc *sc, u16 handle)
+{
+ return (_mapping_get_mt_idx_from_handle(sc, handle));
+}
+
+/**
+ * mps_mapping_get_raid_id - assign a target id for raid device
+ * @sc: per adapter object
+ * @wwid: world wide identifier for raid volume
+ * @handle: device handle
+ *
+ * Returns valid ID on success or BAD_ID.
+ */
+unsigned int
+mps_mapping_get_raid_id(struct mps_softc *sc, u64 wwid, u16 handle)
+{
+ u32 map_idx;
+ struct dev_mapping_table *mt_entry;
+
+ for (map_idx = 0; map_idx < sc->max_devices; map_idx++) {
+ mt_entry = &sc->mapping_table[map_idx];
+ if (mt_entry->dev_handle == handle && mt_entry->physical_id ==
+ wwid)
+ return mt_entry->id;
+ }
+
+ return MPS_MAP_BAD_ID;
+}
+
+/**
+ * mps_mapping_get_raid_id_from_handle - find raid device in mapping table
+ * using only the volume dev handle. This is just a wrapper function for the
+ * local function _mapping_get_ir_mt_idx_from_handle.
+ * @sc: per adapter object
+ * @volHandle: volume device handle
+ *
+ * Returns valid ID on success or BAD_ID.
+ */
+unsigned int
+mps_mapping_get_raid_id_from_handle(struct mps_softc *sc, u16 volHandle)
+{
+ return (_mapping_get_ir_mt_idx_from_handle(sc, volHandle));
+}
+
+/**
+ * mps_mapping_enclosure_dev_status_change_event - handle enclosure events
+ * @sc: per adapter object
+ * @event_data: event data payload
+ *
+ * Return nothing.
+ */
+void
+mps_mapping_enclosure_dev_status_change_event(struct mps_softc *sc,
+ Mpi2EventDataSasEnclDevStatusChange_t *event_data)
+{
+ u8 enc_idx, missing_count;
+ struct enc_mapping_table *et_entry;
+ Mpi2DriverMap0Entry_t *dpm_entry;
+ u16 ioc_pg8_flags = le16toh(sc->ioc_pg8.Flags);
+ u8 map_shift = MPI2_DRVMAP0_MAPINFO_SLOT_SHIFT;
+ u8 update_phy_bits = 0;
+ u32 saved_phy_bits;
+ uint64_t temp64_var;
+
+ if ((ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) !=
+ MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING)
+ goto out;
+
+ dpm_entry = (Mpi2DriverMap0Entry_t *)((u8 *)sc->dpm_pg0 +
+ sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER));
+
+ if (event_data->ReasonCode == MPI2_EVENT_SAS_ENCL_RC_ADDED) {
+ if (!event_data->NumSlots) {
+ printf("%s: enclosure with handle = 0x%x reported 0 "
+ "slots\n", __func__,
+ le16toh(event_data->EnclosureHandle));
+ goto out;
+ }
+ temp64_var = event_data->EnclosureLogicalID.High;
+ temp64_var = (temp64_var << 32) |
+ event_data->EnclosureLogicalID.Low;
+ enc_idx = _mapping_get_enc_idx_from_id(sc, temp64_var,
+ event_data->PhyBits);
+ if (enc_idx != MPS_ENCTABLE_BAD_IDX) {
+ et_entry = &sc->enclosure_table[enc_idx];
+ if (et_entry->init_complete &&
+ !et_entry->missing_count) {
+ printf("%s: enclosure %d is already present "
+ "with handle = 0x%x\n",__func__, enc_idx,
+ et_entry->enc_handle);
+ goto out;
+ }
+ et_entry->enc_handle = le16toh(event_data->
+ EnclosureHandle);
+ et_entry->start_slot = le16toh(event_data->StartSlot);
+ saved_phy_bits = et_entry->phy_bits;
+ et_entry->phy_bits |= le32toh(event_data->PhyBits);
+ if (saved_phy_bits != et_entry->phy_bits)
+ update_phy_bits = 1;
+ if (et_entry->missing_count || update_phy_bits) {
+ et_entry->missing_count = 0;
+ if (sc->is_dpm_enable &&
+ et_entry->dpm_entry_num !=
+ MPS_DPM_BAD_IDX) {
+ dpm_entry += et_entry->dpm_entry_num;
+ missing_count =
+ (u8)(dpm_entry->MappingInformation &
+ MPI2_DRVMAP0_MAPINFO_MISSING_MASK);
+ if (!et_entry->init_complete && (
+ missing_count || update_phy_bits)) {
+ dpm_entry->MappingInformation
+ = et_entry->num_slots;
+ dpm_entry->MappingInformation
+ <<= map_shift;
+ dpm_entry->PhysicalBitsMapping
+ = et_entry->phy_bits;
+ sc->dpm_flush_entry[et_entry->
+ dpm_entry_num] = 1;
+ }
+ }
+ }
+ } else {
+ enc_idx = sc->num_enc_table_entries;
+ if (enc_idx >= sc->max_enclosures) {
+ printf("%s: enclosure can not be added; "
+ "mapping table is full\n", __func__);
+ goto out;
+ }
+ sc->num_enc_table_entries++;
+ et_entry = &sc->enclosure_table[enc_idx];
+ et_entry->enc_handle = le16toh(event_data->
+ EnclosureHandle);
+ et_entry->enclosure_id = event_data->
+ EnclosureLogicalID.High;
+ et_entry->enclosure_id = ( et_entry->enclosure_id <<
+ 32) | event_data->EnclosureLogicalID.Low;
+ et_entry->start_index = MPS_MAPTABLE_BAD_IDX;
+ et_entry->dpm_entry_num = MPS_DPM_BAD_IDX;
+ et_entry->num_slots = le16toh(event_data->NumSlots);
+ et_entry->start_slot = le16toh(event_data->StartSlot);
+ et_entry->phy_bits = le32toh(event_data->PhyBits);
+ }
+ et_entry->init_complete = 1;
+ } else if (event_data->ReasonCode ==
+ MPI2_EVENT_SAS_ENCL_RC_NOT_RESPONDING) {
+ enc_idx = _mapping_get_enc_idx_from_handle(sc,
+ le16toh(event_data->EnclosureHandle));
+ if (enc_idx == MPS_ENCTABLE_BAD_IDX) {
+ printf("%s: cannot unmap enclosure %d because it has "
+ "already been deleted", __func__, enc_idx);
+ goto out;
+ }
+ et_entry = &sc->enclosure_table[enc_idx];
+ if (!et_entry->init_complete) {
+ if (et_entry->missing_count < MPS_MAX_MISSING_COUNT)
+ et_entry->missing_count++;
+ else
+ et_entry->init_complete = 1;
+ }
+ if (!et_entry->missing_count)
+ et_entry->missing_count++;
+ if (sc->is_dpm_enable && !et_entry->init_complete &&
+ et_entry->dpm_entry_num != MPS_DPM_BAD_IDX) {
+ dpm_entry += et_entry->dpm_entry_num;
+ dpm_entry->MappingInformation = et_entry->num_slots;
+ dpm_entry->MappingInformation <<= map_shift;
+ dpm_entry->MappingInformation |=
+ et_entry->missing_count;
+ sc->dpm_flush_entry[et_entry->dpm_entry_num] = 1;
+ }
+ et_entry->init_complete = 1;
+ }
+
+out:
+ _mapping_flush_dpm_pages(sc);
+ if (sc->pending_map_events)
+ sc->pending_map_events--;
+}
+
+/**
+ * mps_mapping_topology_change_event - handle topology change events
+ * @sc: per adapter object
+ * @event_data: event data payload
+ *
+ * Returns nothing.
+ */
+void
+mps_mapping_topology_change_event(struct mps_softc *sc,
+ Mpi2EventDataSasTopologyChangeList_t *event_data)
+{
+ struct _map_topology_change topo_change;
+ struct _map_phy_change *phy_change;
+ Mpi2EventSasTopoPhyEntry_t *event_phy_change;
+ u8 i, num_entries;
+
+ topo_change.enc_handle = le16toh(event_data->EnclosureHandle);
+ topo_change.exp_handle = le16toh(event_data->ExpanderDevHandle);
+ num_entries = event_data->NumEntries;
+ topo_change.num_entries = num_entries;
+ topo_change.start_phy_num = event_data->StartPhyNum;
+ topo_change.num_phys = event_data->NumPhys;
+ topo_change.exp_status = event_data->ExpStatus;
+ event_phy_change = event_data->PHY;
+ topo_change.phy_details = NULL;
+
+ if (!num_entries)
+ goto out;
+ phy_change = malloc(sizeof(struct _map_phy_change) * num_entries,
+ M_MPT2, M_NOWAIT|M_ZERO);
+ topo_change.phy_details = phy_change;
+ if (!phy_change)
+ goto out;
+ for (i = 0; i < num_entries; i++, event_phy_change++, phy_change++) {
+ phy_change->dev_handle = le16toh(event_phy_change->
+ AttachedDevHandle);
+ phy_change->reason = event_phy_change->PhyStatus &
+ MPI2_EVENT_SAS_TOPO_RC_MASK;
+ }
+ _mapping_update_missing_count(sc, &topo_change);
+ _mapping_get_dev_info(sc, &topo_change);
+ _mapping_clear_removed_entries(sc);
+ _mapping_add_new_device(sc, &topo_change);
+
+out:
+ free(topo_change.phy_details, M_MPT2);
+ _mapping_flush_dpm_pages(sc);
+ if (sc->pending_map_events)
+ sc->pending_map_events--;
+}
+
+/**
+ * _mapping_check_update_ir_mt_idx - Check and update IR map table index
+ * @sc: per adapter object
+ * @event_data: event data payload
+ * @evt_idx: current event index
+ * @map_idx: current index and the place holder for new map table index
+ * @wwid_table: world wide name for volumes in the element table
+ *
+ * pass through IR events and find whether any events matches and if so
+ * tries to find new index if not returns failure
+ *
+ * Returns 0 on success and 1 on failure
+ */
+static int
+_mapping_check_update_ir_mt_idx(struct mps_softc *sc,
+ Mpi2EventDataIrConfigChangeList_t *event_data, int evt_idx, u32 *map_idx,
+ u64 *wwid_table)
+{
+ struct dev_mapping_table *mt_entry;
+ u32 st_idx, end_idx, mt_idx = *map_idx;
+ u8 match = 0;
+ Mpi2EventIrConfigElement_t *element;
+ u16 element_flags;
+ int i;
+
+ mt_entry = &sc->mapping_table[mt_idx];
+ _mapping_get_ir_maprange(sc, &st_idx, &end_idx);
+search_again:
+ match = 0;
+ for (i = evt_idx + 1; i < event_data->NumElements; i++) {
+ element = (Mpi2EventIrConfigElement_t *)
+ &event_data->ConfigElement[i];
+ element_flags = le16toh(element->ElementFlags);
+ if ((element_flags &
+ MPI2_EVENT_IR_CHANGE_EFLAGS_ELEMENT_TYPE_MASK) !=
+ MPI2_EVENT_IR_CHANGE_EFLAGS_VOLUME_ELEMENT)
+ continue;
+ if (element->ReasonCode == MPI2_EVENT_IR_CHANGE_RC_ADDED ||
+ element->ReasonCode ==
+ MPI2_EVENT_IR_CHANGE_RC_VOLUME_CREATED) {
+ if (mt_entry->physical_id == wwid_table[i]) {
+ match = 1;
+ break;
+ }
+ }
+ }
+
+ if (match) {
+ do {
+ mt_idx++;
+ if (mt_idx > end_idx)
+ return 1;
+ mt_entry = &sc->mapping_table[mt_idx];
+ } while (mt_entry->device_info & MPS_MAP_IN_USE);
+ goto search_again;
+ }
+ *map_idx = mt_idx;
+ return 0;
+}
+
+/**
+ * mps_mapping_ir_config_change_event - handle IR config change list events
+ * @sc: per adapter object
+ * @event_data: event data payload
+ *
+ * Returns nothing.
+ */
+void
+mps_mapping_ir_config_change_event(struct mps_softc *sc,
+ Mpi2EventDataIrConfigChangeList_t *event_data)
+{
+ Mpi2EventIrConfigElement_t *element;
+ int i;
+ u64 *wwid_table;
+ u32 map_idx, flags;
+ struct dev_mapping_table *mt_entry;
+ u16 element_flags;
+ u8 log_full_error = 0;
+
+ wwid_table = malloc(sizeof(u64) * event_data->NumElements, M_MPT2,
+ M_NOWAIT | M_ZERO);
+ if (!wwid_table)
+ goto out;
+ element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0];
+ flags = le32toh(event_data->Flags);
+ for (i = 0; i < event_data->NumElements; i++, element++) {
+ element_flags = le16toh(element->ElementFlags);
+ if ((element->ReasonCode != MPI2_EVENT_IR_CHANGE_RC_ADDED) &&
+ (element->ReasonCode != MPI2_EVENT_IR_CHANGE_RC_REMOVED) &&
+ (element->ReasonCode != MPI2_EVENT_IR_CHANGE_RC_NO_CHANGE)
+ && (element->ReasonCode !=
+ MPI2_EVENT_IR_CHANGE_RC_VOLUME_CREATED))
+ continue;
+ if ((element_flags &
+ MPI2_EVENT_IR_CHANGE_EFLAGS_ELEMENT_TYPE_MASK) ==
+ MPI2_EVENT_IR_CHANGE_EFLAGS_VOLUME_ELEMENT) {
+ mps_config_get_volume_wwid(sc,
+ le16toh(element->VolDevHandle), &wwid_table[i]);
+ map_idx = _mapping_get_ir_mt_idx_from_wwid(sc,
+ wwid_table[i]);
+ if (map_idx != MPS_MAPTABLE_BAD_IDX) {
+ mt_entry = &sc->mapping_table[map_idx];
+ mt_entry->device_info |= MPS_MAP_IN_USE;
+ }
+ }
+ }
+ if (flags == MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG)
+ goto out;
+ else {
+ element = (Mpi2EventIrConfigElement_t *)&event_data->
+ ConfigElement[0];
+ for (i = 0; i < event_data->NumElements; i++, element++) {
+ if (element->ReasonCode ==
+ MPI2_EVENT_IR_CHANGE_RC_ADDED ||
+ element->ReasonCode ==
+ MPI2_EVENT_IR_CHANGE_RC_VOLUME_CREATED) {
+ map_idx = _mapping_get_ir_mt_idx_from_wwid
+ (sc, wwid_table[i]);
+ if (map_idx != MPS_MAPTABLE_BAD_IDX) {
+ mt_entry = &sc->mapping_table[map_idx];
+ mt_entry->channel = MPS_RAID_CHANNEL;
+ mt_entry->id = map_idx;
+ mt_entry->dev_handle = le16toh
+ (element->VolDevHandle);
+ mt_entry->device_info =
+ MPS_DEV_RESERVED | MPS_MAP_IN_USE;
+ _mapping_update_ir_missing_cnt(sc,
+ map_idx, element, wwid_table[i]);
+ continue;
+ }
+ map_idx = _mapping_get_free_ir_mt_idx(sc);
+ if (map_idx == MPS_MAPTABLE_BAD_IDX)
+ log_full_error = 1;
+ else if (i < (event_data->NumElements - 1)) {
+ log_full_error =
+ _mapping_check_update_ir_mt_idx
+ (sc, event_data, i, &map_idx,
+ wwid_table);
+ }
+ if (log_full_error) {
+ printf("%s: no space to add the RAID "
+ "volume with handle 0x%04x in "
+ "mapping table\n", __func__, le16toh
+ (element->VolDevHandle));
+ continue;
+ }
+ mt_entry = &sc->mapping_table[map_idx];
+ mt_entry->physical_id = wwid_table[i];
+ mt_entry->channel = MPS_RAID_CHANNEL;
+ mt_entry->id = map_idx;
+ mt_entry->dev_handle = le16toh(element->
+ VolDevHandle);
+ mt_entry->device_info = MPS_DEV_RESERVED |
+ MPS_MAP_IN_USE;
+ mt_entry->init_complete = 0;
+ _mapping_update_ir_missing_cnt(sc, map_idx,
+ element, wwid_table[i]);
+ } else if (element->ReasonCode ==
+ MPI2_EVENT_IR_CHANGE_RC_REMOVED) {
+ map_idx = _mapping_get_ir_mt_idx_from_wwid(sc,
+ wwid_table[i]);
+ if (map_idx == MPS_MAPTABLE_BAD_IDX) {
+ printf("%s: failed to remove a volume "
+ "because it has already been "
+ "removed\n", __func__);
+ continue;
+ }
+ _mapping_update_ir_missing_cnt(sc, map_idx,
+ element, wwid_table[i]);
+ } else if (element->ReasonCode ==
+ MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED) {
+ map_idx = _mapping_get_mt_idx_from_handle(sc,
+ le16toh(element->VolDevHandle));
+ if (map_idx == MPS_MAPTABLE_BAD_IDX) {
+ printf("%s: failed to remove volume "
+ "with handle 0x%04x because it has "
+ "already been removed\n", __func__,
+ le16toh(element->VolDevHandle));
+ continue;
+ }
+ mt_entry = &sc->mapping_table[map_idx];
+ _mapping_update_ir_missing_cnt(sc, map_idx,
+ element, mt_entry->physical_id);
+ }
+ }
+ }
+
+out:
+ _mapping_flush_dpm_pages(sc);
+ free(wwid_table, M_MPT2);
+ if (sc->pending_map_events)
+ sc->pending_map_events--;
+}
diff --git a/sys/dev/mps/mps_mapping.h b/sys/dev/mps/mps_mapping.h
new file mode 100644
index 0000000..5e1877b
--- /dev/null
+++ b/sys/dev/mps/mps_mapping.h
@@ -0,0 +1,71 @@
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MPS_MAPPING_H
+#define _MPS_MAPPING_H
+
+/**
+ * struct _map_phy_change - PHY entries recieved in Topology change list
+ * @physical_id: SAS address of the device attached with the associate PHY
+ * @device_info: bitfield provides detailed info about the device
+ * @dev_handle: device handle for the device pointed by this entry
+ * @slot: slot ID
+ * @is_processed: Flag to indicate whether this entry is processed or not
+ */
+struct _map_phy_change {
+ uint64_t physical_id;
+ uint32_t device_info;
+ uint16_t dev_handle;
+ uint16_t slot;
+ uint8_t reason;
+ uint8_t is_processed;
+};
+
+/**
+ * struct _map_topology_change - entries to be removed from mapping table
+ * @dpm_entry_num: index of this device in device persistent map table
+ * @dev_handle: device handle for the device pointed by this entry
+ */
+struct _map_topology_change {
+ uint16_t enc_handle;
+ uint16_t exp_handle;
+ uint8_t num_entries;
+ uint8_t start_phy_num;
+ uint8_t num_phys;
+ uint8_t exp_status;
+ struct _map_phy_change *phy_details;
+};
+
+
+extern int
+mpssas_get_sas_address_for_sata_disk(struct mps_softc *ioc,
+ u64 *sas_address, u16 handle, u32 device_info);
+
+#endif
diff --git a/sys/dev/mps/mps_pci.c b/sys/dev/mps/mps_pci.c
index 9b27488..c50dd25 100644
--- a/sys/dev/mps/mps_pci.c
+++ b/sys/dev/mps/mps_pci.c
@@ -29,6 +29,7 @@ __FBSDID("$FreeBSD$");
/* PCI/PCI-X/PCIe bus interface for the LSI MPT2 controllers */
+/* TODO Move headers to mpsvar */
#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
@@ -46,12 +47,17 @@ __FBSDID("$FreeBSD$");
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
+#include <dev/pci/pci_private.h>
#include <dev/mps/mpi/mpi2_type.h>
#include <dev/mps/mpi/mpi2.h>
#include <dev/mps/mpi/mpi2_ioc.h>
#include <dev/mps/mpi/mpi2_cnfg.h>
+#include <dev/mps/mpi/mpi2_tool.h>
+#include <sys/queue.h>
+#include <sys/kthread.h>
+#include <dev/mps/mps_ioctl.h>
#include <dev/mps/mpsvar.h>
static int mps_pci_probe(device_t);
@@ -63,15 +69,6 @@ static void mps_pci_free(struct mps_softc *);
static int mps_alloc_msix(struct mps_softc *sc, int msgs);
static int mps_alloc_msi(struct mps_softc *sc, int msgs);
-int mps_disable_msix = 0;
-TUNABLE_INT("hw.mps.disable_msix", &mps_disable_msix);
-SYSCTL_INT(_hw_mps, OID_AUTO, disable_msix, CTLFLAG_RD, &mps_disable_msix, 0,
- "Disable MSIX interrupts\n");
-int mps_disable_msi = 0;
-TUNABLE_INT("hw.mps.disable_msi", &mps_disable_msi);
-SYSCTL_INT(_hw_mps, OID_AUTO, disable_msi, CTLFLAG_RD, &mps_disable_msi, 0,
- "Disable MSI interrupts\n");
-
static device_method_t mps_methods[] = {
DEVMETHOD(device_probe, mps_pci_probe),
DEVMETHOD(device_attach, mps_pci_attach),
@@ -125,10 +122,24 @@ struct mps_ident {
0xffff, 0xffff, 0, "LSI SAS2208" },
{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_6,
0xffff, 0xffff, 0, "LSI SAS2208" },
- { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_7,
- 0xffff, 0xffff, 0, "LSI SAS2208" },
- { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_8,
- 0xffff, 0xffff, 0, "LSI SAS2208" },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_1,
+ 0xffff, 0xffff, 0, "LSI SAS2308" },
+ // Add Customer specific vender/subdevice id before generic
+ // (0xffff) vender/subdevice id.
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_2,
+ 0x8086, 0x3516, 0, "Intel(R) Integrated RAID Module RMS25JB080" },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_2,
+ 0x8086, 0x3517, 0, "Intel(R) Integrated RAID Module RMS25JB040" },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_2,
+ 0x8086, 0x3518, 0, "Intel(R) Integrated RAID Module RMS25KB080" },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_2,
+ 0x8086, 0x3519, 0, "Intel(R) Integrated RAID Module RMS25KB040" },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_2,
+ 0xffff, 0xffff, 0, "LSI SAS2308" },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_3,
+ 0xffff, 0xffff, 0, "LSI SAS2308" },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SSS6200,
+ 0xffff, 0xffff, MPS_FLAGS_WD_AVAILABLE, "LSI SSS6200" },
{ 0, 0, 0, 0, 0, NULL }
};
@@ -161,7 +172,7 @@ mps_pci_probe(device_t dev)
if ((id = mps_find_ident(dev)) != NULL) {
device_set_desc(dev, id->desc);
- return (BUS_PROBE_DEFAULT);
+ return (BUS_PROBE_VENDOR);
}
return (ENXIO);
}
@@ -205,7 +216,7 @@ mps_pci_attach(device_t dev)
sc->mps_bhandle = rman_get_bushandle(sc->mps_regs_resource);
/* Allocate the parent DMA tag */
- if (bus_dma_tag_create( NULL, /* parent */
+ if (bus_dma_tag_create( bus_get_dma_tag(dev), /* parent */
1, 0, /* algnmnt, boundary */
BUS_SPACE_MAXADDR, /* lowaddr */
BUS_SPACE_MAXADDR, /* highaddr */
@@ -235,10 +246,10 @@ mps_pci_setup_interrupts(struct mps_softc *sc)
dev = sc->mps_dev;
error = ENXIO;
- if ((mps_disable_msix == 0) &&
+ if ((sc->disable_msix == 0) &&
((msgs = pci_msix_count(dev)) >= MPS_MSI_COUNT))
error = mps_alloc_msix(sc, MPS_MSI_COUNT);
- if ((error != 0) && (mps_disable_msi == 0) &&
+ if ((error != 0) && (sc->disable_msi == 0) &&
((msgs = pci_msi_count(dev)) >= MPS_MSI_COUNT))
error = mps_alloc_msi(sc, MPS_MSI_COUNT);
@@ -362,3 +373,20 @@ mps_alloc_msi(struct mps_softc *sc, int msgs)
return (error);
}
+int
+mps_pci_restore(struct mps_softc *sc)
+{
+ struct pci_devinfo *dinfo;
+
+ mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+
+ dinfo = device_get_ivars(sc->mps_dev);
+ if (dinfo == NULL) {
+ mps_dprint(sc, MPS_FAULT, "%s: NULL dinfo\n", __func__);
+ return (EINVAL);
+ }
+
+ pci_cfg_restore(sc->mps_dev, dinfo);
+ return (0);
+}
+
diff --git a/sys/dev/mps/mps_sas.c b/sys/dev/mps/mps_sas.c
index c253ee0..fb57b63 100644
--- a/sys/dev/mps/mps_sas.c
+++ b/sys/dev/mps/mps_sas.c
@@ -23,12 +23,42 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ *
+ * $FreeBSD$
+ */
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/* Communications core for LSI MPT2 */
+/* TODO Move headers to mpsvar */
#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
@@ -41,15 +71,21 @@ __FBSDID("$FreeBSD$");
#include <sys/malloc.h>
#include <sys/uio.h>
#include <sys/sysctl.h>
-#include <sys/sglist.h>
#include <sys/endian.h>
+#include <sys/queue.h>
+#include <sys/kthread.h>
+#include <sys/taskqueue.h>
+#include <sys/sbuf.h>
#include <machine/bus.h>
#include <machine/resource.h>
#include <sys/rman.h>
+#include <machine/stdarg.h>
+
#include <cam/cam.h>
#include <cam/cam_ccb.h>
+#include <cam/cam_xpt.h>
#include <cam/cam_debug.h>
#include <cam/cam_sim.h>
#include <cam/cam_xpt_sim.h>
@@ -67,426 +103,319 @@ __FBSDID("$FreeBSD$");
#include <dev/mps/mpi/mpi2_sas.h>
#include <dev/mps/mpi/mpi2_cnfg.h>
#include <dev/mps/mpi/mpi2_init.h>
+#include <dev/mps/mpi/mpi2_tool.h>
+#include <dev/mps/mps_ioctl.h>
#include <dev/mps/mpsvar.h>
#include <dev/mps/mps_table.h>
+#include <dev/mps/mps_sas.h>
-struct mpssas_target {
- uint16_t handle;
- uint8_t linkrate;
- uint64_t devname;
- uint64_t sasaddr;
- uint32_t devinfo;
- uint16_t encl_handle;
- uint16_t encl_slot;
- uint16_t parent_handle;
- int flags;
-#define MPSSAS_TARGET_INABORT (1 << 0)
-#define MPSSAS_TARGET_INRESET (1 << 1)
-#define MPSSAS_TARGET_INCHIPRESET (1 << 2)
-#define MPSSAS_TARGET_INRECOVERY 0x7
- uint16_t tid;
-};
-
-struct mpssas_softc {
- struct mps_softc *sc;
- u_int flags;
-#define MPSSAS_IN_DISCOVERY (1 << 0)
-#define MPSSAS_IN_STARTUP (1 << 1)
-#define MPSSAS_DISCOVERY_TIMEOUT_PENDING (1 << 2)
-#define MPSSAS_QUEUE_FROZEN (1 << 3)
- struct mpssas_target *targets;
- struct cam_devq *devq;
- struct cam_sim *sim;
- struct cam_path *path;
- struct intr_config_hook sas_ich;
- struct callout discovery_callout;
- u_int discovery_timeouts;
- struct mps_event_handle *mpssas_eh;
-};
+#define MPSSAS_DISCOVERY_TIMEOUT 20
+#define MPSSAS_MAX_DISCOVERY_TIMEOUTS 10 /* 200 seconds */
-struct mpssas_devprobe {
- struct mps_config_params params;
- u_int state;
-#define MPSSAS_PROBE_DEV1 0x01
-#define MPSSAS_PROBE_DEV2 0x02
-#define MPSSAS_PROBE_PHY 0x03
-#define MPSSAS_PROBE_EXP 0x04
-#define MPSSAS_PROBE_PHY2 0x05
-#define MPSSAS_PROBE_EXP2 0x06
- struct mpssas_target target;
+/*
+ * static array to check SCSI OpCode for EEDP protection bits
+ */
+#define PRO_R MPI2_SCSIIO_EEDPFLAGS_CHECK_REMOVE_OP
+#define PRO_W MPI2_SCSIIO_EEDPFLAGS_INSERT_OP
+#define PRO_V MPI2_SCSIIO_EEDPFLAGS_INSERT_OP
+static uint8_t op_code_prot[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, PRO_R, 0, PRO_W, 0, 0, 0, PRO_W, PRO_V,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, PRO_W, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, PRO_R, 0, PRO_W, 0, 0, 0, PRO_W, PRO_V,
+ 0, 0, 0, PRO_W, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, PRO_R, 0, PRO_W, 0, 0, 0, PRO_W, PRO_V,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
-#define MPSSAS_DISCOVERY_TIMEOUT 20
-#define MPSSAS_MAX_DISCOVERY_TIMEOUTS 10 /* 200 seconds */
+MALLOC_DEFINE(M_MPSSAS, "MPSSAS", "MPS SAS memory");
-static MALLOC_DEFINE(M_MPSSAS, "MPSSAS", "MPS SAS memory");
-
-static __inline int mpssas_set_lun(uint8_t *lun, u_int ccblun);
-static struct mpssas_target * mpssas_alloc_target(struct mpssas_softc *,
- struct mpssas_target *);
-static struct mpssas_target * mpssas_find_target(struct mpssas_softc *, int,
- uint16_t);
-static void mpssas_announce_device(struct mpssas_softc *,
- struct mpssas_target *);
-static void mpssas_startup(void *data);
-static void mpssas_discovery_end(struct mpssas_softc *sassc);
+static struct mpssas_target * mpssas_find_target_by_handle(struct mpssas_softc *, int, uint16_t);
static void mpssas_discovery_timeout(void *data);
-static void mpssas_prepare_remove(struct mpssas_softc *,
- MPI2_EVENT_SAS_TOPO_PHY_ENTRY *);
static void mpssas_remove_device(struct mps_softc *, struct mps_command *);
static void mpssas_remove_complete(struct mps_softc *, struct mps_command *);
static void mpssas_action(struct cam_sim *sim, union ccb *ccb);
static void mpssas_poll(struct cam_sim *sim);
-static void mpssas_probe_device(struct mps_softc *sc, uint16_t handle);
-static void mpssas_probe_device_complete(struct mps_softc *sc,
- struct mps_config_params *params);
static void mpssas_scsiio_timeout(void *data);
static void mpssas_abort_complete(struct mps_softc *sc, struct mps_command *cm);
-static void mpssas_recovery(struct mps_softc *, struct mps_command *);
-static int mpssas_map_tm_request(struct mps_softc *sc, struct mps_command *cm);
-static void mpssas_issue_tm_request(struct mps_softc *sc,
- struct mps_command *cm);
-static void mpssas_tm_complete(struct mps_softc *sc, struct mps_command *cm,
- int error);
-static int mpssas_complete_tm_request(struct mps_softc *sc,
- struct mps_command *cm, int free_cm);
+static void mpssas_direct_drive_io(struct mpssas_softc *sassc,
+ struct mps_command *cm, union ccb *ccb);
static void mpssas_action_scsiio(struct mpssas_softc *, union ccb *);
static void mpssas_scsiio_complete(struct mps_softc *, struct mps_command *);
+static void mpssas_action_resetdev(struct mpssas_softc *, union ccb *);
#if __FreeBSD_version >= 900026
static void mpssas_smpio_complete(struct mps_softc *sc, struct mps_command *cm);
static void mpssas_send_smpcmd(struct mpssas_softc *sassc, union ccb *ccb,
uint64_t sasaddr);
static void mpssas_action_smpio(struct mpssas_softc *sassc, union ccb *ccb);
-#endif /* __FreeBSD_version >= 900026 */
-static void mpssas_resetdev(struct mpssas_softc *, struct mps_command *);
-static void mpssas_action_resetdev(struct mpssas_softc *, union ccb *);
+#endif //FreeBSD_version >= 900026
static void mpssas_resetdev_complete(struct mps_softc *, struct mps_command *);
-static void mpssas_freeze_device(struct mpssas_softc *, struct mpssas_target *);
-static void mpssas_unfreeze_device(struct mpssas_softc *, struct mpssas_target *) __unused;
-
-/*
- * Abstracted so that the driver can be backwards and forwards compatible
- * with future versions of CAM that will provide this functionality.
- */
-#define MPS_SET_LUN(lun, ccblun) \
- mpssas_set_lun(lun, ccblun)
+static int mpssas_send_abort(struct mps_softc *sc, struct mps_command *tm, struct mps_command *cm);
+static int mpssas_send_reset(struct mps_softc *sc, struct mps_command *tm, uint8_t type);
+static void mpssas_rescan(struct mpssas_softc *sassc, union ccb *ccb);
+static void mpssas_rescan_done(struct cam_periph *periph, union ccb *done_ccb);
+static void mpssas_scanner_thread(void *arg);
+#if __FreeBSD_version >= 1000006
+static void mpssas_async(void *callback_arg, uint32_t code,
+ struct cam_path *path, void *arg);
+#else
+static void mpssas_check_eedp(struct mpssas_softc *sassc);
+static void mpssas_read_cap_done(struct cam_periph *periph, union ccb *done_ccb);
+#endif
+static int mpssas_send_portenable(struct mps_softc *sc);
+static void mpssas_portenable_complete(struct mps_softc *sc,
+ struct mps_command *cm);
-static __inline int
-mpssas_set_lun(uint8_t *lun, u_int ccblun)
+static struct mpssas_target *
+mpssas_find_target_by_handle(struct mpssas_softc *sassc, int start, uint16_t handle)
{
- uint64_t *newlun;
-
- newlun = (uint64_t *)lun;
- *newlun = 0;
- if (ccblun <= 0xff) {
- /* Peripheral device address method, LUN is 0 to 255 */
- lun[1] = ccblun;
- } else if (ccblun <= 0x3fff) {
- /* Flat space address method, LUN is <= 16383 */
- scsi_ulto2b(ccblun, lun);
- lun[0] |= 0x40;
- } else if (ccblun <= 0xffffff) {
- /* Extended flat space address method, LUN is <= 16777215 */
- scsi_ulto3b(ccblun, &lun[1]);
- /* Extended Flat space address method */
- lun[0] = 0xc0;
- /* Length = 1, i.e. LUN is 3 bytes long */
- lun[0] |= 0x10;
- /* Extended Address Method */
- lun[0] |= 0x02;
- } else {
- return (EINVAL);
+ struct mpssas_target *target;
+ int i;
+
+ for (i = start; i < sassc->sc->facts->MaxTargets; i++) {
+ target = &sassc->targets[i];
+ if (target->handle == handle)
+ return (target);
}
- return (0);
+ return (NULL);
}
-static struct mpssas_target *
-mpssas_alloc_target(struct mpssas_softc *sassc, struct mpssas_target *probe)
+/* we need to freeze the simq during attach and diag reset, to avoid failing
+ * commands before device handles have been found by discovery. Since
+ * discovery involves reading config pages and possibly sending commands,
+ * discovery actions may continue even after we receive the end of discovery
+ * event, so refcount discovery actions instead of assuming we can unfreeze
+ * the simq when we get the event.
+ */
+void
+mpssas_startup_increment(struct mpssas_softc *sassc)
{
- struct mpssas_target *target;
- int start;
-
- mps_dprint(sassc->sc, MPS_TRACE, "%s\n", __func__);
-
- /*
- * If it's not a sata or sas target, CAM won't be able to see it. Put
- * it into a high-numbered slot so that it's accessible but not
- * interrupting the target numbering sequence of real drives.
- */
- if ((probe->devinfo & (MPI2_SAS_DEVICE_INFO_SSP_TARGET |
- MPI2_SAS_DEVICE_INFO_STP_TARGET | MPI2_SAS_DEVICE_INFO_SATA_DEVICE))
- == 0) {
- start = 200;
- } else {
- /*
- * Use the enclosure number and slot number as a hint for target
- * numbering. If that doesn't produce a sane result, search the
- * entire space.
- */
-#if 0
- start = probe->encl_handle * 16 + probe->encl_slot;
-#else
- start = probe->encl_slot;
-#endif
- if (start >= sassc->sc->facts->MaxTargets)
- start = 0;
+ if ((sassc->flags & MPSSAS_IN_STARTUP) != 0) {
+ if (sassc->startup_refcount++ == 0) {
+ /* just starting, freeze the simq */
+ mps_dprint(sassc->sc, MPS_INFO,
+ "%s freezing simq\n", __func__);
+ xpt_freeze_simq(sassc->sim, 1);
+ }
+ mps_dprint(sassc->sc, MPS_TRACE, "%s refcount %u\n", __func__,
+ sassc->startup_refcount);
}
+}
- target = mpssas_find_target(sassc, start, 0);
+void
+mpssas_startup_decrement(struct mpssas_softc *sassc)
+{
+ if ((sassc->flags & MPSSAS_IN_STARTUP) != 0) {
+ if (--sassc->startup_refcount == 0) {
+ /* finished all discovery-related actions, release
+ * the simq and rescan for the latest topology.
+ */
+ mps_dprint(sassc->sc, MPS_INFO,
+ "%s releasing simq\n", __func__);
+ sassc->flags &= ~MPSSAS_IN_STARTUP;
+ xpt_release_simq(sassc->sim, 1);
+ mpssas_rescan_target(sassc->sc, NULL);
+ }
+ mps_dprint(sassc->sc, MPS_TRACE, "%s refcount %u\n", __func__,
+ sassc->startup_refcount);
+ }
+}
- /*
- * Nothing found on the first pass, try a second pass that searches the
- * entire space.
- */
- if (target == NULL)
- target = mpssas_find_target(sassc, 0, 0);
+/* LSI's firmware requires us to stop sending commands when we're doing task
+ * management, so refcount the TMs and keep the simq frozen when any are in
+ * use.
+ */
+struct mps_command *
+mpssas_alloc_tm(struct mps_softc *sc)
+{
+ struct mps_command *tm;
- return (target);
+ tm = mps_alloc_high_priority_command(sc);
+ if (tm != NULL) {
+ if (sc->sassc->tm_count++ == 0) {
+ mps_printf(sc, "%s freezing simq\n", __func__);
+ xpt_freeze_simq(sc->sassc->sim, 1);
+ }
+ mps_dprint(sc, MPS_TRACE, "%s tm_count %u\n", __func__,
+ sc->sassc->tm_count);
+ }
+ return tm;
}
-static struct mpssas_target *
-mpssas_find_target(struct mpssas_softc *sassc, int start, uint16_t handle)
+void
+mpssas_free_tm(struct mps_softc *sc, struct mps_command *tm)
{
- struct mpssas_target *target;
- int i;
+ if (tm == NULL)
+ return;
- for (i = start; i < sassc->sc->facts->MaxTargets; i++) {
- target = &sassc->targets[i];
- if (target->handle == handle)
- return (target);
+ /* if there are no TMs in use, we can release the simq. We use our
+ * own refcount so that it's easier for a diag reset to cleanup and
+ * release the simq.
+ */
+ if (--sc->sassc->tm_count == 0) {
+ mps_printf(sc, "%s releasing simq\n", __func__);
+ xpt_release_simq(sc->sassc->sim, 1);
}
+ mps_dprint(sc, MPS_TRACE, "%s tm_count %u\n", __func__,
+ sc->sassc->tm_count);
- return (NULL);
+ mps_free_high_priority_command(sc, tm);
}
-/*
- * Start the probe sequence for a given device handle. This will not
- * block.
- */
-static void
-mpssas_probe_device(struct mps_softc *sc, uint16_t handle)
+
+void
+mpssas_rescan_target(struct mps_softc *sc, struct mpssas_target *targ)
{
- struct mpssas_devprobe *probe;
- struct mps_config_params *params;
- MPI2_CONFIG_EXTENDED_PAGE_HEADER *hdr;
- int error;
+ struct mpssas_softc *sassc = sc->sassc;
+ path_id_t pathid;
+ target_id_t targetid;
+ union ccb *ccb;
- mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+ pathid = cam_sim_path(sassc->sim);
+ if (targ == NULL)
+ targetid = CAM_TARGET_WILDCARD;
+ else
+ targetid = targ - sassc->targets;
- probe = malloc(sizeof(*probe), M_MPSSAS, M_NOWAIT | M_ZERO);
- if (probe == NULL) {
- mps_dprint(sc, MPS_FAULT, "Out of memory starting probe\n");
+ /*
+ * Allocate a CCB and schedule a rescan.
+ */
+ ccb = xpt_alloc_ccb_nowait();
+ if (ccb == NULL) {
+ mps_dprint(sc, MPS_FAULT, "unable to alloc CCB for rescan\n");
return;
}
- params = &probe->params;
- hdr = &params->hdr.Ext;
-
- params->action = MPI2_CONFIG_ACTION_PAGE_HEADER;
- params->page_address = MPI2_SAS_DEVICE_PGAD_FORM_HANDLE | handle;
- hdr->ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE;
- hdr->ExtPageLength = 0;
- hdr->PageNumber = 0;
- hdr->PageVersion = 0;
- params->buffer = NULL;
- params->length = 0;
- params->callback = mpssas_probe_device_complete;
- params->cbdata = probe;
- probe->target.handle = handle;
- probe->state = MPSSAS_PROBE_DEV1;
-
- if ((error = mps_read_config_page(sc, params)) != 0) {
- free(probe, M_MPSSAS);
- mps_dprint(sc, MPS_FAULT, "Failure starting device probe\n");
+
+ if (xpt_create_path(&ccb->ccb_h.path, xpt_periph, pathid,
+ targetid, CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
+ mps_dprint(sc, MPS_FAULT, "unable to create path for rescan\n");
+ xpt_free_ccb(ccb);
return;
}
+
+ /* XXX Hardwired to scan the bus for now */
+ ccb->ccb_h.func_code = XPT_SCAN_BUS;
+ mps_dprint(sc, MPS_TRACE, "%s targetid %u\n", __func__, targetid);
+ mpssas_rescan(sassc, ccb);
}
static void
-mpssas_probe_device_complete(struct mps_softc *sc,
- struct mps_config_params *params)
+mpssas_log_command(struct mps_command *cm, const char *fmt, ...)
{
- MPI2_CONFIG_EXTENDED_PAGE_HEADER *hdr;
- struct mpssas_devprobe *probe;
- int error;
+ struct sbuf sb;
+ va_list ap;
+ char str[192];
+ char path_str[64];
- mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+ if (cm == NULL)
+ return;
- hdr = &params->hdr.Ext;
- probe = params->cbdata;
-
- switch (probe->state) {
- case MPSSAS_PROBE_DEV1:
- case MPSSAS_PROBE_PHY:
- case MPSSAS_PROBE_EXP:
- if (params->status != MPI2_IOCSTATUS_SUCCESS) {
- mps_dprint(sc, MPS_FAULT,
- "Probe Failure 0x%x state %d\n", params->status,
- probe->state);
- free(probe, M_MPSSAS);
- return;
- }
- params->action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- params->length = hdr->ExtPageLength * 4;
- params->buffer = malloc(params->length, M_MPSSAS,
- M_ZERO|M_NOWAIT);
- if (params->buffer == NULL) {
- mps_dprint(sc, MPS_FAULT, "Out of memory at state "
- "0x%x, size 0x%x\n", probe->state, params->length);
- free(probe, M_MPSSAS);
- return;
- }
- if (probe->state == MPSSAS_PROBE_DEV1)
- probe->state = MPSSAS_PROBE_DEV2;
- else if (probe->state == MPSSAS_PROBE_PHY)
- probe->state = MPSSAS_PROBE_PHY2;
- else if (probe->state == MPSSAS_PROBE_EXP)
- probe->state = MPSSAS_PROBE_EXP2;
- error = mps_read_config_page(sc, params);
- break;
- case MPSSAS_PROBE_DEV2:
- {
- MPI2_CONFIG_PAGE_SAS_DEV_0 *buf;
-
- if (params->status != MPI2_IOCSTATUS_SUCCESS) {
- mps_dprint(sc, MPS_FAULT,
- "Probe Failure 0x%x state %d\n", params->status,
- probe->state);
- free(params->buffer, M_MPSSAS);
- free(probe, M_MPSSAS);
- return;
- }
- buf = params->buffer;
- mps_print_sasdev0(sc, buf);
-
- probe->target.devname = mps_to_u64(&buf->DeviceName);
- probe->target.devinfo = buf->DeviceInfo;
- probe->target.encl_handle = buf->EnclosureHandle;
- probe->target.encl_slot = buf->Slot;
- probe->target.sasaddr = mps_to_u64(&buf->SASAddress);
- probe->target.parent_handle = buf->ParentDevHandle;
-
- if (buf->DeviceInfo & MPI2_SAS_DEVICE_INFO_DIRECT_ATTACH) {
- params->page_address =
- MPI2_SAS_PHY_PGAD_FORM_PHY_NUMBER | buf->PhyNum;
- hdr->ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_PHY;
- hdr->PageNumber = 0;
- probe->state = MPSSAS_PROBE_PHY;
- } else {
- params->page_address =
- MPI2_SAS_EXPAND_PGAD_FORM_HNDL_PHY_NUM |
- buf->ParentDevHandle | (buf->PhyNum << 16);
- hdr->ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER;
- hdr->PageNumber = 1;
- probe->state = MPSSAS_PROBE_EXP;
+ sbuf_new(&sb, str, sizeof(str), 0);
+
+ va_start(ap, fmt);
+
+ if (cm->cm_ccb != NULL) {
+ xpt_path_string(cm->cm_ccb->csio.ccb_h.path, path_str,
+ sizeof(path_str));
+ sbuf_cat(&sb, path_str);
+ if (cm->cm_ccb->ccb_h.func_code == XPT_SCSI_IO) {
+ scsi_command_string(&cm->cm_ccb->csio, &sb);
+ sbuf_printf(&sb, "length %d ",
+ cm->cm_ccb->csio.dxfer_len);
}
- params->action = MPI2_CONFIG_ACTION_PAGE_HEADER;
- hdr->ExtPageLength = 0;
- hdr->PageVersion = 0;
- params->buffer = NULL;
- params->length = 0;
- free(buf, M_MPSSAS);
- error = mps_read_config_page(sc, params);
- break;
}
- case MPSSAS_PROBE_PHY2:
- case MPSSAS_PROBE_EXP2:
- {
- MPI2_CONFIG_PAGE_SAS_PHY_0 *phy;
- MPI2_CONFIG_PAGE_EXPANDER_1 *exp;
- struct mpssas_softc *sassc;
- struct mpssas_target *targ;
- char devstring[80];
- uint16_t handle;
-
- if (params->status != MPI2_IOCSTATUS_SUCCESS) {
- mps_dprint(sc, MPS_FAULT,
- "Probe Failure 0x%x state %d\n", params->status,
- probe->state);
- free(params->buffer, M_MPSSAS);
- free(probe, M_MPSSAS);
- return;
- }
+ else {
+ sbuf_printf(&sb, "(noperiph:%s%d:%u:%u:%u): ",
+ cam_sim_name(cm->cm_sc->sassc->sim),
+ cam_sim_unit(cm->cm_sc->sassc->sim),
+ cam_sim_bus(cm->cm_sc->sassc->sim),
+ cm->cm_targ ? cm->cm_targ->tid : 0xFFFFFFFF,
+ cm->cm_lun);
+ }
- if (probe->state == MPSSAS_PROBE_PHY2) {
- phy = params->buffer;
- mps_print_sasphy0(sc, phy);
- probe->target.linkrate = phy->NegotiatedLinkRate & 0xf;
- } else {
- exp = params->buffer;
- mps_print_expander1(sc, exp);
- probe->target.linkrate = exp->NegotiatedLinkRate & 0xf;
- }
- free(params->buffer, M_MPSSAS);
+ sbuf_printf(&sb, "SMID %u ", cm->cm_desc.Default.SMID);
+ sbuf_vprintf(&sb, fmt, ap);
+ sbuf_finish(&sb);
+ printf("%s", sbuf_data(&sb));
- sassc = sc->sassc;
- handle = probe->target.handle;
- if ((targ = mpssas_find_target(sassc, 0, handle)) != NULL) {
- mps_printf(sc, "Ignoring dup device handle 0x%04x\n",
- handle);
- free(probe, M_MPSSAS);
- return;
- }
- if ((targ = mpssas_alloc_target(sassc, &probe->target)) == NULL) {
- mps_printf(sc, "Target table overflow, handle 0x%04x\n",
- handle);
- free(probe, M_MPSSAS);
- return;
- }
+ va_end(ap);
+}
- *targ = probe->target; /* Copy the attributes */
- targ->tid = targ - sassc->targets;
- mps_describe_devinfo(targ->devinfo, devstring, 80);
- if (bootverbose)
- mps_printf(sc, "Found device <%s> <%s> <0x%04x> "
- "<%d/%d>\n", devstring,
- mps_describe_table(mps_linkrate_names,
- targ->linkrate), targ->handle, targ->encl_handle,
- targ->encl_slot);
-
- free(probe, M_MPSSAS);
- mpssas_announce_device(sassc, targ);
- break;
- }
- default:
- printf("what?\n");
+static void
+mpssas_lost_target(struct mps_softc *sc, struct mpssas_target *targ)
+{
+ struct mpssas_softc *sassc = sc->sassc;
+ path_id_t pathid = cam_sim_path(sassc->sim);
+ struct cam_path *path;
+
+ mps_printf(sc, "%s targetid %u\n", __func__, targ->tid);
+ if (xpt_create_path(&path, NULL, pathid, targ->tid, 0) != CAM_REQ_CMP) {
+ mps_printf(sc, "unable to create path for lost target %d\n",
+ targ->tid);
+ return;
}
+
+ xpt_async(AC_LOST_DEVICE, path, NULL);
+ xpt_free_path(path);
}
/*
- * The MPT2 firmware performs debounce on the link to avoid transient link errors
- * and false removals. When it does decide that link has been lost and a device
- * need to go away, it expects that the host will perform a target reset and then
- * an op remove. The reset has the side-effect of aborting any outstanding
- * requests for the device, which is required for the op-remove to succeed. It's
- * not clear if the host should check for the device coming back alive after the
- * reset.
+ * The MPT2 firmware performs debounce on the link to avoid transient link
+ * errors and false removals. When it does decide that link has been lost
+ * and a device need to go away, it expects that the host will perform a
+ * target reset and then an op remove. The reset has the side-effect of
+ * aborting any outstanding requests for the device, which is required for
+ * the op-remove to succeed. It's not clear if the host should check for
+ * the device coming back alive after the reset.
*/
-static void
-mpssas_prepare_remove(struct mpssas_softc *sassc, MPI2_EVENT_SAS_TOPO_PHY_ENTRY *phy)
+void
+mpssas_prepare_remove(struct mpssas_softc *sassc, uint16_t handle)
{
MPI2_SCSI_TASK_MANAGE_REQUEST *req;
struct mps_softc *sc;
struct mps_command *cm;
struct mpssas_target *targ = NULL;
- uint16_t handle;
mps_dprint(sassc->sc, MPS_TRACE, "%s\n", __func__);
- handle = phy->AttachedDevHandle;
- targ = mpssas_find_target(sassc, 0, handle);
- if (targ == NULL)
+ /*
+ * If this is a WD controller, determine if the disk should be exposed
+ * to the OS or not. If disk should be exposed, return from this
+ * function without doing anything.
+ */
+ sc = sassc->sc;
+ if ((sc->mps_flags & MPS_FLAGS_WD_AVAILABLE) && (sc->WD_hide_expose ==
+ MPS_WD_EXPOSE_ALWAYS)) {
+ return;
+ }
+
+ targ = mpssas_find_target_by_handle(sassc, 0, handle);
+ if (targ == NULL) {
+ /* FIXME: what is the action? */
/* We don't know about this device? */
+ printf("%s: invalid handle 0x%x \n", __func__, handle);
return;
+ }
- sc = sassc->sc;
- cm = mps_alloc_command(sc);
+ targ->flags |= MPSSAS_TARGET_INREMOVAL;
+
+ cm = mpssas_alloc_tm(sc);
if (cm == NULL) {
- mps_printf(sc, "comand alloc failure in mpssas_prepare_remove\n");
+ mps_printf(sc, "%s: command alloc failure\n", __func__);
return;
}
- mps_dprint(sc, MPS_INFO, "Preparing to remove target %d\n", targ->tid);
+ mpssas_lost_target(sc, targ);
req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)cm->cm_req;
memset(req, 0, sizeof(*req));
@@ -497,15 +426,16 @@ mpssas_prepare_remove(struct mpssas_softc *sassc, MPI2_EVENT_SAS_TOPO_PHY_ENTRY
/* SAS Hard Link Reset / SATA Link Reset */
req->MsgFlags = MPI2_SCSITASKMGMT_MSGFLAGS_LINK_RESET;
+ cm->cm_targ = targ;
cm->cm_data = NULL;
- cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+ cm->cm_desc.HighPriority.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY;
cm->cm_complete = mpssas_remove_device;
- cm->cm_targ = targ;
- mpssas_issue_tm_request(sc, cm);
+ cm->cm_complete_data = (void *)(uintptr_t)handle;
+ mps_map_command(sc, cm);
}
static void
-mpssas_remove_device(struct mps_softc *sc, struct mps_command *cm)
+mpssas_remove_device(struct mps_softc *sc, struct mps_command *tm)
{
MPI2_SCSI_TASK_MANAGE_REPLY *reply;
MPI2_SAS_IOUNIT_CONTROL_REQUEST *req;
@@ -515,150 +445,122 @@ mpssas_remove_device(struct mps_softc *sc, struct mps_command *cm)
mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
- reply = (MPI2_SCSI_TASK_MANAGE_REPLY *)cm->cm_reply;
- handle = cm->cm_targ->handle;
-
- mpssas_complete_tm_request(sc, cm, /*free_cm*/ 0);
+ reply = (MPI2_SCSI_TASK_MANAGE_REPLY *)tm->cm_reply;
+ handle = (uint16_t)(uintptr_t)tm->cm_complete_data;
+ targ = tm->cm_targ;
/*
* Currently there should be no way we can hit this case. It only
* happens when we have a failure to allocate chain frames, and
* task management commands don't have S/G lists.
*/
- if ((cm->cm_flags & MPS_CM_FLAGS_ERROR_MASK) != 0) {
+ if ((tm->cm_flags & MPS_CM_FLAGS_ERROR_MASK) != 0) {
mps_printf(sc, "%s: cm_flags = %#x for remove of handle %#04x! "
- "This should not happen!\n", __func__, cm->cm_flags,
+ "This should not happen!\n", __func__, tm->cm_flags,
handle);
+ mpssas_free_tm(sc, tm);
+ return;
+ }
+
+ if (reply == NULL) {
+ /* XXX retry the remove after the diag reset completes? */
+ mps_printf(sc, "%s NULL reply reseting device 0x%04x\n",
+ __func__, handle);
+ mpssas_free_tm(sc, tm);
return;
}
if (reply->IOCStatus != MPI2_IOCSTATUS_SUCCESS) {
- mps_printf(sc, "Failure 0x%x reseting device 0x%04x\n",
+ mps_printf(sc, "IOCStatus = 0x%x while resetting device 0x%x\n",
reply->IOCStatus, handle);
- mps_free_command(sc, cm);
+ mpssas_free_tm(sc, tm);
return;
}
mps_dprint(sc, MPS_INFO, "Reset aborted %u commands\n",
reply->TerminationCount);
- mps_free_reply(sc, cm->cm_reply_data);
+ mps_free_reply(sc, tm->cm_reply_data);
+ tm->cm_reply = NULL; /* Ensures the the reply won't get re-freed */
/* Reuse the existing command */
- req = (MPI2_SAS_IOUNIT_CONTROL_REQUEST *)cm->cm_req;
+ req = (MPI2_SAS_IOUNIT_CONTROL_REQUEST *)tm->cm_req;
memset(req, 0, sizeof(*req));
req->Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL;
req->Operation = MPI2_SAS_OP_REMOVE_DEVICE;
req->DevHandle = handle;
- cm->cm_data = NULL;
- cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
- cm->cm_flags &= ~MPS_CM_FLAGS_COMPLETE;
- cm->cm_complete = mpssas_remove_complete;
+ tm->cm_data = NULL;
+ tm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+ tm->cm_complete = mpssas_remove_complete;
+ tm->cm_complete_data = (void *)(uintptr_t)handle;
- mps_map_command(sc, cm);
+ mps_map_command(sc, tm);
- mps_dprint(sc, MPS_INFO, "clearing target handle 0x%04x\n", handle);
- TAILQ_FOREACH_SAFE(cm, &sc->io_list, cm_link, next_cm) {
+ mps_dprint(sc, MPS_INFO, "clearing target %u handle 0x%04x\n",
+ targ->tid, handle);
+ TAILQ_FOREACH_SAFE(tm, &targ->commands, cm_link, next_cm) {
union ccb *ccb;
- if (cm->cm_targ->handle != handle)
- continue;
-
- mps_dprint(sc, MPS_INFO, "Completing missed command %p\n", cm);
- ccb = cm->cm_complete_data;
+ mps_dprint(sc, MPS_INFO, "Completing missed command %p\n", tm);
+ ccb = tm->cm_complete_data;
ccb->ccb_h.status = CAM_DEV_NOT_THERE;
- mpssas_scsiio_complete(sc, cm);
- }
- targ = mpssas_find_target(sc->sassc, 0, handle);
- if (targ != NULL) {
- targ->handle = 0x0;
- mpssas_announce_device(sc->sassc, targ);
+ mpssas_scsiio_complete(sc, tm);
}
}
static void
-mpssas_remove_complete(struct mps_softc *sc, struct mps_command *cm)
+mpssas_remove_complete(struct mps_softc *sc, struct mps_command *tm)
{
MPI2_SAS_IOUNIT_CONTROL_REPLY *reply;
+ uint16_t handle;
+ struct mpssas_target *targ;
mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
- reply = (MPI2_SAS_IOUNIT_CONTROL_REPLY *)cm->cm_reply;
-
- mps_printf(sc, "mpssas_remove_complete on target 0x%04x,"
- " IOCStatus= 0x%x\n", cm->cm_targ->tid, reply->IOCStatus);
-
- mps_free_command(sc, cm);
-}
-
-static void
-mpssas_evt_handler(struct mps_softc *sc, uintptr_t data,
- MPI2_EVENT_NOTIFICATION_REPLY *event)
-{
- struct mpssas_softc *sassc;
-
- mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
-
- sassc = sc->sassc;
- mps_print_evt_sas(sc, event);
-
- switch (event->Event) {
- case MPI2_EVENT_SAS_DISCOVERY:
- {
- MPI2_EVENT_DATA_SAS_DISCOVERY *data;
+ reply = (MPI2_SAS_IOUNIT_CONTROL_REPLY *)tm->cm_reply;
+ handle = (uint16_t)(uintptr_t)tm->cm_complete_data;
- data = (MPI2_EVENT_DATA_SAS_DISCOVERY *)&event->EventData;
+ /*
+ * Currently there should be no way we can hit this case. It only
+ * happens when we have a failure to allocate chain frames, and
+ * task management commands don't have S/G lists.
+ */
+ if ((tm->cm_flags & MPS_CM_FLAGS_ERROR_MASK) != 0) {
+ mps_printf(sc, "%s: cm_flags = %#x for remove of handle %#04x! "
+ "This should not happen!\n", __func__, tm->cm_flags,
+ handle);
+ mpssas_free_tm(sc, tm);
+ return;
+ }
- if (data->ReasonCode & MPI2_EVENT_SAS_DISC_RC_STARTED)
- mps_dprint(sc, MPS_TRACE,"SAS discovery start event\n");
- if (data->ReasonCode & MPI2_EVENT_SAS_DISC_RC_COMPLETED) {
- mps_dprint(sc, MPS_TRACE, "SAS discovery end event\n");
- sassc->flags &= ~MPSSAS_IN_DISCOVERY;
- mpssas_discovery_end(sassc);
- }
- break;
+ if (reply == NULL) {
+ /* most likely a chip reset */
+ mps_printf(sc, "%s NULL reply removing device 0x%04x\n",
+ __func__, handle);
+ mpssas_free_tm(sc, tm);
+ return;
}
- case MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST:
- {
- MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST *data;
- MPI2_EVENT_SAS_TOPO_PHY_ENTRY *phy;
- int i;
-
- data = (MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST *)
- &event->EventData;
-
- if (data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_ADDED) {
- if (bootverbose)
- printf("Expander found at enclosure %d\n",
- data->EnclosureHandle);
- mpssas_probe_device(sc, data->ExpanderDevHandle);
- }
- for (i = 0; i < data->NumEntries; i++) {
- phy = &data->PHY[i];
- switch (phy->PhyStatus & MPI2_EVENT_SAS_TOPO_RC_MASK) {
- case MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED:
- mpssas_probe_device(sc, phy->AttachedDevHandle);
- break;
- case MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING:
- mpssas_prepare_remove(sassc, phy);
- break;
- case MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED:
- case MPI2_EVENT_SAS_TOPO_RC_NO_CHANGE:
- case MPI2_EVENT_SAS_TOPO_RC_DELAY_NOT_RESPONDING:
- default:
- break;
- }
- }
+ mps_printf(sc, "%s on handle 0x%04x, IOCStatus= 0x%x\n", __func__,
+ handle, reply->IOCStatus);
- break;
- }
- case MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE:
- break;
- default:
- break;
+ /*
+ * Don't clear target if remove fails because things will get confusing.
+ * Leave the devname and sasaddr intact so that we know to avoid reusing
+ * this target id if possible, and so we can assign the same target id
+ * to this device if it comes back in the future.
+ */
+ if (reply->IOCStatus == MPI2_IOCSTATUS_SUCCESS) {
+ targ = tm->cm_targ;
+ targ->handle = 0x0;
+ targ->encl_handle = 0x0;
+ targ->encl_slot = 0x0;
+ targ->exp_dev_handle = 0x0;
+ targ->phy_num = 0x0;
+ targ->linkrate = 0x0;
+ targ->devinfo = 0x0;
}
- mps_free_reply(sc, data);
+ mpssas_free_tm(sc, tm);
}
static int
@@ -674,6 +576,11 @@ mpssas_register_events(struct mps_softc *sc)
setbit(events, MPI2_EVENT_SAS_INIT_TABLE_OVERFLOW);
setbit(events, MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST);
setbit(events, MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE);
+ setbit(events, MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST);
+ setbit(events, MPI2_EVENT_IR_VOLUME);
+ setbit(events, MPI2_EVENT_IR_PHYSICAL_DISK);
+ setbit(events, MPI2_EVENT_IR_OPERATION_STATUS);
+ setbit(events, MPI2_EVENT_LOG_ENTRY_ADDED);
mps_register_events(sc, events, mpssas_evt_handler, NULL,
&sc->sassc->mpssas_eh);
@@ -685,8 +592,10 @@ int
mps_attach_sas(struct mps_softc *sc)
{
struct mpssas_softc *sassc;
- int error = 0;
- int num_sim_reqs;
+#if __FreeBSD_version >= 1000006
+ cam_status status;
+#endif
+ int unit, error = 0;
mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
@@ -696,42 +605,48 @@ mps_attach_sas(struct mps_softc *sc)
sc->sassc = sassc;
sassc->sc = sc;
- /*
- * Tell CAM that we can handle 5 fewer requests than we have
- * allocated. If we allow the full number of requests, all I/O
- * will halt when we run out of resources. Things work fine with
- * just 1 less request slot given to CAM than we have allocated.
- * We also need a couple of extra commands so that we can send down
- * abort, reset, etc. requests when commands time out. Otherwise
- * we could wind up in a situation with sc->num_reqs requests down
- * on the card and no way to send an abort.
- *
- * XXX KDM need to figure out why I/O locks up if all commands are
- * used.
- */
- num_sim_reqs = sc->num_reqs - 5;
-
- if ((sassc->devq = cam_simq_alloc(num_sim_reqs)) == NULL) {
+ if ((sassc->devq = cam_simq_alloc(sc->num_reqs)) == NULL) {
mps_dprint(sc, MPS_FAULT, "Cannot allocate SIMQ\n");
error = ENOMEM;
goto out;
}
+ unit = device_get_unit(sc->mps_dev);
sassc->sim = cam_sim_alloc(mpssas_action, mpssas_poll, "mps", sassc,
- device_get_unit(sc->mps_dev), &sc->mps_mtx, num_sim_reqs,
- num_sim_reqs, sassc->devq);
+ unit, &sc->mps_mtx, sc->num_reqs, sc->num_reqs, sassc->devq);
if (sassc->sim == NULL) {
mps_dprint(sc, MPS_FAULT, "Cannot allocate SIM\n");
error = EINVAL;
goto out;
}
+ TAILQ_INIT(&sassc->ev_queue);
+
+ /* Initialize taskqueue for Event Handling */
+ TASK_INIT(&sassc->ev_task, 0, mpssas_firmware_event_work, sc);
+ sassc->ev_tq = taskqueue_create("mps_taskq", M_NOWAIT | M_ZERO,
+ taskqueue_thread_enqueue, &sassc->ev_tq);
+
+ /* Run the task queue with lowest priority */
+ taskqueue_start_threads(&sassc->ev_tq, 1, 255, "%s taskq",
+ device_get_nameunit(sc->mps_dev));
+
+ TAILQ_INIT(&sassc->ccb_scanq);
+ error = mps_kproc_create(mpssas_scanner_thread, sassc,
+ &sassc->rescan_thread, 0, 0, "mps_scan%d", unit);
+ if (error) {
+ mps_printf(sc, "Error %d starting rescan thread\n", error);
+ goto out;
+ }
+
+ mps_lock(sc);
+ sassc->flags |= MPSSAS_SCANTHREAD;
+
/*
* XXX There should be a bus for every port on the adapter, but since
* we're just going to fake the topology for now, we'll pretend that
* everything is just a target on a single bus.
*/
- mps_lock(sc);
if ((error = xpt_bus_register(sassc->sim, sc->mps_dev, 0)) != 0) {
mps_dprint(sc, MPS_FAULT, "Error %d registering SCSI bus\n",
error);
@@ -744,14 +659,25 @@ mps_attach_sas(struct mps_softc *sc)
* the simq will prevent the CAM boottime scanner from running
* before discovery is complete.
*/
- sassc->flags = MPSSAS_IN_STARTUP | MPSSAS_IN_DISCOVERY;
+ sassc->flags |= MPSSAS_IN_STARTUP | MPSSAS_IN_DISCOVERY;
xpt_freeze_simq(sassc->sim, 1);
-
- mps_unlock(sc);
+ sc->sassc->startup_refcount = 0;
callout_init(&sassc->discovery_callout, 1 /*mpsafe*/);
sassc->discovery_timeouts = 0;
+ sassc->tm_count = 0;
+
+#if __FreeBSD_version >= 1000006
+ status = xpt_register_async(AC_ADVINFO_CHANGED, mpssas_async, sc, NULL);
+ if (status != CAM_REQ_CMP) {
+ mps_printf(sc, "Error %#x registering async handler for "
+ "AC_ADVINFO_CHANGED events\n", status);
+ }
+#endif
+
+ mps_unlock(sc);
+
mpssas_register_events(sc);
out:
if (error)
@@ -770,22 +696,41 @@ mps_detach_sas(struct mps_softc *sc)
return (0);
sassc = sc->sassc;
+ mps_deregister_events(sc, sassc->mpssas_eh);
+
+ /*
+ * Drain and free the event handling taskqueue with the lock
+ * unheld so that any parallel processing tasks drain properly
+ * without deadlocking.
+ */
+ if (sassc->ev_tq != NULL)
+ taskqueue_free(sassc->ev_tq);
/* Make sure CAM doesn't wedge if we had to bail out early. */
mps_lock(sc);
- if (sassc->flags & MPSSAS_IN_STARTUP)
- xpt_release_simq(sassc->sim, 1);
- mps_unlock(sc);
- if (sassc->mpssas_eh != NULL)
- mps_deregister_events(sc, sassc->mpssas_eh);
+ /* Deregister our async handler */
+#if __FreeBSD_version >= 1000006
+ xpt_register_async(0, mpssas_async, sc, NULL);
+#endif
- mps_lock(sc);
+ if (sassc->flags & MPSSAS_IN_STARTUP)
+ xpt_release_simq(sassc->sim, 1);
if (sassc->sim != NULL) {
xpt_bus_deregister(cam_sim_path(sassc->sim));
cam_sim_free(sassc->sim, FALSE);
}
+
+ if (sassc->flags & MPSSAS_SCANTHREAD) {
+ sassc->flags |= MPSSAS_SHUTDOWN;
+ wakeup(&sassc->ccb_scanq);
+
+ if (sassc->flags & MPSSAS_SCANTHREAD) {
+ msleep(&sassc->flags, &sc->mps_mtx, PRIBIO,
+ "mps_shutdown", 30 * hz);
+ }
+ }
mps_unlock(sc);
if (sassc->devq != NULL)
@@ -798,7 +743,7 @@ mps_detach_sas(struct mps_softc *sc)
return (0);
}
-static void
+void
mpssas_discovery_end(struct mpssas_softc *sassc)
{
struct mps_softc *sc = sassc->sc;
@@ -808,59 +753,28 @@ mpssas_discovery_end(struct mpssas_softc *sassc)
if (sassc->flags & MPSSAS_DISCOVERY_TIMEOUT_PENDING)
callout_stop(&sassc->discovery_callout);
- if ((sassc->flags & MPSSAS_IN_STARTUP) != 0) {
- mps_dprint(sc, MPS_INFO,
- "mpssas_discovery_end: removing confighook\n");
- sassc->flags &= ~MPSSAS_IN_STARTUP;
- xpt_release_simq(sassc->sim, 1);
- }
-#if 0
- mpssas_announce_device(sassc, NULL);
-#endif
-
}
static void
-mpssas_announce_device(struct mpssas_softc *sassc, struct mpssas_target *targ)
+mpssas_discovery_timeout(void *data)
{
- union ccb *ccb;
- int bus, tid, lun;
+ struct mpssas_softc *sassc = data;
+ struct mps_softc *sc;
- /*
- * Force a rescan, a hackish way to announce devices.
- * XXX Doing a scan on an individual device is hackish in that it
- * won't scan the LUNs.
- * XXX Does it matter if any of this fails?
- */
- bus = cam_sim_path(sassc->sim);
- if (targ != NULL) {
- tid = targ->tid;
- lun = 0;
- } else {
- tid = CAM_TARGET_WILDCARD;
- lun = CAM_LUN_WILDCARD;
- }
- ccb = xpt_alloc_ccb_nowait();
- if (ccb == NULL)
- return;
- if (xpt_create_path(&ccb->ccb_h.path, xpt_periph, bus, tid,
- CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
- xpt_free_ccb(ccb);
- return;
- }
- mps_dprint(sassc->sc, MPS_INFO, "Triggering rescan of %d:%d:-1\n",
- bus, tid);
- xpt_rescan(ccb);
-}
+ sc = sassc->sc;
+ mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
-static void
-mpssas_startup(void *data)
-{
- struct mpssas_softc *sassc = data;
+ mps_lock(sc);
+ mps_printf(sc,
+ "Timeout waiting for discovery, interrupts may not be working!\n");
+ sassc->flags &= ~MPSSAS_DISCOVERY_TIMEOUT_PENDING;
- mps_dprint(sassc->sc, MPS_TRACE, "%s\n", __func__);
+ /* Poll the hardware for events in case interrupts aren't working */
+ mps_intr_locked(sc);
+
+ mps_printf(sassc->sc,
+ "Finished polling after discovery timeout at %d\n", ticks);
- mps_lock(sassc->sc);
if ((sassc->flags & MPSSAS_IN_DISCOVERY) == 0) {
mpssas_discovery_end(sassc);
} else {
@@ -877,31 +791,8 @@ mpssas_startup(void *data)
mpssas_discovery_end(sassc);
}
}
- mps_unlock(sassc->sc);
-
- return;
-}
-
-static void
-mpssas_discovery_timeout(void *data)
-{
- struct mpssas_softc *sassc = data;
- struct mps_softc *sc;
-
- sc = sassc->sc;
- mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
-
- mps_lock(sc);
- mps_printf(sc,
- "Timeout waiting for discovery, interrupts may not be working!\n");
- sassc->flags &= ~MPSSAS_DISCOVERY_TIMEOUT_PENDING;
- /* Poll the hardware for events in case interrupts aren't working */
- mps_intr_locked(sc);
mps_unlock(sc);
-
- /* Check the status of discovery and re-arm the timeout if needed */
- mpssas_startup(sassc);
}
static void
@@ -911,8 +802,9 @@ mpssas_action(struct cam_sim *sim, union ccb *ccb)
sassc = cam_sim_softc(sim);
- mps_dprint(sassc->sc, MPS_TRACE, "%s func 0x%x\n", __func__,
+ mps_dprint(sassc->sc, MPS_TRACE, "%s func 0x%x\n", __func__,
ccb->ccb_h.func_code);
+ mtx_assert(&sassc->sc->mps_mtx, MA_OWNED);
switch (ccb->ccb_h.func_code) {
case XPT_PATH_INQ:
@@ -925,7 +817,7 @@ mpssas_action(struct cam_sim *sim, union ccb *ccb)
cpi->hba_misc = PIM_NOBUSRESET;
cpi->hba_eng_cnt = 0;
cpi->max_target = sassc->sc->facts->MaxTargets - 1;
- cpi->max_lun = 8;
+ cpi->max_lun = 0;
cpi->initiator_id = 255;
strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
strncpy(cpi->hba_vid, "LSILogic", HBA_IDLEN);
@@ -937,7 +829,12 @@ mpssas_action(struct cam_sim *sim, union ccb *ccb)
cpi->transport_version = 0;
cpi->protocol = PROTO_SCSI;
cpi->protocol_version = SCSI_REV_SPC;
- cpi->maxio = MAXPHYS;
+#if __FreeBSD_version >= 800001
+ /*
+ * XXX KDM where does this number come from?
+ */
+ cpi->maxio = 256 * 1024;
+#endif
cpi->ccb_h.status = CAM_REQ_CMP;
break;
}
@@ -989,11 +886,14 @@ mpssas_action(struct cam_sim *sim, union ccb *ccb)
ccb->ccb_h.status = CAM_REQ_CMP;
break;
case XPT_RESET_DEV:
+ mps_printf(sassc->sc, "mpssas_action XPT_RESET_DEV\n");
mpssas_action_resetdev(sassc, ccb);
return;
case XPT_RESET_BUS:
case XPT_ABORT:
case XPT_TERM_IO:
+ mps_printf(sassc->sc, "mpssas_action faking success for "
+ "abort or reset\n");
ccb->ccb_h.status = CAM_REQ_CMP;
break;
case XPT_SCSI_IO:
@@ -1003,7 +903,7 @@ mpssas_action(struct cam_sim *sim, union ccb *ccb)
case XPT_SMP_IO:
mpssas_action_smpio(sassc, ccb);
return;
-#endif /* __FreeBSD_version >= 900026 */
+#endif
default:
ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
break;
@@ -1012,349 +912,554 @@ mpssas_action(struct cam_sim *sim, union ccb *ccb)
}
-#if 0
static void
-mpssas_resettimeout_complete(struct mps_softc *sc, struct mps_command *cm)
+mpssas_announce_reset(struct mps_softc *sc, uint32_t ac_code,
+ target_id_t target_id, lun_id_t lun_id)
{
- MPI2_SCSI_TASK_MANAGE_REPLY *resp;
- uint16_t code;
-
- mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
-
- resp = (MPI2_SCSI_TASK_MANAGE_REPLY *)cm->cm_reply;
- code = resp->ResponseCode;
+ path_id_t path_id = cam_sim_path(sc->sassc->sim);
+ struct cam_path *path;
- mps_free_command(sc, cm);
- mpssas_unfreeze_device(sassc, targ);
+ mps_printf(sc, "%s code %x target %d lun %d\n", __func__,
+ ac_code, target_id, lun_id);
- if (code != MPI2_SCSITASKMGMT_RSP_TM_COMPLETE) {
- mps_reset_controller(sc);
+ if (xpt_create_path(&path, NULL,
+ path_id, target_id, lun_id) != CAM_REQ_CMP) {
+ mps_printf(sc, "unable to create path for reset "
+ "notification\n");
+ return;
}
- return;
+ xpt_async(ac_code, path, NULL);
+ xpt_free_path(path);
}
-#endif
-static void
-mpssas_scsiio_timeout(void *data)
+static void
+mpssas_complete_all_commands(struct mps_softc *sc)
{
- union ccb *ccb;
- struct mps_softc *sc;
struct mps_command *cm;
- struct mpssas_target *targ;
-#if 0
- char cdb_str[(SCSI_MAX_CDBLEN * 3) + 1];
-#endif
+ int i;
+ int completed;
- cm = (struct mps_command *)data;
- sc = cm->cm_sc;
+ mps_printf(sc, "%s\n", __func__);
+ mtx_assert(&sc->mps_mtx, MA_OWNED);
- /*
- * Run the interrupt handler to make sure it's not pending. This
- * isn't perfect because the command could have already completed
- * and been re-used, though this is unlikely.
- */
- mps_lock(sc);
- mps_intr_locked(sc);
- if (cm->cm_state == MPS_CM_STATE_FREE) {
- mps_unlock(sc);
- return;
+ /* complete all commands with a NULL reply */
+ for (i = 1; i < sc->num_reqs; i++) {
+ cm = &sc->commands[i];
+ cm->cm_reply = NULL;
+ completed = 0;
+
+ if (cm->cm_flags & MPS_CM_FLAGS_POLLED)
+ cm->cm_flags |= MPS_CM_FLAGS_COMPLETE;
+
+ if (cm->cm_complete != NULL) {
+ mpssas_log_command(cm,
+ "completing cm %p state %x ccb %p for diag reset\n",
+ cm, cm->cm_state, cm->cm_ccb);
+
+ cm->cm_complete(sc, cm);
+ completed = 1;
+ }
+
+ if (cm->cm_flags & MPS_CM_FLAGS_WAKEUP) {
+ mpssas_log_command(cm,
+ "waking up cm %p state %x ccb %p for diag reset\n",
+ cm, cm->cm_state, cm->cm_ccb);
+ wakeup(cm);
+ completed = 1;
+ }
+
+ if ((completed == 0) && (cm->cm_state != MPS_CM_STATE_FREE)) {
+ /* this should never happen, but if it does, log */
+ mpssas_log_command(cm,
+ "cm %p state %x flags 0x%x ccb %p during diag "
+ "reset\n", cm, cm->cm_state, cm->cm_flags,
+ cm->cm_ccb);
+ }
}
+}
- ccb = cm->cm_complete_data;
- targ = cm->cm_targ;
- if (targ == 0x00)
- /* Driver bug */
- targ = &sc->sassc->targets[ccb->ccb_h.target_id];
+void
+mpssas_handle_reinit(struct mps_softc *sc)
+{
+ int i;
- xpt_print(ccb->ccb_h.path, "SCSI command timeout on device handle "
- "0x%04x SMID %d\n", targ->handle, cm->cm_desc.Default.SMID);
- /*
- * XXX KDM this is useful for debugging purposes, but the existing
- * scsi_op_desc() implementation can't handle a NULL value for
- * inq_data. So this will remain commented out until I bring in
- * those changes as well.
+ /* Go back into startup mode and freeze the simq, so that CAM
+ * doesn't send any commands until after we've rediscovered all
+ * targets and found the proper device handles for them.
+ *
+ * After the reset, portenable will trigger discovery, and after all
+ * discovery-related activities have finished, the simq will be
+ * released.
*/
-#if 0
- xpt_print(ccb->ccb_h.path, "Timed out command: %s. CDB %s\n",
- scsi_op_desc((ccb->ccb_h.flags & CAM_CDB_POINTER) ?
- ccb->csio.cdb_io.cdb_ptr[0] :
- ccb->csio.cdb_io.cdb_bytes[0], NULL),
- scsi_cdb_string((ccb->ccb_h.flags & CAM_CDB_POINTER) ?
- ccb->csio.cdb_io.cdb_ptr :
- ccb->csio.cdb_io.cdb_bytes, cdb_str,
- sizeof(cdb_str)));
-#endif
+ mps_printf(sc, "%s startup\n", __func__);
+ sc->sassc->flags |= MPSSAS_IN_STARTUP;
+ sc->sassc->flags |= MPSSAS_IN_DISCOVERY;
+ xpt_freeze_simq(sc->sassc->sim, 1);
- /* Inform CAM about the timeout and that recovery is starting. */
-#if 0
- if ((targ->flags & MPSSAS_TARGET_INRECOVERY) == 0) {
- mpssas_freeze_device(sc->sassc, targ);
- ccb->ccb_h.status = CAM_CMD_TIMEOUT;
- xpt_done(ccb);
- }
-#endif
- mpssas_freeze_device(sc->sassc, targ);
- ccb->ccb_h.status = CAM_CMD_TIMEOUT;
+ /* notify CAM of a bus reset */
+ mpssas_announce_reset(sc, AC_BUS_RESET, CAM_TARGET_WILDCARD,
+ CAM_LUN_WILDCARD);
+
+ /* complete and cleanup after all outstanding commands */
+ mpssas_complete_all_commands(sc);
+
+ mps_printf(sc, "%s startup %u tm %u after command completion\n",
+ __func__, sc->sassc->startup_refcount, sc->sassc->tm_count);
/*
- * recycle the command into recovery so that there's no risk of
- * command allocation failure.
+ * The simq was explicitly frozen above, so set the refcount to 0.
+ * The simq will be explicitly released after port enable completes.
*/
- cm->cm_state = MPS_CM_STATE_TIMEDOUT;
- mpssas_recovery(sc, cm);
- mps_unlock(sc);
+ sc->sassc->startup_refcount = 0;
+
+ /* zero all the target handles, since they may change after the
+ * reset, and we have to rediscover all the targets and use the new
+ * handles.
+ */
+ for (i = 0; i < sc->facts->MaxTargets; i++) {
+ if (sc->sassc->targets[i].outstanding != 0)
+ mps_printf(sc, "target %u outstanding %u\n",
+ i, sc->sassc->targets[i].outstanding);
+ sc->sassc->targets[i].handle = 0x0;
+ sc->sassc->targets[i].exp_dev_handle = 0x0;
+ sc->sassc->targets[i].outstanding = 0;
+ sc->sassc->targets[i].flags = MPSSAS_TARGET_INDIAGRESET;
+ }
+}
+static void
+mpssas_tm_timeout(void *data)
+{
+ struct mps_command *tm = data;
+ struct mps_softc *sc = tm->cm_sc;
+
+ mtx_assert(&sc->mps_mtx, MA_OWNED);
+
+ mpssas_log_command(tm, "task mgmt %p timed out\n", tm);
+ mps_reinit(sc);
}
static void
-mpssas_abort_complete(struct mps_softc *sc, struct mps_command *cm)
+mpssas_logical_unit_reset_complete(struct mps_softc *sc, struct mps_command *tm)
{
+ MPI2_SCSI_TASK_MANAGE_REPLY *reply;
MPI2_SCSI_TASK_MANAGE_REQUEST *req;
+ unsigned int cm_count = 0;
+ struct mps_command *cm;
+ struct mpssas_target *targ;
- req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)cm->cm_req;
+ callout_stop(&tm->cm_callout);
+
+ req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)tm->cm_req;
+ reply = (MPI2_SCSI_TASK_MANAGE_REPLY *)tm->cm_reply;
+ targ = tm->cm_targ;
/*
* Currently there should be no way we can hit this case. It only
* happens when we have a failure to allocate chain frames, and
* task management commands don't have S/G lists.
*/
- if ((cm->cm_flags & MPS_CM_FLAGS_ERROR_MASK) != 0) {
- mps_printf(sc, "%s: cm_flags = %#x for abort on handle %#04x! "
- "This should not happen!\n", __func__, cm->cm_flags,
- req->DevHandle);
+ if ((tm->cm_flags & MPS_CM_FLAGS_ERROR_MASK) != 0) {
+ mps_printf(sc, "%s: cm_flags = %#x for LUN reset! "
+ "This should not happen!\n", __func__, tm->cm_flags);
+ mpssas_free_tm(sc, tm);
+ return;
}
- mps_printf(sc, "%s: abort request on handle %#04x SMID %d "
- "complete\n", __func__, req->DevHandle, req->TaskMID);
+ if (reply == NULL) {
+ mpssas_log_command(tm, "NULL reset reply for tm %p\n", tm);
+ if ((sc->mps_flags & MPS_FLAGS_DIAGRESET) != 0) {
+ /* this completion was due to a reset, just cleanup */
+ targ->flags &= ~MPSSAS_TARGET_INRESET;
+ targ->tm = NULL;
+ mpssas_free_tm(sc, tm);
+ }
+ else {
+ /* we should have gotten a reply. */
+ mps_reinit(sc);
+ }
+ return;
+ }
- mpssas_complete_tm_request(sc, cm, /*free_cm*/ 1);
+ mpssas_log_command(tm,
+ "logical unit reset status 0x%x code 0x%x count %u\n",
+ reply->IOCStatus, reply->ResponseCode,
+ reply->TerminationCount);
+
+ /* See if there are any outstanding commands for this LUN.
+ * This could be made more efficient by using a per-LU data
+ * structure of some sort.
+ */
+ TAILQ_FOREACH(cm, &targ->commands, cm_link) {
+ if (cm->cm_lun == tm->cm_lun)
+ cm_count++;
+ }
+
+ if (cm_count == 0) {
+ mpssas_log_command(tm,
+ "logical unit %u finished recovery after reset\n",
+ tm->cm_lun, tm);
+
+ mpssas_announce_reset(sc, AC_SENT_BDR, tm->cm_targ->tid,
+ tm->cm_lun);
+
+ /* we've finished recovery for this logical unit. check and
+ * see if some other logical unit has a timedout command
+ * that needs to be processed.
+ */
+ cm = TAILQ_FIRST(&targ->timedout_commands);
+ if (cm) {
+ mpssas_send_abort(sc, tm, cm);
+ }
+ else {
+ targ->tm = NULL;
+ mpssas_free_tm(sc, tm);
+ }
+ }
+ else {
+ /* if we still have commands for this LUN, the reset
+ * effectively failed, regardless of the status reported.
+ * Escalate to a target reset.
+ */
+ mpssas_log_command(tm,
+ "logical unit reset complete for tm %p, but still have %u command(s)\n",
+ tm, cm_count);
+ mpssas_send_reset(sc, tm,
+ MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET);
+ }
}
static void
-mpssas_recovery(struct mps_softc *sc, struct mps_command *abort_cm)
+mpssas_target_reset_complete(struct mps_softc *sc, struct mps_command *tm)
{
- struct mps_command *cm;
- MPI2_SCSI_TASK_MANAGE_REQUEST *req, *orig_req;
+ MPI2_SCSI_TASK_MANAGE_REPLY *reply;
+ MPI2_SCSI_TASK_MANAGE_REQUEST *req;
+ struct mpssas_target *targ;
- cm = mps_alloc_command(sc);
- if (cm == NULL) {
- mps_printf(sc, "%s: command allocation failure\n", __func__);
+ callout_stop(&tm->cm_callout);
+
+ req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)tm->cm_req;
+ reply = (MPI2_SCSI_TASK_MANAGE_REPLY *)tm->cm_reply;
+ targ = tm->cm_targ;
+
+ /*
+ * Currently there should be no way we can hit this case. It only
+ * happens when we have a failure to allocate chain frames, and
+ * task management commands don't have S/G lists.
+ */
+ if ((tm->cm_flags & MPS_CM_FLAGS_ERROR_MASK) != 0) {
+ mps_printf(sc, "%s: cm_flags = %#x for target reset! "
+ "This should not happen!\n", __func__, tm->cm_flags);
+ mpssas_free_tm(sc, tm);
return;
}
- cm->cm_targ = abort_cm->cm_targ;
- cm->cm_complete = mpssas_abort_complete;
+ if (reply == NULL) {
+ mpssas_log_command(tm, "NULL reset reply for tm %p\n", tm);
+ if ((sc->mps_flags & MPS_FLAGS_DIAGRESET) != 0) {
+ /* this completion was due to a reset, just cleanup */
+ targ->flags &= ~MPSSAS_TARGET_INRESET;
+ targ->tm = NULL;
+ mpssas_free_tm(sc, tm);
+ }
+ else {
+ /* we should have gotten a reply. */
+ mps_reinit(sc);
+ }
+ return;
+ }
- req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)cm->cm_req;
- orig_req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)abort_cm->cm_req;
- req->DevHandle = abort_cm->cm_targ->handle;
- req->Function = MPI2_FUNCTION_SCSI_TASK_MGMT;
- req->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK;
- memcpy(req->LUN, orig_req->LUN, sizeof(req->LUN));
- req->TaskMID = abort_cm->cm_desc.Default.SMID;
+ mpssas_log_command(tm,
+ "target reset status 0x%x code 0x%x count %u\n",
+ reply->IOCStatus, reply->ResponseCode,
+ reply->TerminationCount);
- cm->cm_data = NULL;
- cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+ targ->flags &= ~MPSSAS_TARGET_INRESET;
+
+ if (targ->outstanding == 0) {
+ /* we've finished recovery for this target and all
+ * of its logical units.
+ */
+ mpssas_log_command(tm,
+ "recovery finished after target reset\n");
- mpssas_issue_tm_request(sc, cm);
+ mpssas_announce_reset(sc, AC_SENT_BDR, tm->cm_targ->tid,
+ CAM_LUN_WILDCARD);
+ targ->tm = NULL;
+ mpssas_free_tm(sc, tm);
+ }
+ else {
+ /* after a target reset, if this target still has
+ * outstanding commands, the reset effectively failed,
+ * regardless of the status reported. escalate.
+ */
+ mpssas_log_command(tm,
+ "target reset complete for tm %p, but still have %u command(s)\n",
+ tm, targ->outstanding);
+ mps_reinit(sc);
+ }
}
-/*
- * Can return 0 or EINPROGRESS on success. Any other value means failure.
- */
+#define MPS_RESET_TIMEOUT 30
+
static int
-mpssas_map_tm_request(struct mps_softc *sc, struct mps_command *cm)
+mpssas_send_reset(struct mps_softc *sc, struct mps_command *tm, uint8_t type)
{
- int error;
+ MPI2_SCSI_TASK_MANAGE_REQUEST *req;
+ struct mpssas_target *target;
+ int err;
- error = 0;
+ target = tm->cm_targ;
+ if (target->handle == 0) {
+ mps_printf(sc, "%s null devhandle for target_id %d\n",
+ __func__, target->tid);
+ return -1;
+ }
- cm->cm_flags |= MPS_CM_FLAGS_ACTIVE;
- error = mps_map_command(sc, cm);
- if ((error == 0)
- || (error == EINPROGRESS))
- sc->tm_cmds_active++;
+ req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)tm->cm_req;
+ req->DevHandle = target->handle;
+ req->Function = MPI2_FUNCTION_SCSI_TASK_MGMT;
+ req->TaskType = type;
+
+ if (type == MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET) {
+ /* XXX Need to handle invalid LUNs */
+ MPS_SET_LUN(req->LUN, tm->cm_lun);
+ tm->cm_targ->logical_unit_resets++;
+ mpssas_log_command(tm, "sending logical unit reset\n");
+ tm->cm_complete = mpssas_logical_unit_reset_complete;
+ }
+ else if (type == MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET) {
+ /* Target reset method = SAS Hard Link Reset / SATA Link Reset */
+ req->MsgFlags = MPI2_SCSITASKMGMT_MSGFLAGS_LINK_RESET;
+ tm->cm_targ->target_resets++;
+ tm->cm_targ->flags |= MPSSAS_TARGET_INRESET;
+ mpssas_log_command(tm, "sending target reset\n");
+ tm->cm_complete = mpssas_target_reset_complete;
+ }
+ else {
+ mps_printf(sc, "unexpected reset type 0x%x\n", type);
+ return -1;
+ }
- return (error);
+ tm->cm_data = NULL;
+ tm->cm_desc.HighPriority.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY;
+ tm->cm_complete_data = (void *)tm;
+
+ callout_reset(&tm->cm_callout, MPS_RESET_TIMEOUT * hz,
+ mpssas_tm_timeout, tm);
+
+ err = mps_map_command(sc, tm);
+ if (err)
+ mpssas_log_command(tm,
+ "error %d sending reset type %u\n",
+ err, type);
+
+ return err;
}
+
static void
-mpssas_issue_tm_request(struct mps_softc *sc, struct mps_command *cm)
+mpssas_abort_complete(struct mps_softc *sc, struct mps_command *tm)
{
- int freeze_queue, send_command, error;
+ struct mps_command *cm;
+ MPI2_SCSI_TASK_MANAGE_REPLY *reply;
+ MPI2_SCSI_TASK_MANAGE_REQUEST *req;
+ struct mpssas_target *targ;
- freeze_queue = 0;
- send_command = 0;
- error = 0;
+ callout_stop(&tm->cm_callout);
- mtx_assert(&sc->mps_mtx, MA_OWNED);
+ req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)tm->cm_req;
+ reply = (MPI2_SCSI_TASK_MANAGE_REPLY *)tm->cm_reply;
+ targ = tm->cm_targ;
/*
- * If there are no other pending task management commands, go
- * ahead and send this one. There is a small amount of anecdotal
- * evidence that sending lots of task management commands at once
- * may cause the controller to lock up. Or, if the user has
- * configured the driver (via the allow_multiple_tm_cmds variable) to
- * not serialize task management commands, go ahead and send the
- * command if even other task management commands are pending.
+ * Currently there should be no way we can hit this case. It only
+ * happens when we have a failure to allocate chain frames, and
+ * task management commands don't have S/G lists.
*/
- if (TAILQ_FIRST(&sc->tm_list) == NULL) {
- send_command = 1;
- freeze_queue = 1;
- } else if (sc->allow_multiple_tm_cmds != 0)
- send_command = 1;
-
- TAILQ_INSERT_TAIL(&sc->tm_list, cm, cm_link);
- if (send_command != 0) {
- /*
- * Freeze the SIM queue while we issue the task management
- * command. According to the Fusion-MPT 2.0 spec, task
- * management requests are serialized, and so the host
- * should not send any I/O requests while task management
- * requests are pending.
- */
- if (freeze_queue != 0)
- xpt_freeze_simq(sc->sassc->sim, 1);
+ if ((tm->cm_flags & MPS_CM_FLAGS_ERROR_MASK) != 0) {
+ mpssas_log_command(tm,
+ "cm_flags = %#x for abort %p TaskMID %u!\n",
+ tm->cm_flags, tm, req->TaskMID);
+ mpssas_free_tm(sc, tm);
+ return;
+ }
- error = mpssas_map_tm_request(sc, cm);
+ if (reply == NULL) {
+ mpssas_log_command(tm,
+ "NULL abort reply for tm %p TaskMID %u\n",
+ tm, req->TaskMID);
+ if ((sc->mps_flags & MPS_FLAGS_DIAGRESET) != 0) {
+ /* this completion was due to a reset, just cleanup */
+ targ->tm = NULL;
+ mpssas_free_tm(sc, tm);
+ }
+ else {
+ /* we should have gotten a reply. */
+ mps_reinit(sc);
+ }
+ return;
+ }
- /*
- * At present, there is no error path back from
- * mpssas_map_tm_request() (which calls mps_map_command())
- * when cm->cm_data == NULL. But since there is a return
- * value, we check it just in case the implementation
- * changes later.
+ mpssas_log_command(tm,
+ "abort TaskMID %u status 0x%x code 0x%x count %u\n",
+ req->TaskMID,
+ reply->IOCStatus, reply->ResponseCode,
+ reply->TerminationCount);
+
+ cm = TAILQ_FIRST(&tm->cm_targ->timedout_commands);
+ if (cm == NULL) {
+ /* if there are no more timedout commands, we're done with
+ * error recovery for this target.
*/
- if ((error != 0)
- && (error != EINPROGRESS))
- mpssas_tm_complete(sc, cm,
- MPI2_SCSITASKMGMT_RSP_TM_FAILED);
+ mpssas_log_command(tm,
+ "finished recovery after aborting TaskMID %u\n",
+ req->TaskMID);
+
+ targ->tm = NULL;
+ mpssas_free_tm(sc, tm);
+ }
+ else if (req->TaskMID != cm->cm_desc.Default.SMID) {
+ /* abort success, but we have more timedout commands to abort */
+ mpssas_log_command(tm,
+ "continuing recovery after aborting TaskMID %u\n",
+ req->TaskMID);
+
+ mpssas_send_abort(sc, tm, cm);
+ }
+ else {
+ /* we didn't get a command completion, so the abort
+ * failed as far as we're concerned. escalate.
+ */
+ mpssas_log_command(tm,
+ "abort failed for TaskMID %u tm %p\n",
+ req->TaskMID, tm);
+
+ mpssas_send_reset(sc, tm,
+ MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET);
}
}
-static void
-mpssas_tm_complete(struct mps_softc *sc, struct mps_command *cm, int error)
+#define MPS_ABORT_TIMEOUT 5
+
+static int
+mpssas_send_abort(struct mps_softc *sc, struct mps_command *tm, struct mps_command *cm)
{
- MPI2_SCSI_TASK_MANAGE_REPLY *resp;
+ MPI2_SCSI_TASK_MANAGE_REQUEST *req;
+ struct mpssas_target *targ;
+ int err;
- resp = (MPI2_SCSI_TASK_MANAGE_REPLY *)cm->cm_reply;
+ targ = cm->cm_targ;
+ if (targ->handle == 0) {
+ mps_printf(sc, "%s null devhandle for target_id %d\n",
+ __func__, cm->cm_ccb->ccb_h.target_id);
+ return -1;
+ }
- if (resp != NULL)
- resp->ResponseCode = error;
+ req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)tm->cm_req;
+ req->DevHandle = targ->handle;
+ req->Function = MPI2_FUNCTION_SCSI_TASK_MGMT;
+ req->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK;
- /*
- * Call the callback for this command, it will be
- * removed from the list and freed via the callback.
- */
- cm->cm_complete(sc, cm);
-}
+ /* XXX Need to handle invalid LUNs */
+ MPS_SET_LUN(req->LUN, cm->cm_ccb->ccb_h.target_lun);
-/*
- * Complete a task management request. The basic completion operation will
- * always succeed. Returns status for sending any further task management
- * commands that were queued.
- */
-static int
-mpssas_complete_tm_request(struct mps_softc *sc, struct mps_command *cm,
- int free_cm)
-{
- int error;
+ req->TaskMID = cm->cm_desc.Default.SMID;
- error = 0;
+ tm->cm_data = NULL;
+ tm->cm_desc.HighPriority.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY;
+ tm->cm_complete = mpssas_abort_complete;
+ tm->cm_complete_data = (void *)tm;
+ tm->cm_targ = cm->cm_targ;
+ tm->cm_lun = cm->cm_lun;
- mtx_assert(&sc->mps_mtx, MA_OWNED);
+ callout_reset(&tm->cm_callout, MPS_ABORT_TIMEOUT * hz,
+ mpssas_tm_timeout, tm);
- TAILQ_REMOVE(&sc->tm_list, cm, cm_link);
- cm->cm_flags &= ~MPS_CM_FLAGS_ACTIVE;
- sc->tm_cmds_active--;
+ targ->aborts++;
- if (free_cm != 0)
- mps_free_command(sc, cm);
+ err = mps_map_command(sc, tm);
+ if (err)
+ mpssas_log_command(tm,
+ "error %d sending abort for cm %p SMID %u\n",
+ err, cm, req->TaskMID);
+ return err;
+}
- if (TAILQ_FIRST(&sc->tm_list) == NULL) {
- /*
- * Release the SIM queue, we froze it when we sent the first
- * task management request.
- */
- xpt_release_simq(sc->sassc->sim, 1);
- } else if ((sc->tm_cmds_active == 0)
- || (sc->allow_multiple_tm_cmds != 0)) {
- int error;
- struct mps_command *cm2;
-restart_traversal:
+static void
+mpssas_scsiio_timeout(void *data)
+{
+ struct mps_softc *sc;
+ struct mps_command *cm;
+ struct mpssas_target *targ;
- /*
- * We don't bother using TAILQ_FOREACH_SAFE here, but
- * rather use the standard version and just restart the
- * list traversal if we run into the error case.
- * TAILQ_FOREACH_SAFE allows safe removal of the current
- * list element, but if you have a queue of task management
- * commands, all of which have mapping errors, you'll end
- * up with recursive calls to this routine and so you could
- * wind up removing more than just the current list element.
- */
- TAILQ_FOREACH(cm2, &sc->tm_list, cm_link) {
- MPI2_SCSI_TASK_MANAGE_REQUEST *req;
+ cm = (struct mps_command *)data;
+ sc = cm->cm_sc;
+
+ mtx_assert(&sc->mps_mtx, MA_OWNED);
+
+ mps_printf(sc, "%s checking sc %p cm %p\n", __func__, sc, cm);
+
+ /*
+ * Run the interrupt handler to make sure it's not pending. This
+ * isn't perfect because the command could have already completed
+ * and been re-used, though this is unlikely.
+ */
+ mps_intr_locked(sc);
+ if (cm->cm_state == MPS_CM_STATE_FREE) {
+ mps_printf(sc, "SCSI command %p sc %p almost timed out\n", cm, sc);
+ return;
+ }
- /* This command is active, no need to send it again */
- if (cm2->cm_flags & MPS_CM_FLAGS_ACTIVE)
- continue;
+ if (cm->cm_ccb == NULL) {
+ mps_printf(sc, "command timeout with NULL ccb\n");
+ return;
+ }
- req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)cm2->cm_req;
+ mpssas_log_command(cm, "command timeout cm %p ccb %p\n",
+ cm, cm->cm_ccb);
- mps_printf(sc, "%s: sending deferred task management "
- "request for handle %#04x SMID %d\n", __func__,
- req->DevHandle, req->TaskMID);
+ targ = cm->cm_targ;
+ targ->timeouts++;
- error = mpssas_map_tm_request(sc, cm2);
+ /* XXX first, check the firmware state, to see if it's still
+ * operational. if not, do a diag reset.
+ */
- /*
- * Check for errors. If we had an error, complete
- * this command with an error, and keep going through
- * the list until we are able to send at least one
- * command or all of them are completed with errors.
- *
- * We don't want to wind up in a situation where
- * we're stalled out with no way for queued task
- * management commands to complete.
- *
- * Note that there is not currently an error path
- * back from mpssas_map_tm_request() (which calls
- * mps_map_command()) when cm->cm_data == NULL.
- * But we still want to check for errors here in
- * case the implementation changes, or in case
- * there is some reason for a data payload here.
- */
- if ((error != 0)
- && (error != EINPROGRESS)) {
- mpssas_tm_complete(sc, cm,
- MPI2_SCSITASKMGMT_RSP_TM_FAILED);
+ cm->cm_ccb->ccb_h.status = CAM_CMD_TIMEOUT;
+ cm->cm_state = MPS_CM_STATE_TIMEDOUT;
+ TAILQ_INSERT_TAIL(&targ->timedout_commands, cm, cm_recovery);
- /*
- * If we don't currently have any commands
- * active, go back to the beginning and see
- * if there are any more that can be started.
- * Otherwise, we're done here.
- */
- if (sc->tm_cmds_active == 0)
- goto restart_traversal;
- else
- break;
- }
+ if (targ->tm != NULL) {
+ /* target already in recovery, just queue up another
+ * timedout command to be processed later.
+ */
+ mps_printf(sc, "queued timedout cm %p for processing by tm %p\n",
+ cm, targ->tm);
+ }
+ else if ((targ->tm = mpssas_alloc_tm(sc)) != NULL) {
+ mps_printf(sc, "timedout cm %p allocated tm %p\n",
+ cm, targ->tm);
- /*
- * If the user only wants one task management command
- * active at a time, we're done, since we've
- * already successfully sent a command at this point.
- */
- if (sc->allow_multiple_tm_cmds == 0)
- break;
- }
+ /* start recovery by aborting the first timedout command */
+ mpssas_send_abort(sc, targ->tm, cm);
+ }
+ else {
+ /* XXX queue this target up for recovery once a TM becomes
+ * available. The firmware only has a limited number of
+ * HighPriority credits for the high priority requests used
+ * for task management, and we ran out.
+ *
+ * Isilon: don't worry about this for now, since we have
+ * more credits than disks in an enclosure, and limit
+ * ourselves to one TM per target for recovery.
+ */
+ mps_printf(sc, "timedout cm %p failed to allocate a tm\n",
+ cm);
}
- return (error);
}
static void
@@ -1364,16 +1469,41 @@ mpssas_action_scsiio(struct mpssas_softc *sassc, union ccb *ccb)
struct ccb_scsiio *csio;
struct mps_softc *sc;
struct mpssas_target *targ;
+ struct mpssas_lun *lun;
struct mps_command *cm;
-
- mps_dprint(sassc->sc, MPS_TRACE, "%s\n", __func__);
+ uint8_t i, lba_byte, *ref_tag_addr;
+ uint16_t eedp_flags;
sc = sassc->sc;
+ mps_dprint(sc, MPS_TRACE, "%s ccb %p\n", __func__, ccb);
+ mtx_assert(&sc->mps_mtx, MA_OWNED);
csio = &ccb->csio;
targ = &sassc->targets[csio->ccb_h.target_id];
if (targ->handle == 0x0) {
- csio->ccb_h.status = CAM_SEL_TIMEOUT;
+ mps_dprint(sc, MPS_TRACE, "%s NULL handle for target %u\n",
+ __func__, csio->ccb_h.target_id);
+ csio->ccb_h.status = CAM_TID_INVALID;
+ xpt_done(ccb);
+ return;
+ }
+ /*
+ * If devinfo is 0 this will be a volume. In that case don't tell CAM
+ * that the volume has timed out. We want volumes to be enumerated
+ * until they are deleted/removed, not just failed.
+ */
+ if (targ->flags & MPSSAS_TARGET_INREMOVAL) {
+ if (targ->devinfo == 0)
+ csio->ccb_h.status = CAM_REQ_CMP;
+ else
+ csio->ccb_h.status = CAM_SEL_TIMEOUT;
+ xpt_done(ccb);
+ return;
+ }
+
+ if ((sc->mps_flags & MPS_FLAGS_SHUTDOWN) != 0) {
+ mps_dprint(sc, MPS_TRACE, "%s shutting down\n", __func__);
+ csio->ccb_h.status = CAM_TID_INVALID;
xpt_done(ccb);
return;
}
@@ -1446,6 +1576,7 @@ mpssas_action_scsiio(struct mpssas_softc *sassc, union ccb *ccb)
req->Control |= MPI2_SCSIIO_CONTROL_SIMPLEQ;
break;
}
+ req->Control |= sc->mapping_table[csio->ccb_h.target_id].TLR_bits;
if (MPS_SET_LUN(req->LUN, csio->ccb_h.target_lun) != 0) {
mps_free_command(sc, cm);
@@ -1461,8 +1592,57 @@ mpssas_action_scsiio(struct mpssas_softc *sassc, union ccb *ccb)
req->IoFlags = csio->cdb_len;
/*
- * XXX need to handle S/G lists and physical addresses here.
+ * Check if EEDP is supported and enabled. If it is then check if the
+ * SCSI opcode could be using EEDP. If so, make sure the LUN exists and
+ * is formatted for EEDP support. If all of this is true, set CDB up
+ * for EEDP transfer.
*/
+ eedp_flags = op_code_prot[req->CDB.CDB32[0]];
+ if (sc->eedp_enabled && eedp_flags) {
+ SLIST_FOREACH(lun, &targ->luns, lun_link) {
+ if (lun->lun_id == csio->ccb_h.target_lun) {
+ break;
+ }
+ }
+
+ if ((lun != NULL) && (lun->eedp_formatted)) {
+ req->EEDPBlockSize = lun->eedp_block_size;
+ eedp_flags |= (MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG |
+ MPI2_SCSIIO_EEDPFLAGS_CHECK_REFTAG |
+ MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD);
+ req->EEDPFlags = eedp_flags;
+
+ /*
+ * If CDB less than 32, fill in Primary Ref Tag with
+ * low 4 bytes of LBA. If CDB is 32, tag stuff is
+ * already there. Also, set protection bit. FreeBSD
+ * currently does not support CDBs bigger than 16, but
+ * the code doesn't hurt, and will be here for the
+ * future.
+ */
+ if (csio->cdb_len != 32) {
+ lba_byte = (csio->cdb_len == 16) ? 6 : 2;
+ ref_tag_addr = (uint8_t *)&req->CDB.EEDP32.
+ PrimaryReferenceTag;
+ for (i = 0; i < 4; i++) {
+ *ref_tag_addr =
+ req->CDB.CDB32[lba_byte + i];
+ ref_tag_addr++;
+ }
+ req->CDB.EEDP32.PrimaryApplicationTagMask =
+ 0xFFFF;
+ req->CDB.CDB32[1] = (req->CDB.CDB32[1] & 0x1F) |
+ 0x20;
+ } else {
+ eedp_flags |=
+ MPI2_SCSIIO_EEDPFLAGS_INC_PRI_APPTAG;
+ req->EEDPFlags = eedp_flags;
+ req->CDB.CDB32[10] = (req->CDB.CDB32[10] &
+ 0x1F) | 0x20;
+ }
+ }
+ }
+
cm->cm_data = csio->data_ptr;
cm->cm_length = csio->dxfer_len;
cm->cm_sge = &req->SGL;
@@ -1472,15 +1652,33 @@ mpssas_action_scsiio(struct mpssas_softc *sassc, union ccb *ccb)
cm->cm_complete = mpssas_scsiio_complete;
cm->cm_complete_data = ccb;
cm->cm_targ = targ;
+ cm->cm_lun = csio->ccb_h.target_lun;
+ cm->cm_ccb = ccb;
- sc->io_cmds_active++;
- if (sc->io_cmds_active > sc->io_cmds_highwater)
- sc->io_cmds_highwater = sc->io_cmds_active;
+ /*
+ * If HBA is a WD and the command is not for a retry, try to build a
+ * direct I/O message. If failed, or the command is for a retry, send
+ * the I/O to the IR volume itself.
+ */
+ if (sc->WD_valid_config) {
+ if (ccb->ccb_h.status != MPS_WD_RETRY) {
+ mpssas_direct_drive_io(sassc, cm, ccb);
+ } else {
+ ccb->ccb_h.status = CAM_REQ_INPROG;
+ }
+ }
- TAILQ_INSERT_TAIL(&sc->io_list, cm, cm_link);
callout_reset(&cm->cm_callout, (ccb->ccb_h.timeout * hz) / 1000,
mpssas_scsiio_timeout, cm);
+ targ->issued++;
+ targ->outstanding++;
+ TAILQ_INSERT_TAIL(&targ->commands, cm, cm_link);
+
+ if ((sc->mps_debug & MPS_TRACE) != 0)
+ mpssas_log_command(cm, "%s cm %p ccb %p outstanding %u\n",
+ __func__, cm, ccb, targ->outstanding);
+
mps_map_command(sc, cm);
return;
}
@@ -1490,19 +1688,25 @@ mpssas_scsiio_complete(struct mps_softc *sc, struct mps_command *cm)
{
MPI2_SCSI_IO_REPLY *rep;
union ccb *ccb;
+ struct ccb_scsiio *csio;
struct mpssas_softc *sassc;
- int dir = 0;
+ struct scsi_vpd_supported_page_list *vpd_list = NULL;
+ u8 *TLR_bits, TLR_on;
+ int dir = 0, i;
+ u16 alloc_len;
- mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+ mps_dprint(sc, MPS_TRACE,
+ "%s cm %p SMID %u ccb %p reply %p outstanding %u\n",
+ __func__, cm, cm->cm_desc.Default.SMID, cm->cm_ccb, cm->cm_reply,
+ cm->cm_targ->outstanding);
callout_stop(&cm->cm_callout);
- TAILQ_REMOVE(&sc->io_list, cm, cm_link);
- sc->io_cmds_active--;
+ mtx_assert(&sc->mps_mtx, MA_OWNED);
sassc = sc->sassc;
ccb = cm->cm_complete_data;
+ csio = &ccb->csio;
rep = (MPI2_SCSI_IO_REPLY *)cm->cm_reply;
-
/*
* XXX KDM if the chain allocation fails, does it matter if we do
* the sync and unload here? It is simpler to do it in every case,
@@ -1517,6 +1721,41 @@ mpssas_scsiio_complete(struct mps_softc *sc, struct mps_command *cm)
bus_dmamap_unload(sc->buffer_dmat, cm->cm_dmamap);
}
+ cm->cm_targ->completed++;
+ cm->cm_targ->outstanding--;
+ TAILQ_REMOVE(&cm->cm_targ->commands, cm, cm_link);
+
+ if (cm->cm_state == MPS_CM_STATE_TIMEDOUT) {
+ TAILQ_REMOVE(&cm->cm_targ->timedout_commands, cm, cm_recovery);
+ if (cm->cm_reply != NULL)
+ mpssas_log_command(cm,
+ "completed timedout cm %p ccb %p during recovery "
+ "ioc %x scsi %x state %x xfer %u\n",
+ cm, cm->cm_ccb,
+ rep->IOCStatus, rep->SCSIStatus, rep->SCSIState,
+ rep->TransferCount);
+ else
+ mpssas_log_command(cm,
+ "completed timedout cm %p ccb %p during recovery\n",
+ cm, cm->cm_ccb);
+ } else if (cm->cm_targ->tm != NULL) {
+ if (cm->cm_reply != NULL)
+ mpssas_log_command(cm,
+ "completed cm %p ccb %p during recovery "
+ "ioc %x scsi %x state %x xfer %u\n",
+ cm, cm->cm_ccb,
+ rep->IOCStatus, rep->SCSIStatus, rep->SCSIState,
+ rep->TransferCount);
+ else
+ mpssas_log_command(cm,
+ "completed cm %p ccb %p during recovery\n",
+ cm, cm->cm_ccb);
+ } else if ((sc->mps_flags & MPS_FLAGS_DIAGRESET) != 0) {
+ mpssas_log_command(cm,
+ "reset completed cm %p ccb %p\n",
+ cm, cm->cm_ccb);
+ }
+
if ((cm->cm_flags & MPS_CM_FLAGS_ERROR_MASK) != 0) {
/*
* We ran into an error after we tried to map the command,
@@ -1550,16 +1789,31 @@ mpssas_scsiio_complete(struct mps_softc *sc, struct mps_command *cm)
/* Take the fast path to completion */
if (cm->cm_reply == NULL) {
if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INPROG) {
- ccb->ccb_h.status = CAM_REQ_CMP;
- ccb->csio.scsi_status = SCSI_STATUS_OK;
-
+ if ((sc->mps_flags & MPS_FLAGS_DIAGRESET) != 0)
+ ccb->ccb_h.status = CAM_SCSI_BUS_RESET;
+ else {
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ ccb->csio.scsi_status = SCSI_STATUS_OK;
+ }
if (sassc->flags & MPSSAS_QUEUE_FROZEN) {
ccb->ccb_h.status |= CAM_RELEASE_SIMQ;
sassc->flags &= ~MPSSAS_QUEUE_FROZEN;
mps_dprint(sc, MPS_INFO,
"Unfreezing SIM queue\n");
}
- } else {
+ }
+
+ /*
+ * There are two scenarios where the status won't be
+ * CAM_REQ_CMP. The first is if MPS_CM_FLAGS_ERROR_MASK is
+ * set, the second is in the MPS_FLAGS_DIAGRESET above.
+ */
+ if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+ /*
+ * Freeze the dev queue so that commands are
+ * executed in the correct order with after error
+ * recovery.
+ */
ccb->ccb_h.status |= CAM_DEV_QFRZN;
xpt_freeze_devq(ccb->ccb_h.path, /*count*/ 1);
}
@@ -1568,37 +1822,149 @@ mpssas_scsiio_complete(struct mps_softc *sc, struct mps_command *cm)
return;
}
- mps_dprint(sc, MPS_INFO, "(%d:%d:%d) IOCStatus= 0x%x, "
- "ScsiStatus= 0x%x, SCSIState= 0x%x TransferCount= 0x%x\n",
- xpt_path_path_id(ccb->ccb_h.path),
- xpt_path_target_id(ccb->ccb_h.path),
- xpt_path_lun_id(ccb->ccb_h.path), rep->IOCStatus,
- rep->SCSIStatus, rep->SCSIState, rep->TransferCount);
+ if (sc->mps_debug & MPS_TRACE)
+ mpssas_log_command(cm,
+ "ioc %x scsi %x state %x xfer %u\n",
+ rep->IOCStatus, rep->SCSIStatus,
+ rep->SCSIState, rep->TransferCount);
+
+ /*
+ * If this is a Direct Drive I/O, reissue the I/O to the original IR
+ * Volume if an error occurred (normal I/O retry). Use the original
+ * CCB, but set a flag that this will be a retry so that it's sent to
+ * the original volume. Free the command but reuse the CCB.
+ */
+ if (cm->cm_flags & MPS_CM_FLAGS_DD_IO) {
+ mps_free_command(sc, cm);
+ ccb->ccb_h.status = MPS_WD_RETRY;
+ mpssas_action_scsiio(sassc, ccb);
+ return;
+ }
switch (rep->IOCStatus & MPI2_IOCSTATUS_MASK) {
- case MPI2_IOCSTATUS_BUSY:
- case MPI2_IOCSTATUS_INSUFFICIENT_RESOURCES:
- /*
- * The controller is overloaded, try waiting a bit for it
- * to free up.
- */
- ccb->ccb_h.status = CAM_BUSY;
- break;
case MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN:
- ccb->csio.resid = cm->cm_length - rep->TransferCount;
+ csio->resid = cm->cm_length - rep->TransferCount;
/* FALLTHROUGH */
case MPI2_IOCSTATUS_SUCCESS:
case MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR:
- ccb->ccb_h.status = CAM_REQ_CMP;
- break;
- case MPI2_IOCSTATUS_SCSI_DATA_OVERRUN:
- /* resid is ignored for this condition */
- ccb->csio.resid = 0;
- ccb->ccb_h.status = CAM_DATA_RUN_ERR;
+
+ if ((rep->IOCStatus & MPI2_IOCSTATUS_MASK) ==
+ MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR)
+ mpssas_log_command(cm, "recovered error\n");
+
+ /* Completion failed at the transport level. */
+ if (rep->SCSIState & (MPI2_SCSI_STATE_NO_SCSI_STATUS |
+ MPI2_SCSI_STATE_TERMINATED)) {
+ ccb->ccb_h.status = CAM_REQ_CMP_ERR;
+ break;
+ }
+
+ /* In a modern packetized environment, an autosense failure
+ * implies that there's not much else that can be done to
+ * recover the command.
+ */
+ if (rep->SCSIState & MPI2_SCSI_STATE_AUTOSENSE_FAILED) {
+ ccb->ccb_h.status = CAM_AUTOSENSE_FAIL;
+ break;
+ }
+
+ /*
+ * CAM doesn't care about SAS Response Info data, but if this is
+ * the state check if TLR should be done. If not, clear the
+ * TLR_bits for the target.
+ */
+ if ((rep->SCSIState & MPI2_SCSI_STATE_RESPONSE_INFO_VALID) &&
+ ((rep->ResponseInfo & MPI2_SCSI_RI_MASK_REASONCODE) ==
+ MPS_SCSI_RI_INVALID_FRAME)) {
+ sc->mapping_table[csio->ccb_h.target_id].TLR_bits =
+ (u8)MPI2_SCSIIO_CONTROL_NO_TLR;
+ }
+
+ /*
+ * Intentionally override the normal SCSI status reporting
+ * for these two cases. These are likely to happen in a
+ * multi-initiator environment, and we want to make sure that
+ * CAM retries these commands rather than fail them.
+ */
+ if ((rep->SCSIStatus == MPI2_SCSI_STATUS_COMMAND_TERMINATED) ||
+ (rep->SCSIStatus == MPI2_SCSI_STATUS_TASK_ABORTED)) {
+ ccb->ccb_h.status = CAM_REQ_ABORTED;
+ break;
+ }
+
+ /* Handle normal status and sense */
+ csio->scsi_status = rep->SCSIStatus;
+ if (rep->SCSIStatus == MPI2_SCSI_STATUS_GOOD)
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ else
+ ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR;
+
+ if (rep->SCSIState & MPI2_SCSI_STATE_AUTOSENSE_VALID) {
+ int sense_len, returned_sense_len;
+
+ returned_sense_len = min(rep->SenseCount,
+ sizeof(struct scsi_sense_data));
+ if (returned_sense_len < ccb->csio.sense_len)
+ ccb->csio.sense_resid = ccb->csio.sense_len -
+ returned_sense_len;
+ else
+ ccb->csio.sense_resid = 0;
+
+ sense_len = min(returned_sense_len,
+ ccb->csio.sense_len - ccb->csio.sense_resid);
+ bzero(&ccb->csio.sense_data,
+ sizeof(&ccb->csio.sense_data));
+ bcopy(cm->cm_sense, &ccb->csio.sense_data, sense_len);
+ ccb->ccb_h.status |= CAM_AUTOSNS_VALID;
+ }
+
+ /*
+ * Check if this is an INQUIRY command. If it's a VPD inquiry,
+ * and it's page code 0 (Supported Page List), and there is
+ * inquiry data, and this is for a sequential access device, and
+ * the device is an SSP target, and TLR is supported by the
+ * controller, turn the TLR_bits value ON if page 0x90 is
+ * supported.
+ */
+ if ((csio->cdb_io.cdb_bytes[0] == INQUIRY) &&
+ (csio->cdb_io.cdb_bytes[1] & SI_EVPD) &&
+ (csio->cdb_io.cdb_bytes[2] == SVPD_SUPPORTED_PAGE_LIST) &&
+ (csio->data_ptr != NULL) && (((uint8_t *)cm->cm_data)[0] ==
+ T_SEQUENTIAL) && (sc->control_TLR) &&
+ (sc->mapping_table[csio->ccb_h.target_id].device_info &
+ MPI2_SAS_DEVICE_INFO_SSP_TARGET)) {
+ vpd_list = (struct scsi_vpd_supported_page_list *)
+ csio->data_ptr;
+ TLR_bits = &sc->mapping_table[csio->ccb_h.target_id].
+ TLR_bits;
+ *TLR_bits = (u8)MPI2_SCSIIO_CONTROL_NO_TLR;
+ TLR_on = (u8)MPI2_SCSIIO_CONTROL_TLR_ON;
+ alloc_len = ((u16)csio->cdb_io.cdb_bytes[3] << 8) +
+ csio->cdb_io.cdb_bytes[4];
+ for (i = 0; i < MIN(vpd_list->length, alloc_len); i++) {
+ if (vpd_list->list[i] == 0x90) {
+ *TLR_bits = TLR_on;
+ break;
+ }
+ }
+ }
break;
case MPI2_IOCSTATUS_SCSI_INVALID_DEVHANDLE:
case MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE:
- ccb->ccb_h.status = CAM_DEV_NOT_THERE;
+ /*
+ * If devinfo is 0 this will be a volume. In that case don't
+ * tell CAM that the volume is not there. We want volumes to
+ * be enumerated until they are deleted/removed, not just
+ * failed.
+ */
+ if (cm->cm_targ->devinfo == 0)
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ else
+ ccb->ccb_h.status = CAM_DEV_NOT_THERE;
+ break;
+ case MPI2_IOCSTATUS_INVALID_SGL:
+ mps_print_scsiio_cmd(sc, cm);
+ ccb->ccb_h.status = CAM_UNREC_HBA_ERROR;
break;
case MPI2_IOCSTATUS_SCSI_TASK_TERMINATED:
/*
@@ -1615,22 +1981,23 @@ mpssas_scsiio_complete(struct mps_softc *sc, struct mps_command *cm)
else
ccb->ccb_h.status = CAM_REQ_ABORTED;
break;
+ case MPI2_IOCSTATUS_SCSI_DATA_OVERRUN:
+ /* resid is ignored for this condition */
+ csio->resid = 0;
+ ccb->ccb_h.status = CAM_DATA_RUN_ERR;
+ break;
case MPI2_IOCSTATUS_SCSI_IOC_TERMINATED:
case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED:
-#if 0
- ccb->ccb_h.status = CAM_REQ_ABORTED;
-#endif
- mps_printf(sc, "(%d:%d:%d) terminated ioc %x scsi %x state %x "
- "xfer %u\n", xpt_path_path_id(ccb->ccb_h.path),
- xpt_path_target_id(ccb->ccb_h.path),
- xpt_path_lun_id(ccb->ccb_h.path),
- rep->IOCStatus, rep->SCSIStatus, rep->SCSIState,
- rep->TransferCount);
+ /*
+ * Since these are generally external (i.e. hopefully
+ * transient transport-related) errors, retry these without
+ * decrementing the retry count.
+ */
ccb->ccb_h.status = CAM_REQUEUE_REQ;
- break;
- case MPI2_IOCSTATUS_INVALID_SGL:
- mps_print_scsiio_cmd(sc, cm);
- ccb->ccb_h.status = CAM_UNREC_HBA_ERROR;
+ mpssas_log_command(cm,
+ "terminated ioc %x scsi %x state %x xfer %u\n",
+ rep->IOCStatus, rep->SCSIStatus, rep->SCSIState,
+ rep->TransferCount);
break;
case MPI2_IOCSTATUS_INVALID_FUNCTION:
case MPI2_IOCSTATUS_INTERNAL_ERROR:
@@ -1643,63 +2010,291 @@ mpssas_scsiio_complete(struct mps_softc *sc, struct mps_command *cm)
case MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH:
case MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED:
default:
+ mpssas_log_command(cm,
+ "completed ioc %x scsi %x state %x xfer %u\n",
+ rep->IOCStatus, rep->SCSIStatus, rep->SCSIState,
+ rep->TransferCount);
+ csio->resid = cm->cm_length;
ccb->ccb_h.status = CAM_REQ_CMP_ERR;
+ break;
}
-
- if ((rep->SCSIState & MPI2_SCSI_STATE_NO_SCSI_STATUS) == 0) {
- ccb->csio.scsi_status = rep->SCSIStatus;
-
- switch (rep->SCSIStatus) {
- case MPI2_SCSI_STATUS_TASK_SET_FULL:
- case MPI2_SCSI_STATUS_CHECK_CONDITION:
- ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR;
- break;
- case MPI2_SCSI_STATUS_COMMAND_TERMINATED:
- case MPI2_SCSI_STATUS_TASK_ABORTED:
- ccb->ccb_h.status = CAM_REQ_ABORTED;
- break;
- case MPI2_SCSI_STATUS_GOOD:
- default:
- break;
- }
- }
-
- if (rep->SCSIState & MPI2_SCSI_STATE_AUTOSENSE_VALID) {
- int sense_len;
-
- if (rep->SenseCount < ccb->csio.sense_len)
- ccb->csio.sense_resid = ccb->csio.sense_len -
- rep->SenseCount;
- else
- ccb->csio.sense_resid = 0;
-
- sense_len = min(rep->SenseCount, ccb->csio.sense_len -
- ccb->csio.sense_resid);
- bzero(&ccb->csio.sense_data, sizeof(&ccb->csio.sense_data));
- bcopy(cm->cm_sense, &ccb->csio.sense_data, sense_len);
- ccb->ccb_h.status |= CAM_AUTOSNS_VALID;
- }
-
- if (rep->SCSIState & MPI2_SCSI_STATE_AUTOSENSE_FAILED)
- ccb->ccb_h.status = CAM_AUTOSENSE_FAIL;
-
- if (rep->SCSIState & MPI2_SCSI_STATE_RESPONSE_INFO_VALID)
- ccb->ccb_h.status = CAM_REQ_CMP_ERR;
-
if (sassc->flags & MPSSAS_QUEUE_FROZEN) {
ccb->ccb_h.status |= CAM_RELEASE_SIMQ;
sassc->flags &= ~MPSSAS_QUEUE_FROZEN;
- mps_printf(sc, "Command completed, unfreezing SIM queue\n");
+ mps_dprint(sc, MPS_INFO, "Command completed, "
+ "unfreezing SIM queue\n");
}
+
if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
ccb->ccb_h.status |= CAM_DEV_QFRZN;
xpt_freeze_devq(ccb->ccb_h.path, /*count*/ 1);
}
+
mps_free_command(sc, cm);
xpt_done(ccb);
}
+static void
+mpssas_direct_drive_io(struct mpssas_softc *sassc, struct mps_command *cm,
+ union ccb *ccb) {
+ pMpi2SCSIIORequest_t pIO_req;
+ struct mps_softc *sc = sassc->sc;
+ uint64_t virtLBA;
+ uint32_t physLBA, stripe_offset, stripe_unit;
+ uint32_t io_size, column;
+ uint8_t *ptrLBA, lba_idx, physLBA_byte, *CDB;
+
+ /*
+ * If this is a valid SCSI command (Read6, Read10, Read16, Write6,
+ * Write10, or Write16), build a direct I/O message. Otherwise, the I/O
+ * will be sent to the IR volume itself. Since Read6 and Write6 are a
+ * bit different than the 10/16 CDBs, handle them separately.
+ */
+ pIO_req = (pMpi2SCSIIORequest_t)cm->cm_req;
+ CDB = pIO_req->CDB.CDB32;
+
+ /*
+ * Handle 6 byte CDBs.
+ */
+ if ((pIO_req->DevHandle == sc->DD_dev_handle) && ((CDB[0] == READ_6) ||
+ (CDB[0] == WRITE_6))) {
+ /*
+ * Get the transfer size in blocks.
+ */
+ io_size = (cm->cm_length >> sc->DD_block_exponent);
+
+ /*
+ * Get virtual LBA given in the CDB.
+ */
+ virtLBA = ((uint64_t)(CDB[1] & 0x1F) << 16) |
+ ((uint64_t)CDB[2] << 8) | (uint64_t)CDB[3];
+
+ /*
+ * Check that LBA range for I/O does not exceed volume's
+ * MaxLBA.
+ */
+ if ((virtLBA + (uint64_t)io_size - 1) <=
+ sc->DD_max_lba) {
+ /*
+ * Check if the I/O crosses a stripe boundary. If not,
+ * translate the virtual LBA to a physical LBA and set
+ * the DevHandle for the PhysDisk to be used. If it
+ * does cross a boundry, do normal I/O. To get the
+ * right DevHandle to use, get the map number for the
+ * column, then use that map number to look up the
+ * DevHandle of the PhysDisk.
+ */
+ stripe_offset = (uint32_t)virtLBA &
+ (sc->DD_stripe_size - 1);
+ if ((stripe_offset + io_size) <= sc->DD_stripe_size) {
+ physLBA = (uint32_t)virtLBA >>
+ sc->DD_stripe_exponent;
+ stripe_unit = physLBA / sc->DD_num_phys_disks;
+ column = physLBA % sc->DD_num_phys_disks;
+ pIO_req->DevHandle =
+ sc->DD_column_map[column].dev_handle;
+ cm->cm_desc.SCSIIO.DevHandle =
+ pIO_req->DevHandle;
+
+ physLBA = (stripe_unit <<
+ sc->DD_stripe_exponent) + stripe_offset;
+ ptrLBA = &pIO_req->CDB.CDB32[1];
+ physLBA_byte = (uint8_t)(physLBA >> 16);
+ *ptrLBA = physLBA_byte;
+ ptrLBA = &pIO_req->CDB.CDB32[2];
+ physLBA_byte = (uint8_t)(physLBA >> 8);
+ *ptrLBA = physLBA_byte;
+ ptrLBA = &pIO_req->CDB.CDB32[3];
+ physLBA_byte = (uint8_t)physLBA;
+ *ptrLBA = physLBA_byte;
+
+ /*
+ * Set flag that Direct Drive I/O is
+ * being done.
+ */
+ cm->cm_flags |= MPS_CM_FLAGS_DD_IO;
+ }
+ }
+ return;
+ }
+
+ /*
+ * Handle 10 or 16 byte CDBs.
+ */
+ if ((pIO_req->DevHandle == sc->DD_dev_handle) && ((CDB[0] == READ_10) ||
+ (CDB[0] == WRITE_10) || (CDB[0] == READ_16) ||
+ (CDB[0] == WRITE_16))) {
+ /*
+ * For 16-byte CDB's, verify that the upper 4 bytes of the CDB
+ * are 0. If not, this is accessing beyond 2TB so handle it in
+ * the else section. 10-byte CDB's are OK.
+ */
+ if ((CDB[0] < READ_16) ||
+ !(CDB[2] | CDB[3] | CDB[4] | CDB[5])) {
+ /*
+ * Get the transfer size in blocks.
+ */
+ io_size = (cm->cm_length >> sc->DD_block_exponent);
+
+ /*
+ * Get virtual LBA. Point to correct lower 4 bytes of
+ * LBA in the CDB depending on command.
+ */
+ lba_idx = (CDB[0] < READ_16) ? 2 : 6;
+ virtLBA = ((uint64_t)CDB[lba_idx] << 24) |
+ ((uint64_t)CDB[lba_idx + 1] << 16) |
+ ((uint64_t)CDB[lba_idx + 2] << 8) |
+ (uint64_t)CDB[lba_idx + 3];
+
+ /*
+ * Check that LBA range for I/O does not exceed volume's
+ * MaxLBA.
+ */
+ if ((virtLBA + (uint64_t)io_size - 1) <=
+ sc->DD_max_lba) {
+ /*
+ * Check if the I/O crosses a stripe boundary.
+ * If not, translate the virtual LBA to a
+ * physical LBA and set the DevHandle for the
+ * PhysDisk to be used. If it does cross a
+ * boundry, do normal I/O. To get the right
+ * DevHandle to use, get the map number for the
+ * column, then use that map number to look up
+ * the DevHandle of the PhysDisk.
+ */
+ stripe_offset = (uint32_t)virtLBA &
+ (sc->DD_stripe_size - 1);
+ if ((stripe_offset + io_size) <=
+ sc->DD_stripe_size) {
+ physLBA = (uint32_t)virtLBA >>
+ sc->DD_stripe_exponent;
+ stripe_unit = physLBA /
+ sc->DD_num_phys_disks;
+ column = physLBA %
+ sc->DD_num_phys_disks;
+ pIO_req->DevHandle =
+ sc->DD_column_map[column].
+ dev_handle;
+ cm->cm_desc.SCSIIO.DevHandle =
+ pIO_req->DevHandle;
+
+ physLBA = (stripe_unit <<
+ sc->DD_stripe_exponent) +
+ stripe_offset;
+ ptrLBA =
+ &pIO_req->CDB.CDB32[lba_idx];
+ physLBA_byte = (uint8_t)(physLBA >> 24);
+ *ptrLBA = physLBA_byte;
+ ptrLBA =
+ &pIO_req->CDB.CDB32[lba_idx + 1];
+ physLBA_byte = (uint8_t)(physLBA >> 16);
+ *ptrLBA = physLBA_byte;
+ ptrLBA =
+ &pIO_req->CDB.CDB32[lba_idx + 2];
+ physLBA_byte = (uint8_t)(physLBA >> 8);
+ *ptrLBA = physLBA_byte;
+ ptrLBA =
+ &pIO_req->CDB.CDB32[lba_idx + 3];
+ physLBA_byte = (uint8_t)physLBA;
+ *ptrLBA = physLBA_byte;
+
+ /*
+ * Set flag that Direct Drive I/O is
+ * being done.
+ */
+ cm->cm_flags |= MPS_CM_FLAGS_DD_IO;
+ }
+ }
+ } else {
+ /*
+ * 16-byte CDB and the upper 4 bytes of the CDB are not
+ * 0. Get the transfer size in blocks.
+ */
+ io_size = (cm->cm_length >> sc->DD_block_exponent);
+
+ /*
+ * Get virtual LBA.
+ */
+ virtLBA = ((uint64_t)CDB[2] << 54) |
+ ((uint64_t)CDB[3] << 48) |
+ ((uint64_t)CDB[4] << 40) |
+ ((uint64_t)CDB[5] << 32) |
+ ((uint64_t)CDB[6] << 24) |
+ ((uint64_t)CDB[7] << 16) |
+ ((uint64_t)CDB[8] << 8) |
+ (uint64_t)CDB[9];
+
+ /*
+ * Check that LBA range for I/O does not exceed volume's
+ * MaxLBA.
+ */
+ if ((virtLBA + (uint64_t)io_size - 1) <=
+ sc->DD_max_lba) {
+ /*
+ * Check if the I/O crosses a stripe boundary.
+ * If not, translate the virtual LBA to a
+ * physical LBA and set the DevHandle for the
+ * PhysDisk to be used. If it does cross a
+ * boundry, do normal I/O. To get the right
+ * DevHandle to use, get the map number for the
+ * column, then use that map number to look up
+ * the DevHandle of the PhysDisk.
+ */
+ stripe_offset = (uint32_t)virtLBA &
+ (sc->DD_stripe_size - 1);
+ if ((stripe_offset + io_size) <=
+ sc->DD_stripe_size) {
+ physLBA = (uint32_t)(virtLBA >>
+ sc->DD_stripe_exponent);
+ stripe_unit = physLBA /
+ sc->DD_num_phys_disks;
+ column = physLBA %
+ sc->DD_num_phys_disks;
+ pIO_req->DevHandle =
+ sc->DD_column_map[column].
+ dev_handle;
+ cm->cm_desc.SCSIIO.DevHandle =
+ pIO_req->DevHandle;
+
+ physLBA = (stripe_unit <<
+ sc->DD_stripe_exponent) +
+ stripe_offset;
+
+ /*
+ * Set upper 4 bytes of LBA to 0. We
+ * assume that the phys disks are less
+ * than 2 TB's in size. Then, set the
+ * lower 4 bytes.
+ */
+ pIO_req->CDB.CDB32[2] = 0;
+ pIO_req->CDB.CDB32[3] = 0;
+ pIO_req->CDB.CDB32[4] = 0;
+ pIO_req->CDB.CDB32[5] = 0;
+ ptrLBA = &pIO_req->CDB.CDB32[6];
+ physLBA_byte = (uint8_t)(physLBA >> 24);
+ *ptrLBA = physLBA_byte;
+ ptrLBA = &pIO_req->CDB.CDB32[7];
+ physLBA_byte = (uint8_t)(physLBA >> 16);
+ *ptrLBA = physLBA_byte;
+ ptrLBA = &pIO_req->CDB.CDB32[8];
+ physLBA_byte = (uint8_t)(physLBA >> 8);
+ *ptrLBA = physLBA_byte;
+ ptrLBA = &pIO_req->CDB.CDB32[9];
+ physLBA_byte = (uint8_t)physLBA;
+ *ptrLBA = physLBA_byte;
+
+ /*
+ * Set flag that Direct Drive I/O is
+ * being done.
+ */
+ cm->cm_flags |= MPS_CM_FLAGS_DD_IO;
+ }
+ }
+ }
+ }
+}
+
#if __FreeBSD_version >= 900026
static void
mpssas_smpio_complete(struct mps_softc *sc, struct mps_command *cm)
@@ -1722,7 +2317,7 @@ mpssas_smpio_complete(struct mps_softc *sc, struct mps_command *cm)
__func__, cm->cm_flags);
ccb->ccb_h.status = CAM_REQ_CMP_ERR;
goto bailout;
- }
+ }
rpl = (MPI2_SMP_PASSTHROUGH_REPLY *)cm->cm_reply;
if (rpl == NULL) {
@@ -1989,7 +2584,9 @@ mpssas_action_smpio(struct mpssas_softc *sassc, union ccb *ccb)
* the parent device of this device, which is probably the expander.
*/
if (sasaddr == 0) {
+#ifdef OLD_MPS_PROBE
struct mpssas_target *parent_target;
+#endif
if (targ->parent_handle == 0x0) {
mps_printf(sc, "%s: handle %d does not have a valid "
@@ -1997,8 +2594,9 @@ mpssas_action_smpio(struct mpssas_softc *sassc, union ccb *ccb)
ccb->ccb_h.status = CAM_REQ_INVALID;
goto bailout;
}
- parent_target = mpssas_find_target(sassc, 0,
- targ->parent_handle);
+#ifdef OLD_MPS_PROBE
+ parent_target = mpssas_find_target_by_handle(sassc, 0,
+ targ->parent_handle);
if (parent_target == NULL) {
mps_printf(sc, "%s: handle %d does not have a valid "
@@ -2018,6 +2616,27 @@ mpssas_action_smpio(struct mpssas_softc *sassc, union ccb *ccb)
}
sasaddr = parent_target->sasaddr;
+#else /* OLD_MPS_PROBE */
+ if ((targ->parent_devinfo &
+ MPI2_SAS_DEVICE_INFO_SMP_TARGET) == 0) {
+ mps_printf(sc, "%s: handle %d parent %d does not "
+ "have an SMP target!\n", __func__,
+ targ->handle, targ->parent_handle);
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ goto bailout;
+
+ }
+ if (targ->parent_sasaddr == 0x0) {
+ mps_printf(sc, "%s: handle %d parent handle %d does "
+ "not have a valid SAS address!\n",
+ __func__, targ->handle, targ->parent_handle);
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ goto bailout;
+ }
+
+ sasaddr = targ->parent_sasaddr;
+#endif /* OLD_MPS_PROBE */
+
}
if (sasaddr == 0) {
@@ -2034,99 +2653,87 @@ bailout:
xpt_done(ccb);
}
-
-#endif /* __FreeBSD_version >= 900026 */
+#endif //__FreeBSD_version >= 900026
static void
mpssas_action_resetdev(struct mpssas_softc *sassc, union ccb *ccb)
{
+ MPI2_SCSI_TASK_MANAGE_REQUEST *req;
struct mps_softc *sc;
- struct mps_command *cm;
+ struct mps_command *tm;
struct mpssas_target *targ;
- sc = sassc->sc;
- targ = &sassc->targets[ccb->ccb_h.target_id];
-
- if (targ->flags & MPSSAS_TARGET_INRECOVERY) {
- ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
- xpt_done(ccb);
- return;
- }
+ mps_dprint(sassc->sc, MPS_TRACE, __func__);
+ mtx_assert(&sassc->sc->mps_mtx, MA_OWNED);
- cm = mps_alloc_command(sc);
- if (cm == NULL) {
- mps_printf(sc, "%s: cannot alloc command\n", __func__);
+ sc = sassc->sc;
+ tm = mps_alloc_command(sc);
+ if (tm == NULL) {
+ mps_printf(sc, "comand alloc failure in mpssas_action_resetdev\n");
ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
xpt_done(ccb);
return;
}
- cm->cm_targ = targ;
- cm->cm_complete = mpssas_resetdev_complete;
- cm->cm_complete_data = ccb;
-
- mpssas_resetdev(sassc, cm);
-}
-
-static void
-mpssas_resetdev(struct mpssas_softc *sassc, struct mps_command *cm)
-{
- MPI2_SCSI_TASK_MANAGE_REQUEST *req;
- struct mps_softc *sc;
-
- mps_dprint(sassc->sc, MPS_TRACE, "%s\n", __func__);
-
- sc = sassc->sc;
-
- req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)cm->cm_req;
- req->DevHandle = cm->cm_targ->handle;
+ targ = &sassc->targets[ccb->ccb_h.target_id];
+ req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)tm->cm_req;
+ req->DevHandle = targ->handle;
req->Function = MPI2_FUNCTION_SCSI_TASK_MGMT;
req->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET;
/* SAS Hard Link Reset / SATA Link Reset */
req->MsgFlags = MPI2_SCSITASKMGMT_MSGFLAGS_LINK_RESET;
- cm->cm_data = NULL;
- cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
-
- mpssas_issue_tm_request(sc, cm);
+ tm->cm_data = NULL;
+ tm->cm_desc.HighPriority.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY;
+ tm->cm_complete = mpssas_resetdev_complete;
+ tm->cm_complete_data = ccb;
+ mps_map_command(sc, tm);
}
static void
-mpssas_resetdev_complete(struct mps_softc *sc, struct mps_command *cm)
+mpssas_resetdev_complete(struct mps_softc *sc, struct mps_command *tm)
{
MPI2_SCSI_TASK_MANAGE_REPLY *resp;
union ccb *ccb;
- mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+ mps_dprint(sc, MPS_TRACE, __func__);
+ mtx_assert(&sc->mps_mtx, MA_OWNED);
- resp = (MPI2_SCSI_TASK_MANAGE_REPLY *)cm->cm_reply;
- ccb = cm->cm_complete_data;
+ resp = (MPI2_SCSI_TASK_MANAGE_REPLY *)tm->cm_reply;
+ ccb = tm->cm_complete_data;
- if ((cm->cm_flags & MPS_CM_FLAGS_ERROR_MASK) != 0) {
+ /*
+ * Currently there should be no way we can hit this case. It only
+ * happens when we have a failure to allocate chain frames, and
+ * task management commands don't have S/G lists.
+ */
+ if ((tm->cm_flags & MPS_CM_FLAGS_ERROR_MASK) != 0) {
MPI2_SCSI_TASK_MANAGE_REQUEST *req;
- req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)cm->cm_req;
+ req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)tm->cm_req;
mps_printf(sc, "%s: cm_flags = %#x for reset of handle %#04x! "
- "This should not happen!\n", __func__, cm->cm_flags,
+ "This should not happen!\n", __func__, tm->cm_flags,
req->DevHandle);
-
ccb->ccb_h.status = CAM_REQ_CMP_ERR;
goto bailout;
}
- printf("resetdev complete IOCStatus= 0x%x ResponseCode= 0x%x\n",
+ printf("%s: IOCStatus = 0x%x ResponseCode = 0x%x\n", __func__,
resp->IOCStatus, resp->ResponseCode);
- if (resp->ResponseCode == MPI2_SCSITASKMGMT_RSP_TM_COMPLETE)
+ if (resp->ResponseCode == MPI2_SCSITASKMGMT_RSP_TM_COMPLETE) {
ccb->ccb_h.status = CAM_REQ_CMP;
+ mpssas_announce_reset(sc, AC_SENT_BDR, tm->cm_targ->tid,
+ CAM_LUN_WILDCARD);
+ }
else
ccb->ccb_h.status = CAM_REQ_CMP_ERR;
bailout:
- mpssas_complete_tm_request(sc, cm, /*free_cm*/ 1);
+ mpssas_free_tm(sc, tm);
xpt_done(ccb);
}
@@ -2136,16 +2743,502 @@ mpssas_poll(struct cam_sim *sim)
struct mpssas_softc *sassc;
sassc = cam_sim_softc(sim);
+
+ if (sassc->sc->mps_debug & MPS_TRACE) {
+ /* frequent debug messages during a panic just slow
+ * everything down too much.
+ */
+ mps_printf(sassc->sc, "%s clearing MPS_TRACE\n", __func__);
+ sassc->sc->mps_debug &= ~MPS_TRACE;
+ }
+
mps_intr_locked(sassc->sc);
}
static void
-mpssas_freeze_device(struct mpssas_softc *sassc, struct mpssas_target *targ)
+mpssas_rescan_done(struct cam_periph *periph, union ccb *done_ccb)
+{
+ struct mpssas_softc *sassc;
+ char path_str[64];
+
+ if (done_ccb == NULL)
+ return;
+
+ sassc = (struct mpssas_softc *)done_ccb->ccb_h.ppriv_ptr1;
+
+ mtx_assert(&sassc->sc->mps_mtx, MA_OWNED);
+
+ xpt_path_string(done_ccb->ccb_h.path, path_str, sizeof(path_str));
+ mps_dprint(sassc->sc, MPS_INFO, "Completing rescan for %s\n", path_str);
+
+ xpt_free_path(done_ccb->ccb_h.path);
+ xpt_free_ccb(done_ccb);
+
+#if __FreeBSD_version < 1000006
+ /*
+ * Before completing scan, get EEDP stuff for all of the existing
+ * targets.
+ */
+ mpssas_check_eedp(sassc);
+#endif
+
+}
+
+/* thread to handle bus rescans */
+static void
+mpssas_scanner_thread(void *arg)
+{
+ struct mpssas_softc *sassc;
+ struct mps_softc *sc;
+ union ccb *ccb;
+
+ sassc = (struct mpssas_softc *)arg;
+ sc = sassc->sc;
+
+ mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+
+ mps_lock(sc);
+ for (;;) {
+ msleep(&sassc->ccb_scanq, &sc->mps_mtx, PRIBIO,
+ "mps_scanq", 0);
+ if (sassc->flags & MPSSAS_SHUTDOWN) {
+ mps_dprint(sc, MPS_TRACE, "Scanner shutting down\n");
+ break;
+ }
+ ccb = (union ccb *)TAILQ_FIRST(&sassc->ccb_scanq);
+ if (ccb == NULL)
+ continue;
+ TAILQ_REMOVE(&sassc->ccb_scanq, &ccb->ccb_h, sim_links.tqe);
+ xpt_action(ccb);
+ }
+
+ sassc->flags &= ~MPSSAS_SCANTHREAD;
+ wakeup(&sassc->flags);
+ mps_unlock(sc);
+ mps_dprint(sc, MPS_TRACE, "Scanner exiting\n");
+ mps_kproc_exit(0);
+}
+
+static void
+mpssas_rescan(struct mpssas_softc *sassc, union ccb *ccb)
+{
+ char path_str[64];
+
+ mps_dprint(sassc->sc, MPS_TRACE, "%s\n", __func__);
+
+ mtx_assert(&sassc->sc->mps_mtx, MA_OWNED);
+
+ if (ccb == NULL)
+ return;
+
+ xpt_path_string(ccb->ccb_h.path, path_str, sizeof(path_str));
+ mps_dprint(sassc->sc, MPS_INFO, "Queueing rescan for %s\n", path_str);
+
+ /* Prepare request */
+ ccb->ccb_h.ppriv_ptr1 = sassc;
+ ccb->ccb_h.cbfcnp = mpssas_rescan_done;
+ xpt_setup_ccb(&ccb->ccb_h, ccb->ccb_h.path, MPS_PRIORITY_XPT);
+ TAILQ_INSERT_TAIL(&sassc->ccb_scanq, &ccb->ccb_h, sim_links.tqe);
+ wakeup(&sassc->ccb_scanq);
+}
+
+#if __FreeBSD_version >= 1000006
+static void
+mpssas_async(void *callback_arg, uint32_t code, struct cam_path *path,
+ void *arg)
+{
+ struct mps_softc *sc;
+
+ sc = (struct mps_softc *)callback_arg;
+
+ switch (code) {
+ case AC_ADVINFO_CHANGED: {
+ struct mpssas_target *target;
+ struct mpssas_softc *sassc;
+ struct scsi_read_capacity_data_long rcap_buf;
+ struct ccb_dev_advinfo cdai;
+ struct mpssas_lun *lun;
+ lun_id_t lunid;
+ int found_lun;
+ uintptr_t buftype;
+
+ buftype = (uintptr_t)arg;
+
+ found_lun = 0;
+ sassc = sc->sassc;
+
+ /*
+ * We're only interested in read capacity data changes.
+ */
+ if (buftype != CDAI_TYPE_RCAPLONG)
+ break;
+
+ /*
+ * We're only interested in devices that are attached to
+ * this controller.
+ */
+ if (xpt_path_path_id(path) != sassc->sim->path_id)
+ break;
+
+ /*
+ * We should have a handle for this, but check to make sure.
+ */
+ target = &sassc->targets[xpt_path_target_id(path)];
+ if (target->handle == 0)
+ break;
+
+ lunid = xpt_path_lun_id(path);
+
+ SLIST_FOREACH(lun, &target->luns, lun_link) {
+ if (lun->lun_id == lunid) {
+ found_lun = 1;
+ break;
+ }
+ }
+
+ if (found_lun == 0) {
+ lun = malloc(sizeof(struct mpssas_lun), M_MPT2,
+ M_NOWAIT | M_ZERO);
+ if (lun == NULL) {
+ mps_dprint(sc, MPS_FAULT, "Unable to alloc "
+ "LUN for EEDP support.\n");
+ break;
+ }
+ lun->lun_id = lunid;
+ SLIST_INSERT_HEAD(&target->luns, lun, lun_link);
+ }
+
+ bzero(&rcap_buf, sizeof(rcap_buf));
+ xpt_setup_ccb(&cdai.ccb_h, path, CAM_PRIORITY_NORMAL);
+ cdai.ccb_h.func_code = XPT_DEV_ADVINFO;
+ cdai.ccb_h.flags = CAM_DIR_IN;
+ cdai.buftype = CDAI_TYPE_RCAPLONG;
+ cdai.flags = 0;
+ cdai.bufsiz = sizeof(rcap_buf);
+ cdai.buf = (uint8_t *)&rcap_buf;
+ xpt_action((union ccb *)&cdai);
+ if ((cdai.ccb_h.status & CAM_DEV_QFRZN) != 0)
+ cam_release_devq(cdai.ccb_h.path,
+ 0, 0, 0, FALSE);
+
+ if (((cdai.ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP)
+ && (rcap_buf.prot & SRC16_PROT_EN)) {
+ lun->eedp_formatted = TRUE;
+ lun->eedp_block_size = scsi_4btoul(rcap_buf.length);
+ } else {
+ lun->eedp_formatted = FALSE;
+ lun->eedp_block_size = 0;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+#else /* __FreeBSD_version >= 1000006 */
+
+static void
+mpssas_check_eedp(struct mpssas_softc *sassc)
{
+ struct mps_softc *sc = sassc->sc;
+ struct ccb_scsiio *csio;
+ struct scsi_read_capacity_16 *scsi_cmd;
+ struct scsi_read_capacity_eedp *rcap_buf;
+ union ccb *ccb;
+ path_id_t pathid = cam_sim_path(sassc->sim);
+ target_id_t targetid;
+ lun_id_t lunid;
+ struct cam_periph *found_periph;
+ struct mpssas_target *target;
+ struct mpssas_lun *lun;
+ uint8_t found_lun;
+
+ /*
+ * Issue a READ CAPACITY 16 command to each LUN of each target. This
+ * info is used to determine if the LUN is formatted for EEDP support.
+ */
+ for (targetid = 0; targetid < sc->facts->MaxTargets; targetid++) {
+ target = &sassc->targets[targetid];
+ if (target->handle == 0x0) {
+ continue;
+ }
+
+ lunid = 0;
+ do {
+ rcap_buf =
+ malloc(sizeof(struct scsi_read_capacity_eedp),
+ M_MPT2, M_NOWAIT | M_ZERO);
+ if (rcap_buf == NULL) {
+ mps_dprint(sc, MPS_FAULT, "Unable to alloc read "
+ "capacity buffer for EEDP support.\n");
+ return;
+ }
+ ccb = xpt_alloc_ccb_nowait();
+ if (ccb == NULL) {
+ mps_dprint(sc, MPS_FAULT, "Unable to alloc CCB "
+ "for EEDP support.\n");
+ free(rcap_buf, M_MPT2);
+ return;
+ }
+
+ if (xpt_create_path(&ccb->ccb_h.path, xpt_periph,
+ pathid, targetid, lunid) != CAM_REQ_CMP) {
+ mps_dprint(sc, MPS_FAULT, "Unable to create "
+ "path for EEDP support\n");
+ free(rcap_buf, M_MPT2);
+ xpt_free_ccb(ccb);
+ return;
+ }
+
+ /*
+ * If a periph is returned, the LUN exists. Create an
+ * entry in the target's LUN list.
+ */
+ if ((found_periph = cam_periph_find(ccb->ccb_h.path,
+ NULL)) != NULL) {
+ /*
+ * If LUN is already in list, don't create a new
+ * one.
+ */
+ found_lun = FALSE;
+ SLIST_FOREACH(lun, &target->luns, lun_link) {
+ if (lun->lun_id == lunid) {
+ found_lun = TRUE;
+ break;
+ }
+ }
+ if (!found_lun) {
+ lun = malloc(sizeof(struct mpssas_lun),
+ M_MPT2, M_WAITOK | M_ZERO);
+ if (lun == NULL) {
+ mps_dprint(sc, MPS_FAULT,
+ "Unable to alloc LUN for "
+ "EEDP support.\n");
+ free(rcap_buf, M_MPT2);
+ xpt_free_path(ccb->ccb_h.path);
+ xpt_free_ccb(ccb);
+ return;
+ }
+ lun->lun_id = lunid;
+ SLIST_INSERT_HEAD(&target->luns, lun,
+ lun_link);
+ }
+ lunid++;
+
+ /*
+ * Issue a READ CAPACITY 16 command for the LUN.
+ * The mpssas_read_cap_done function will load
+ * the read cap info into the LUN struct.
+ */
+ csio = &ccb->csio;
+ csio->ccb_h.func_code = XPT_SCSI_IO;
+ csio->ccb_h.flags = CAM_DIR_IN;
+ csio->ccb_h.retry_count = 4;
+ csio->ccb_h.cbfcnp = mpssas_read_cap_done;
+ csio->ccb_h.timeout = 60000;
+ csio->data_ptr = (uint8_t *)rcap_buf;
+ csio->dxfer_len = sizeof(struct
+ scsi_read_capacity_eedp);
+ csio->sense_len = MPS_SENSE_LEN;
+ csio->cdb_len = sizeof(*scsi_cmd);
+ csio->tag_action = MSG_SIMPLE_Q_TAG;
+
+ scsi_cmd = (struct scsi_read_capacity_16 *)
+ &csio->cdb_io.cdb_bytes;
+ bzero(scsi_cmd, sizeof(*scsi_cmd));
+ scsi_cmd->opcode = 0x9E;
+ scsi_cmd->service_action = SRC16_SERVICE_ACTION;
+ ((uint8_t *)scsi_cmd)[13] = sizeof(struct
+ scsi_read_capacity_eedp);
+
+ /*
+ * Set the path, target and lun IDs for the READ
+ * CAPACITY request.
+ */
+ ccb->ccb_h.path_id =
+ xpt_path_path_id(ccb->ccb_h.path);
+ ccb->ccb_h.target_id =
+ xpt_path_target_id(ccb->ccb_h.path);
+ ccb->ccb_h.target_lun =
+ xpt_path_lun_id(ccb->ccb_h.path);
+
+ ccb->ccb_h.ppriv_ptr1 = sassc;
+ xpt_action(ccb);
+ } else {
+ free(rcap_buf, M_MPT2);
+ xpt_free_path(ccb->ccb_h.path);
+ xpt_free_ccb(ccb);
+ }
+ } while (found_periph);
+ }
}
+
static void
-mpssas_unfreeze_device(struct mpssas_softc *sassc, struct mpssas_target *targ)
+mpssas_read_cap_done(struct cam_periph *periph, union ccb *done_ccb)
{
+ struct mpssas_softc *sassc;
+ struct mpssas_target *target;
+ struct mpssas_lun *lun;
+ struct scsi_read_capacity_eedp *rcap_buf;
+
+ if (done_ccb == NULL)
+ return;
+
+ rcap_buf = (struct scsi_read_capacity_eedp *)done_ccb->csio.data_ptr;
+
+ /*
+ * Get the LUN ID for the path and look it up in the LUN list for the
+ * target.
+ */
+ sassc = (struct mpssas_softc *)done_ccb->ccb_h.ppriv_ptr1;
+ target = &sassc->targets[done_ccb->ccb_h.target_id];
+ SLIST_FOREACH(lun, &target->luns, lun_link) {
+ if (lun->lun_id != done_ccb->ccb_h.target_lun)
+ continue;
+
+ /*
+ * Got the LUN in the target's LUN list. Fill it in
+ * with EEDP info. If the READ CAP 16 command had some
+ * SCSI error (common if command is not supported), mark
+ * the lun as not supporting EEDP and set the block size
+ * to 0.
+ */
+ if (((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)
+ || (done_ccb->csio.scsi_status != SCSI_STATUS_OK)) {
+ lun->eedp_formatted = FALSE;
+ lun->eedp_block_size = 0;
+ break;
+ }
+
+ if (rcap_buf->protect & 0x01) {
+ lun->eedp_formatted = TRUE;
+ lun->eedp_block_size = scsi_4btoul(rcap_buf->length);
+ }
+ break;
+ }
+
+ // Finished with this CCB and path.
+ free(rcap_buf, M_MPT2);
+ xpt_free_path(done_ccb->ccb_h.path);
+ xpt_free_ccb(done_ccb);
+}
+#endif /* __FreeBSD_version >= 1000006 */
+
+int
+mpssas_startup(struct mps_softc *sc)
+{
+ struct mpssas_softc *sassc;
+
+ /*
+ * Send the port enable message and set the wait_for_port_enable flag.
+ * This flag helps to keep the simq frozen until all discovery events
+ * are processed.
+ */
+ sassc = sc->sassc;
+ mpssas_startup_increment(sassc);
+ sc->wait_for_port_enable = 1;
+ mpssas_send_portenable(sc);
+ return (0);
+}
+
+static int
+mpssas_send_portenable(struct mps_softc *sc)
+{
+ MPI2_PORT_ENABLE_REQUEST *request;
+ struct mps_command *cm;
+
+ mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+
+ if ((cm = mps_alloc_command(sc)) == NULL)
+ return (EBUSY);
+ request = (MPI2_PORT_ENABLE_REQUEST *)cm->cm_req;
+ request->Function = MPI2_FUNCTION_PORT_ENABLE;
+ request->MsgFlags = 0;
+ request->VP_ID = 0;
+ cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+ cm->cm_complete = mpssas_portenable_complete;
+ cm->cm_data = NULL;
+ cm->cm_sge = NULL;
+
+ mps_map_command(sc, cm);
+ mps_dprint(sc, MPS_TRACE,
+ "mps_send_portenable finished cm %p req %p complete %p\n",
+ cm, cm->cm_req, cm->cm_complete);
+ return (0);
+}
+
+static void
+mpssas_portenable_complete(struct mps_softc *sc, struct mps_command *cm)
+{
+ MPI2_PORT_ENABLE_REPLY *reply;
+ struct mpssas_softc *sassc;
+ struct mpssas_target *target;
+ int i;
+
+ mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+ sassc = sc->sassc;
+
+ /*
+ * Currently there should be no way we can hit this case. It only
+ * happens when we have a failure to allocate chain frames, and
+ * port enable commands don't have S/G lists.
+ */
+ if ((cm->cm_flags & MPS_CM_FLAGS_ERROR_MASK) != 0) {
+ mps_printf(sc, "%s: cm_flags = %#x for port enable! "
+ "This should not happen!\n", __func__, cm->cm_flags);
+ }
+
+ reply = (MPI2_PORT_ENABLE_REPLY *)cm->cm_reply;
+ if (reply == NULL)
+ mps_dprint(sc, MPS_FAULT, "Portenable NULL reply\n");
+ else if ((reply->IOCStatus & MPI2_IOCSTATUS_MASK) !=
+ MPI2_IOCSTATUS_SUCCESS)
+ mps_dprint(sc, MPS_FAULT, "Portenable failed\n");
+
+ mps_free_command(sc, cm);
+ if (sc->mps_ich.ich_arg != NULL) {
+ mps_dprint(sc, MPS_INFO, "disestablish config intrhook\n");
+ config_intrhook_disestablish(&sc->mps_ich);
+ sc->mps_ich.ich_arg = NULL;
+ }
+
+ /*
+ * Get WarpDrive info after discovery is complete but before the scan
+ * starts. At this point, all devices are ready to be exposed to the
+ * OS. If devices should be hidden instead, take them out of the
+ * 'targets' array before the scan. The devinfo for a disk will have
+ * some info and a volume's will be 0. Use that to remove disks.
+ */
+ mps_wd_config_pages(sc);
+ if (((sc->mps_flags & MPS_FLAGS_WD_AVAILABLE)
+ && (sc->WD_hide_expose == MPS_WD_HIDE_ALWAYS))
+ || (sc->WD_valid_config && (sc->WD_hide_expose ==
+ MPS_WD_HIDE_IF_VOLUME))) {
+ for (i = 0; i < sassc->sc->facts->MaxTargets; i++) {
+ target = &sassc->targets[i];
+ if (target->devinfo) {
+ target->devinfo = 0x0;
+ target->encl_handle = 0x0;
+ target->encl_slot = 0x0;
+ target->handle = 0x0;
+ target->tid = 0x0;
+ target->linkrate = 0x0;
+ target->flags = 0x0;
+ }
+ }
+ }
+
+ /*
+ * Done waiting for port enable to complete. Decrement the refcount.
+ * If refcount is 0, discovery is complete and a rescan of the bus can
+ * take place. Since the simq was explicitly frozen before port
+ * enable, it must be explicitly released here to keep the
+ * freeze/release count in sync.
+ */
+ sc->wait_for_port_enable = 0;
+ sc->port_enable_complete = 1;
+ mpssas_startup_decrement(sassc);
+ xpt_release_simq(sassc->sim, 1);
}
diff --git a/sys/dev/mps/mps_sas.h b/sys/dev/mps/mps_sas.h
new file mode 100644
index 0000000..343247f
--- /dev/null
+++ b/sys/dev/mps/mps_sas.h
@@ -0,0 +1,161 @@
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ *
+ * $FreeBSD$
+ */
+
+struct mps_fw_event_work;
+
+struct mpssas_lun {
+ SLIST_ENTRY(mpssas_lun) lun_link;
+ lun_id_t lun_id;
+ uint8_t eedp_formatted;
+ uint32_t eedp_block_size;
+};
+
+struct mpssas_target {
+ uint16_t handle;
+ uint8_t linkrate;
+ uint64_t devname;
+ uint32_t devinfo;
+ uint16_t encl_handle;
+ uint16_t encl_slot;
+ uint8_t flags;
+#define MPSSAS_TARGET_INABORT (1 << 0)
+#define MPSSAS_TARGET_INRESET (1 << 1)
+#define MPSSAS_TARGET_INDIAGRESET (1 << 2)
+#define MPSSAS_TARGET_INREMOVAL (1 << 3)
+#define MPSSAS_TARGET_INRECOVERY (MPSSAS_TARGET_INABORT | \
+ MPSSAS_TARGET_INRESET | MPSSAS_TARGET_INCHIPRESET)
+#define MPSSAS_TARGET_ADD (1 << 29)
+#define MPSSAS_TARGET_REMOVE (1 << 30)
+ uint16_t tid;
+ SLIST_HEAD(, mpssas_lun) luns;
+ TAILQ_HEAD(, mps_command) commands;
+ struct mps_command *tm;
+ TAILQ_HEAD(, mps_command) timedout_commands;
+ uint16_t exp_dev_handle;
+ uint16_t phy_num;
+ uint64_t sasaddr;
+ uint16_t parent_handle;
+ uint64_t parent_sasaddr;
+ uint32_t parent_devinfo;
+ struct sysctl_ctx_list sysctl_ctx;
+ struct sysctl_oid *sysctl_tree;
+ TAILQ_ENTRY(mpssas_target) sysctl_link;
+ uint64_t issued;
+ uint64_t completed;
+ unsigned int outstanding;
+ unsigned int timeouts;
+ unsigned int aborts;
+ unsigned int logical_unit_resets;
+ unsigned int target_resets;
+};
+
+struct mpssas_softc {
+ struct mps_softc *sc;
+ u_int flags;
+#define MPSSAS_IN_DISCOVERY (1 << 0)
+#define MPSSAS_IN_STARTUP (1 << 1)
+#define MPSSAS_DISCOVERY_TIMEOUT_PENDING (1 << 2)
+#define MPSSAS_QUEUE_FROZEN (1 << 3)
+#define MPSSAS_SHUTDOWN (1 << 4)
+#define MPSSAS_SCANTHREAD (1 << 5)
+ struct mpssas_target *targets;
+ struct cam_devq *devq;
+ struct cam_sim *sim;
+ struct cam_path *path;
+ struct intr_config_hook sas_ich;
+ struct callout discovery_callout;
+ u_int discovery_timeouts;
+ struct mps_event_handle *mpssas_eh;
+
+ u_int startup_refcount;
+ u_int tm_count;
+ struct proc *sysctl_proc;
+
+ TAILQ_HEAD(, ccb_hdr) ccb_scanq;
+ struct proc *rescan_thread;
+
+ struct taskqueue *ev_tq;
+ struct task ev_task;
+ TAILQ_HEAD(, mps_fw_event_work) ev_queue;
+};
+
+MALLOC_DECLARE(M_MPSSAS);
+
+/*
+ * Abstracted so that the driver can be backwards and forwards compatible
+ * with future versions of CAM that will provide this functionality.
+ */
+#define MPS_SET_LUN(lun, ccblun) \
+ mpssas_set_lun(lun, ccblun)
+
+static __inline int
+mpssas_set_lun(uint8_t *lun, u_int ccblun)
+{
+ uint64_t *newlun;
+
+ newlun = (uint64_t *)lun;
+ *newlun = 0;
+ if (ccblun <= 0xff) {
+ /* Peripheral device address method, LUN is 0 to 255 */
+ lun[1] = ccblun;
+ } else if (ccblun <= 0x3fff) {
+ /* Flat space address method, LUN is <= 16383 */
+ scsi_ulto2b(ccblun, lun);
+ lun[0] |= 0x40;
+ } else if (ccblun <= 0xffffff) {
+ /* Extended flat space address method, LUN is <= 16777215 */
+ scsi_ulto3b(ccblun, &lun[1]);
+ /* Extended Flat space address method */
+ lun[0] = 0xc0;
+ /* Length = 1, i.e. LUN is 3 bytes long */
+ lun[0] |= 0x10;
+ /* Extended Address Method */
+ lun[0] |= 0x02;
+ } else {
+ return (EINVAL);
+ }
+
+ return (0);
+}
+
+#define MPS_SET_SINGLE_LUN(req, lun) \
+do { \
+ bzero((req)->LUN, 8); \
+ (req)->LUN[1] = lun; \
+} while(0)
+
+void mpssas_rescan_target(struct mps_softc *sc, struct mpssas_target *targ);
+void mpssas_discovery_end(struct mpssas_softc *sassc);
+void mpssas_startup_increment(struct mpssas_softc *sassc);
+void mpssas_startup_decrement(struct mpssas_softc *sassc);
+
+struct mps_command * mpssas_alloc_tm(struct mps_softc *sc);
+void mpssas_free_tm(struct mps_softc *sc, struct mps_command *tm);
+void mpssas_firmware_event_work(void *arg, int pending);
diff --git a/sys/dev/mps/mps_sas_lsi.c b/sys/dev/mps/mps_sas_lsi.c
new file mode 100644
index 0000000..70c74ea
--- /dev/null
+++ b/sys/dev/mps/mps_sas_lsi.c
@@ -0,0 +1,865 @@
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/* Communications core for LSI MPT2 */
+
+/* TODO Move headers to mpsvar */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/selinfo.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/bio.h>
+#include <sys/malloc.h>
+#include <sys/uio.h>
+#include <sys/sysctl.h>
+#include <sys/endian.h>
+#include <sys/queue.h>
+#include <sys/kthread.h>
+#include <sys/taskqueue.h>
+#include <sys/sbuf.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <machine/stdarg.h>
+
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <cam/cam_debug.h>
+#include <cam/cam_sim.h>
+#include <cam/cam_xpt_sim.h>
+#include <cam/cam_xpt_periph.h>
+#include <cam/cam_periph.h>
+#include <cam/scsi/scsi_all.h>
+#include <cam/scsi/scsi_message.h>
+
+#include <dev/mps/mpi/mpi2_type.h>
+#include <dev/mps/mpi/mpi2.h>
+#include <dev/mps/mpi/mpi2_ioc.h>
+#include <dev/mps/mpi/mpi2_sas.h>
+#include <dev/mps/mpi/mpi2_cnfg.h>
+#include <dev/mps/mpi/mpi2_init.h>
+#include <dev/mps/mpi/mpi2_raid.h>
+#include <dev/mps/mpi/mpi2_tool.h>
+#include <dev/mps/mps_ioctl.h>
+#include <dev/mps/mpsvar.h>
+#include <dev/mps/mps_table.h>
+#include <dev/mps/mps_sas.h>
+
+/* For Hashed SAS Address creation for SATA Drives */
+#define MPT2SAS_SN_LEN 20
+#define MPT2SAS_MN_LEN 40
+
+struct mps_fw_event_work {
+ u16 event;
+ void *event_data;
+ TAILQ_ENTRY(mps_fw_event_work) ev_link;
+};
+
+union _sata_sas_address {
+ u8 wwid[8];
+ struct {
+ u32 high;
+ u32 low;
+ } word;
+};
+
+/*
+ * define the IDENTIFY DEVICE structure
+ */
+struct _ata_identify_device_data {
+ u16 reserved1[10]; /* 0-9 */
+ u16 serial_number[10]; /* 10-19 */
+ u16 reserved2[7]; /* 20-26 */
+ u16 model_number[20]; /* 27-46*/
+ u16 reserved3[209]; /* 47-255*/
+};
+
+static void mpssas_fw_work(struct mps_softc *sc,
+ struct mps_fw_event_work *fw_event);
+static void mpssas_fw_event_free(struct mps_softc *,
+ struct mps_fw_event_work *);
+static int mpssas_add_device(struct mps_softc *sc, u16 handle, u8 linkrate);
+static int mpssas_get_sata_identify(struct mps_softc *sc, u16 handle,
+ Mpi2SataPassthroughReply_t *mpi_reply, char *id_buffer, int sz,
+ u32 devinfo);
+int mpssas_get_sas_address_for_sata_disk(struct mps_softc *sc,
+ u64 *sas_address, u16 handle, u32 device_info);
+static int mpssas_volume_add(struct mps_softc *sc,
+ Mpi2EventIrConfigElement_t *element);
+
+void
+mpssas_evt_handler(struct mps_softc *sc, uintptr_t data,
+ MPI2_EVENT_NOTIFICATION_REPLY *event)
+{
+ struct mps_fw_event_work *fw_event;
+ u16 sz;
+
+ mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+ mps_print_evt_sas(sc, event);
+ mpssas_record_event(sc, event);
+
+ fw_event = malloc(sizeof(struct mps_fw_event_work), M_MPT2,
+ M_ZERO|M_NOWAIT);
+ if (!fw_event) {
+ printf("%s: allocate failed for fw_event\n", __func__);
+ return;
+ }
+ sz = le16toh(event->EventDataLength) * 4;
+ fw_event->event_data = malloc(sz, M_MPT2, M_ZERO|M_NOWAIT);
+ if (!fw_event->event_data) {
+ printf("%s: allocate failed for event_data\n", __func__);
+ free(fw_event, M_MPT2);
+ return;
+ }
+
+ bcopy(event->EventData, fw_event->event_data, sz);
+ fw_event->event = event->Event;
+ if ((event->Event == MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST ||
+ event->Event == MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE ||
+ event->Event == MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST) &&
+ sc->track_mapping_events)
+ sc->pending_map_events++;
+
+ /*
+ * When wait_for_port_enable flag is set, make sure that all the events
+ * are processed. Increment the startup_refcount and decrement it after
+ * events are processed.
+ */
+ if ((event->Event == MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST ||
+ event->Event == MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST) &&
+ sc->wait_for_port_enable)
+ mpssas_startup_increment(sc->sassc);
+
+ TAILQ_INSERT_TAIL(&sc->sassc->ev_queue, fw_event, ev_link);
+ taskqueue_enqueue(sc->sassc->ev_tq, &sc->sassc->ev_task);
+
+}
+
+static void
+mpssas_fw_event_free(struct mps_softc *sc, struct mps_fw_event_work *fw_event)
+{
+
+ free(fw_event->event_data, M_MPT2);
+ free(fw_event, M_MPT2);
+}
+
+/**
+ * _mps_fw_work - delayed task for processing firmware events
+ * @sc: per adapter object
+ * @fw_event: The fw_event_work object
+ * Context: user.
+ *
+ * Return nothing.
+ */
+static void
+mpssas_fw_work(struct mps_softc *sc, struct mps_fw_event_work *fw_event)
+{
+ struct mpssas_softc *sassc;
+ sassc = sc->sassc;
+
+ switch (fw_event->event) {
+ case MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST:
+ {
+ MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST *data;
+ MPI2_EVENT_SAS_TOPO_PHY_ENTRY *phy;
+ int i;
+
+ data = (MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST *)
+ fw_event->event_data;
+
+ mps_mapping_topology_change_event(sc, fw_event->event_data);
+
+ for (i = 0; i < data->NumEntries; i++) {
+ phy = &data->PHY[i];
+ switch (phy->PhyStatus & MPI2_EVENT_SAS_TOPO_RC_MASK) {
+ case MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED:
+ if (mpssas_add_device(sc,
+ phy->AttachedDevHandle, phy->LinkRate)){
+ printf("%s: failed to add device with "
+ "handle 0x%x\n", __func__,
+ phy->AttachedDevHandle);
+ mpssas_prepare_remove(sassc, phy->
+ AttachedDevHandle);
+ }
+ break;
+ case MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING:
+ mpssas_prepare_remove(sassc, phy->
+ AttachedDevHandle);
+ break;
+ case MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED:
+ case MPI2_EVENT_SAS_TOPO_RC_NO_CHANGE:
+ case MPI2_EVENT_SAS_TOPO_RC_DELAY_NOT_RESPONDING:
+ default:
+ break;
+ }
+ }
+ /*
+ * refcount was incremented for this event in
+ * mpssas_evt_handler. Decrement it here because the event has
+ * been processed.
+ */
+ mpssas_startup_decrement(sassc);
+ break;
+ }
+ case MPI2_EVENT_SAS_DISCOVERY:
+ {
+ MPI2_EVENT_DATA_SAS_DISCOVERY *data;
+
+ data = (MPI2_EVENT_DATA_SAS_DISCOVERY *)fw_event->event_data;
+
+ if (data->ReasonCode & MPI2_EVENT_SAS_DISC_RC_STARTED)
+ mps_dprint(sc, MPS_TRACE,"SAS discovery start event\n");
+ if (data->ReasonCode & MPI2_EVENT_SAS_DISC_RC_COMPLETED) {
+ mps_dprint(sc, MPS_TRACE,"SAS discovery stop event\n");
+ sassc->flags &= ~MPSSAS_IN_DISCOVERY;
+ mpssas_discovery_end(sassc);
+ }
+ break;
+ }
+ case MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE:
+ {
+ Mpi2EventDataSasEnclDevStatusChange_t *data;
+ data = (Mpi2EventDataSasEnclDevStatusChange_t *)
+ fw_event->event_data;
+ mps_mapping_enclosure_dev_status_change_event(sc,
+ fw_event->event_data);
+ break;
+ }
+ case MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST:
+ {
+ Mpi2EventIrConfigElement_t *element;
+ int i;
+ u8 foreign_config;
+ Mpi2EventDataIrConfigChangeList_t *event_data;
+ struct mpssas_target *targ;
+ unsigned int id;
+
+ event_data = fw_event->event_data;
+ foreign_config = (le32toh(event_data->Flags) &
+ MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG) ? 1 : 0;
+
+ element =
+ (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0];
+ id = mps_mapping_get_raid_id_from_handle
+ (sc, element->VolDevHandle);
+
+ mps_mapping_ir_config_change_event(sc, event_data);
+
+ for (i = 0; i < event_data->NumElements; i++, element++) {
+ switch (element->ReasonCode) {
+ case MPI2_EVENT_IR_CHANGE_RC_VOLUME_CREATED:
+ case MPI2_EVENT_IR_CHANGE_RC_ADDED:
+ if (!foreign_config) {
+ if (mpssas_volume_add(sc, element)) {
+ printf("%s: failed to add RAID "
+ "volume with handle 0x%x\n",
+ __func__, le16toh(element->
+ VolDevHandle));
+ }
+ }
+ break;
+ case MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED:
+ case MPI2_EVENT_IR_CHANGE_RC_REMOVED:
+ /*
+ * Rescan after volume is deleted or removed.
+ */
+ if (!foreign_config) {
+ if (id == MPS_MAP_BAD_ID) {
+ printf("%s: could not get ID "
+ "for volume with handle "
+ "0x%04x\n", __func__,
+ element->VolDevHandle);
+ break;
+ }
+
+ targ = &sassc->targets[id];
+ targ->handle = 0x0;
+ targ->encl_slot = 0x0;
+ targ->encl_handle = 0x0;
+ targ->exp_dev_handle = 0x0;
+ targ->phy_num = 0x0;
+ targ->linkrate = 0x0;
+ mpssas_rescan_target(sc, targ);
+ printf("RAID target id 0x%x removed\n",
+ targ->tid);
+ }
+ break;
+ case MPI2_EVENT_IR_CHANGE_RC_PD_CREATED:
+ /*
+ * Phys Disk of a volume has been created. Hide
+ * it from the OS.
+ */
+ mpssas_prepare_remove(sassc, element->
+ PhysDiskDevHandle);
+ break;
+ case MPI2_EVENT_IR_CHANGE_RC_PD_DELETED:
+ /*
+ * Phys Disk of a volume has been deleted.
+ * Expose it to the OS.
+ */
+ if (mpssas_add_device(sc,
+ element->PhysDiskDevHandle, 0)){
+ printf("%s: failed to add device with "
+ "handle 0x%x\n", __func__,
+ element->PhysDiskDevHandle);
+ mpssas_prepare_remove(sassc, element->
+ PhysDiskDevHandle);
+ }
+ break;
+ }
+ }
+ /*
+ * refcount was incremented for this event in
+ * mpssas_evt_handler. Decrement it here because the event has
+ * been processed.
+ */
+ mpssas_startup_decrement(sassc);
+ break;
+ }
+ case MPI2_EVENT_IR_VOLUME:
+ {
+ Mpi2EventDataIrVolume_t *event_data = fw_event->event_data;
+
+ /*
+ * Informational only.
+ */
+ mps_dprint(sc, MPS_INFO, "Received IR Volume event:\n");
+ switch (event_data->ReasonCode) {
+ case MPI2_EVENT_IR_VOLUME_RC_SETTINGS_CHANGED:
+ mps_dprint(sc, MPS_INFO, " Volume Settings "
+ "changed from 0x%x to 0x%x for Volome with "
+ "handle 0x%x", event_data->PreviousValue,
+ event_data->NewValue,
+ event_data->VolDevHandle);
+ break;
+ case MPI2_EVENT_IR_VOLUME_RC_STATUS_FLAGS_CHANGED:
+ mps_dprint(sc, MPS_INFO, " Volume Status "
+ "changed from 0x%x to 0x%x for Volome with "
+ "handle 0x%x", event_data->PreviousValue,
+ event_data->NewValue,
+ event_data->VolDevHandle);
+ break;
+ case MPI2_EVENT_IR_VOLUME_RC_STATE_CHANGED:
+ mps_dprint(sc, MPS_INFO, " Volume State "
+ "changed from 0x%x to 0x%x for Volome with "
+ "handle 0x%x", event_data->PreviousValue,
+ event_data->NewValue,
+ event_data->VolDevHandle);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case MPI2_EVENT_IR_PHYSICAL_DISK:
+ {
+ Mpi2EventDataIrPhysicalDisk_t *event_data =
+ fw_event->event_data;
+
+ /*
+ * Informational only.
+ */
+ mps_dprint(sc, MPS_INFO, "Received IR Phys Disk event:\n");
+ switch (event_data->ReasonCode) {
+ case MPI2_EVENT_IR_PHYSDISK_RC_SETTINGS_CHANGED:
+ mps_dprint(sc, MPS_INFO, " Phys Disk Settings "
+ "changed from 0x%x to 0x%x for Phys Disk Number "
+ "%d and handle 0x%x at Enclosure handle 0x%x, Slot "
+ "%d", event_data->PreviousValue,
+ event_data->NewValue, event_data->PhysDiskNum,
+ event_data->PhysDiskDevHandle,
+ event_data->EnclosureHandle, event_data->Slot);
+ break;
+ case MPI2_EVENT_IR_PHYSDISK_RC_STATUS_FLAGS_CHANGED:
+ mps_dprint(sc, MPS_INFO, " Phys Disk Status changed "
+ "from 0x%x to 0x%x for Phys Disk Number %d and "
+ "handle 0x%x at Enclosure handle 0x%x, Slot %d",
+ event_data->PreviousValue, event_data->NewValue,
+ event_data->PhysDiskNum,
+ event_data->PhysDiskDevHandle,
+ event_data->EnclosureHandle, event_data->Slot);
+ break;
+ case MPI2_EVENT_IR_PHYSDISK_RC_STATE_CHANGED:
+ mps_dprint(sc, MPS_INFO, " Phys Disk State changed "
+ "from 0x%x to 0x%x for Phys Disk Number %d and "
+ "handle 0x%x at Enclosure handle 0x%x, Slot %d",
+ event_data->PreviousValue, event_data->NewValue,
+ event_data->PhysDiskNum,
+ event_data->PhysDiskDevHandle,
+ event_data->EnclosureHandle, event_data->Slot);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case MPI2_EVENT_IR_OPERATION_STATUS:
+ {
+ Mpi2EventDataIrOperationStatus_t *event_data =
+ fw_event->event_data;
+
+ /*
+ * Informational only.
+ */
+ mps_dprint(sc, MPS_INFO, "Received IR Op Status event:\n");
+ mps_dprint(sc, MPS_INFO, " RAID Operation of %d is %d "
+ "percent complete for Volume with handle 0x%x",
+ event_data->RAIDOperation, event_data->PercentComplete,
+ event_data->VolDevHandle);
+ break;
+ }
+ case MPI2_EVENT_LOG_ENTRY_ADDED:
+ {
+ pMpi2EventDataLogEntryAdded_t logEntry;
+ uint16_t logQualifier;
+ uint8_t logCode;
+
+ logEntry = (pMpi2EventDataLogEntryAdded_t)fw_event->event_data;
+ logQualifier = logEntry->LogEntryQualifier;
+
+ if (logQualifier == MPI2_WD_LOG_ENTRY) {
+ logCode = logEntry->LogData[0];
+
+ switch (logCode) {
+ case MPI2_WD_SSD_THROTTLING:
+ printf("WarpDrive Warning: IO Throttling has "
+ "occurred in the WarpDrive subsystem. "
+ "Check WarpDrive documentation for "
+ "additional details\n");
+ break;
+ case MPI2_WD_DRIVE_LIFE_WARN:
+ printf("WarpDrive Warning: Program/Erase "
+ "Cycles for the WarpDrive subsystem in "
+ "degraded range. Check WarpDrive "
+ "documentation for additional details\n");
+ break;
+ case MPI2_WD_DRIVE_LIFE_DEAD:
+ printf("WarpDrive Fatal Error: There are no "
+ "Program/Erase Cycles for the WarpDrive "
+ "subsystem. The storage device will be in "
+ "read-only mode. Check WarpDrive "
+ "documentation for additional details\n");
+ break;
+ case MPI2_WD_RAIL_MON_FAIL:
+ printf("WarpDrive Fatal Error: The Backup Rail "
+ "Monitor has failed on the WarpDrive "
+ "subsystem. Check WarpDrive documentation "
+ "for additional details\n");
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ }
+ case MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE:
+ case MPI2_EVENT_SAS_BROADCAST_PRIMITIVE:
+ default:
+ mps_dprint(sc, MPS_TRACE,"Unhandled event 0x%0X\n",
+ fw_event->event);
+ break;
+
+ }
+ mpssas_fw_event_free(sc, fw_event);
+}
+
+void
+mpssas_firmware_event_work(void *arg, int pending)
+{
+ struct mps_fw_event_work *fw_event;
+ struct mps_softc *sc;
+
+ sc = (struct mps_softc *)arg;
+ mps_lock(sc);
+ while ((fw_event = TAILQ_FIRST(&sc->sassc->ev_queue)) != NULL) {
+ TAILQ_REMOVE(&sc->sassc->ev_queue, fw_event, ev_link);
+ mpssas_fw_work(sc, fw_event);
+ }
+ mps_unlock(sc);
+}
+
+static int
+mpssas_add_device(struct mps_softc *sc, u16 handle, u8 linkrate){
+ char devstring[80];
+ struct mpssas_softc *sassc;
+ struct mpssas_target *targ;
+ Mpi2ConfigReply_t mpi_reply;
+ Mpi2SasDevicePage0_t config_page;
+ uint64_t sas_address, sata_sas_address;
+ uint64_t parent_sas_address = 0;
+ u16 ioc_pg8_flags = le16toh(sc->ioc_pg8.Flags);
+ u32 device_info, parent_devinfo = 0;
+ unsigned int id;
+ int ret;
+ int error = 0;
+
+ sassc = sc->sassc;
+ mpssas_startup_increment(sassc);
+ if ((mps_config_get_sas_device_pg0(sc, &mpi_reply, &config_page,
+ MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) {
+ printf("%s: error reading SAS device page0\n", __func__);
+ error = ENXIO;
+ goto out;
+ }
+
+ device_info = le32toh(config_page.DeviceInfo);
+
+ if (((device_info & MPI2_SAS_DEVICE_INFO_SMP_TARGET) == 0)
+ && (config_page.ParentDevHandle != 0)) {
+ Mpi2ConfigReply_t tmp_mpi_reply;
+ Mpi2SasDevicePage0_t parent_config_page;
+
+ if ((mps_config_get_sas_device_pg0(sc, &tmp_mpi_reply,
+ &parent_config_page, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE,
+ le16toh(config_page.ParentDevHandle)))) {
+ printf("%s: error reading SAS device %#x page0\n",
+ __func__, le16toh(config_page.ParentDevHandle));
+ } else {
+ parent_sas_address = parent_config_page.SASAddress.High;
+ parent_sas_address = (parent_sas_address << 32) |
+ parent_config_page.SASAddress.Low;
+ parent_devinfo = le32toh(parent_config_page.DeviceInfo);
+ }
+ }
+ /* TODO Check proper endianess */
+ sas_address = config_page.SASAddress.High;
+ sas_address = (sas_address << 32) |
+ config_page.SASAddress.Low;
+
+ if ((ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE)
+ == MPI2_IOCPAGE8_FLAGS_DEVICE_PERSISTENCE_MAPPING) {
+ if (device_info & MPI2_SAS_DEVICE_INFO_SATA_DEVICE) {
+ ret = mpssas_get_sas_address_for_sata_disk(sc,
+ &sata_sas_address, handle, device_info);
+ if (!ret)
+ id = mps_mapping_get_sas_id(sc,
+ sata_sas_address, handle);
+ else
+ id = mps_mapping_get_sas_id(sc,
+ sas_address, handle);
+ } else
+ id = mps_mapping_get_sas_id(sc, sas_address,
+ handle);
+ } else
+ id = mps_mapping_get_sas_id(sc, sas_address, handle);
+
+ if (id == MPS_MAP_BAD_ID) {
+ printf("failure at %s:%d/%s()! Could not get ID for device "
+ "with handle 0x%04x\n", __FILE__, __LINE__, __func__,
+ handle);
+ error = ENXIO;
+ goto out;
+ }
+ mps_vprintf(sc, "SAS Address from SAS device page0 = %jx\n",
+ sas_address);
+ targ = &sassc->targets[id];
+ targ->devinfo = device_info;
+ targ->devname = le32toh(config_page.DeviceName.High);
+ targ->devname = (targ->devname << 32) |
+ le32toh(config_page.DeviceName.Low);
+ targ->encl_handle = le16toh(config_page.EnclosureHandle);
+ targ->encl_slot = le16toh(config_page.Slot);
+ targ->handle = handle;
+ targ->parent_handle = le16toh(config_page.ParentDevHandle);
+ targ->sasaddr = mps_to_u64(&config_page.SASAddress);
+ targ->parent_sasaddr = le64toh(parent_sas_address);
+ targ->parent_devinfo = parent_devinfo;
+ targ->tid = id;
+ targ->linkrate = (linkrate>>4);
+ targ->flags = 0;
+ TAILQ_INIT(&targ->commands);
+ TAILQ_INIT(&targ->timedout_commands);
+ SLIST_INIT(&targ->luns);
+ mps_describe_devinfo(targ->devinfo, devstring, 80);
+ mps_vprintf(sc, "Found device <%s> <%s> <0x%04x> <%d/%d>\n", devstring,
+ mps_describe_table(mps_linkrate_names, targ->linkrate),
+ targ->handle, targ->encl_handle, targ->encl_slot);
+ if ((sassc->flags & MPSSAS_IN_STARTUP) == 0)
+ mpssas_rescan_target(sc, targ);
+ mps_vprintf(sc, "Target id 0x%x added\n", targ->tid);
+out:
+ mpssas_startup_decrement(sassc);
+ return (error);
+
+}
+
+int
+mpssas_get_sas_address_for_sata_disk(struct mps_softc *sc,
+ u64 *sas_address, u16 handle, u32 device_info)
+{
+ Mpi2SataPassthroughReply_t mpi_reply;
+ int i, rc, try_count;
+ u32 *bufferptr;
+ union _sata_sas_address hash_address;
+ struct _ata_identify_device_data ata_identify;
+ u8 buffer[MPT2SAS_MN_LEN + MPT2SAS_SN_LEN];
+ u32 ioc_status;
+ u8 sas_status;
+
+ memset(&ata_identify, 0, sizeof(ata_identify));
+ try_count = 0;
+ do {
+ rc = mpssas_get_sata_identify(sc, handle, &mpi_reply,
+ (char *)&ata_identify, sizeof(ata_identify), device_info);
+ try_count++;
+ ioc_status = le16toh(mpi_reply.IOCStatus)
+ & MPI2_IOCSTATUS_MASK;
+ sas_status = mpi_reply.SASStatus;
+ } while ((rc == -EAGAIN || ioc_status || sas_status) &&
+ (try_count < 5));
+
+ if (rc == 0 && !ioc_status && !sas_status) {
+ mps_dprint(sc, MPS_INFO, "%s: got SATA identify successfully "
+ "for handle = 0x%x with try_count = %d\n",
+ __func__, handle, try_count);
+ } else {
+ mps_dprint(sc, MPS_INFO, "%s: handle = 0x%x failed\n",
+ __func__, handle);
+ return -1;
+ }
+ /* Copy & byteswap the 40 byte model number to a buffer */
+ for (i = 0; i < MPT2SAS_MN_LEN; i += 2) {
+ buffer[i] = ((u8 *)ata_identify.model_number)[i + 1];
+ buffer[i + 1] = ((u8 *)ata_identify.model_number)[i];
+ }
+ /* Copy & byteswap the 20 byte serial number to a buffer */
+ for (i = 0; i < MPT2SAS_SN_LEN; i += 2) {
+ buffer[MPT2SAS_MN_LEN + i] =
+ ((u8 *)ata_identify.serial_number)[i + 1];
+ buffer[MPT2SAS_MN_LEN + i + 1] =
+ ((u8 *)ata_identify.serial_number)[i];
+ }
+ bufferptr = (u32 *)buffer;
+ /* There are 60 bytes to hash down to 8. 60 isn't divisible by 8,
+ * so loop through the first 56 bytes (7*8),
+ * and then add in the last dword.
+ */
+ hash_address.word.low = 0;
+ hash_address.word.high = 0;
+ for (i = 0; (i < ((MPT2SAS_MN_LEN+MPT2SAS_SN_LEN)/8)); i++) {
+ hash_address.word.low += *bufferptr;
+ bufferptr++;
+ hash_address.word.high += *bufferptr;
+ bufferptr++;
+ }
+ /* Add the last dword */
+ hash_address.word.low += *bufferptr;
+ /* Make sure the hash doesn't start with 5, because it could clash
+ * with a SAS address. Change 5 to a D.
+ */
+ if ((hash_address.word.high & 0x000000F0) == (0x00000050))
+ hash_address.word.high |= 0x00000080;
+ *sas_address = (u64)hash_address.wwid[0] << 56 |
+ (u64)hash_address.wwid[1] << 48 | (u64)hash_address.wwid[2] << 40 |
+ (u64)hash_address.wwid[3] << 32 | (u64)hash_address.wwid[4] << 24 |
+ (u64)hash_address.wwid[5] << 16 | (u64)hash_address.wwid[6] << 8 |
+ (u64)hash_address.wwid[7];
+ return 0;
+}
+
+static int
+mpssas_get_sata_identify(struct mps_softc *sc, u16 handle,
+ Mpi2SataPassthroughReply_t *mpi_reply, char *id_buffer, int sz, u32 devinfo)
+{
+ Mpi2SataPassthroughRequest_t *mpi_request;
+ Mpi2SataPassthroughReply_t *reply;
+ struct mps_command *cm;
+ char *buffer;
+ int error = 0;
+
+ buffer = malloc( sz, M_MPT2, M_NOWAIT | M_ZERO);
+ if (!buffer)
+ return ENOMEM;
+
+ if ((cm = mps_alloc_command(sc)) == NULL)
+ return (EBUSY);
+ mpi_request = (MPI2_SATA_PASSTHROUGH_REQUEST *)cm->cm_req;
+ bzero(mpi_request,sizeof(MPI2_SATA_PASSTHROUGH_REQUEST));
+ mpi_request->Function = MPI2_FUNCTION_SATA_PASSTHROUGH;
+ mpi_request->VF_ID = 0;
+ mpi_request->DevHandle = htole16(handle);
+ mpi_request->PassthroughFlags = (MPI2_SATA_PT_REQ_PT_FLAGS_PIO |
+ MPI2_SATA_PT_REQ_PT_FLAGS_READ);
+ mpi_request->DataLength = htole32(sz);
+ mpi_request->CommandFIS[0] = 0x27;
+ mpi_request->CommandFIS[1] = 0x80;
+ mpi_request->CommandFIS[2] = (devinfo &
+ MPI2_SAS_DEVICE_INFO_ATAPI_DEVICE) ? 0xA1 : 0xEC;
+ cm->cm_sge = &mpi_request->SGL;
+ cm->cm_sglsize = sizeof(MPI2_SGE_IO_UNION);
+ cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE | MPS_CM_FLAGS_DATAIN;
+ cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+ cm->cm_data = buffer;
+ cm->cm_length = htole32(sz);
+ error = mps_request_polled(sc, cm);
+ reply = (Mpi2SataPassthroughReply_t *)cm->cm_reply;
+ if (error || (reply == NULL)) {
+ /* FIXME */
+ /* If the poll returns error then we need to do diag reset */
+ printf("%s: poll for page completed with error %d",
+ __func__, error);
+ error = ENXIO;
+ goto out;
+ }
+ bcopy(buffer, id_buffer, sz);
+ bcopy(reply, mpi_reply, sizeof(Mpi2SataPassthroughReply_t));
+ if ((reply->IOCStatus & MPI2_IOCSTATUS_MASK) !=
+ MPI2_IOCSTATUS_SUCCESS) {
+ printf("%s: error reading SATA PASSTHRU; iocstatus = 0x%x\n",
+ __func__, reply->IOCStatus);
+ error = ENXIO;
+ goto out;
+ }
+out:
+ mps_free_command(sc, cm);
+ free(buffer, M_MPT2);
+ return (error);
+}
+
+static int
+mpssas_volume_add(struct mps_softc *sc, Mpi2EventIrConfigElement_t *element)
+{
+ struct mpssas_softc *sassc;
+ struct mpssas_target *targ;
+ u64 wwid;
+ u16 handle = le16toh(element->VolDevHandle);
+ unsigned int id;
+ int error = 0;
+
+ sassc = sc->sassc;
+ mpssas_startup_increment(sassc);
+ mps_config_get_volume_wwid(sc, handle, &wwid);
+ if (!wwid) {
+ printf("%s: invalid WWID; cannot add volume to mapping table\n",
+ __func__);
+ error = ENXIO;
+ goto out;
+ }
+
+ id = mps_mapping_get_raid_id(sc, wwid, handle);
+ if (id == MPS_MAP_BAD_ID) {
+ printf("%s: could not get ID for volume with handle 0x%04x and "
+ "WWID 0x%016llx\n", __func__, handle,
+ (unsigned long long)wwid);
+ error = ENXIO;
+ goto out;
+ }
+
+ targ = &sassc->targets[id];
+ targ->tid = id;
+ targ->handle = handle;
+ targ->devname = wwid;
+ TAILQ_INIT(&targ->commands);
+ TAILQ_INIT(&targ->timedout_commands);
+ SLIST_INIT(&targ->luns);
+ if ((sassc->flags & MPSSAS_IN_STARTUP) == 0)
+ mpssas_rescan_target(sc, targ);
+ mps_dprint(sc, MPS_INFO, "RAID target id %d added (WWID = 0x%jx)\n",
+ targ->tid, wwid);
+out:
+ mpssas_startup_decrement(sassc);
+ return (error);
+}
+
+/**
+ * mpssas_ir_shutdown - IR shutdown notification
+ * @sc: per adapter object
+ *
+ * Sending RAID Action to alert the Integrated RAID subsystem of the IOC that
+ * the host system is shutting down.
+ *
+ * Return nothing.
+ */
+void
+mpssas_ir_shutdown(struct mps_softc *sc)
+{
+ u16 volume_mapping_flags;
+ u16 ioc_pg8_flags = le16toh(sc->ioc_pg8.Flags);
+ struct dev_mapping_table *mt_entry;
+ u32 start_idx, end_idx;
+ unsigned int id, found_volume = 0;
+ struct mps_command *cm;
+ Mpi2RaidActionRequest_t *action;
+
+ mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+
+ /* is IR firmware build loaded? */
+ if (!sc->ir_firmware)
+ return;
+
+ /* are there any volumes? Look at IR target IDs. */
+ // TODO-later, this should be looked up in the RAID config structure
+ // when it is implemented.
+ volume_mapping_flags = le16toh(sc->ioc_pg8.IRVolumeMappingFlags) &
+ MPI2_IOCPAGE8_IRFLAGS_MASK_VOLUME_MAPPING_MODE;
+ if (volume_mapping_flags == MPI2_IOCPAGE8_IRFLAGS_LOW_VOLUME_MAPPING) {
+ start_idx = 0;
+ if (ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_RESERVED_TARGETID_0)
+ start_idx = 1;
+ } else
+ start_idx = sc->max_devices - sc->max_volumes;
+ end_idx = start_idx + sc->max_volumes - 1;
+
+ for (id = start_idx; id < end_idx; id++) {
+ mt_entry = &sc->mapping_table[id];
+ if ((mt_entry->physical_id != 0) &&
+ (mt_entry->missing_count == 0)) {
+ found_volume = 1;
+ break;
+ }
+ }
+
+ if (!found_volume)
+ return;
+
+ if ((cm = mps_alloc_command(sc)) == NULL) {
+ printf("%s: command alloc failed\n", __func__);
+ return;
+ }
+
+ action = (MPI2_RAID_ACTION_REQUEST *)cm->cm_req;
+ action->Function = MPI2_FUNCTION_RAID_ACTION;
+ action->Action = MPI2_RAID_ACTION_SYSTEM_SHUTDOWN_INITIATED;
+ cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+ mps_request_polled(sc, cm);
+
+ /*
+ * Don't check for reply, just leave.
+ */
+ if (cm)
+ mps_free_command(sc, cm);
+}
diff --git a/sys/dev/mps/mps_table.c b/sys/dev/mps/mps_table.c
index 55110a1..c9acefe 100644
--- a/sys/dev/mps/mps_table.c
+++ b/sys/dev/mps/mps_table.c
@@ -29,6 +29,7 @@ __FBSDID("$FreeBSD$");
/* Debugging tables for MPT2 */
+/* TODO Move headers to mpsvar */
#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
@@ -41,6 +42,9 @@ __FBSDID("$FreeBSD$");
#include <sys/malloc.h>
#include <sys/uio.h>
#include <sys/sysctl.h>
+#include <sys/queue.h>
+#include <sys/kthread.h>
+#include <sys/taskqueue.h>
#include <machine/bus.h>
#include <machine/resource.h>
@@ -53,6 +57,8 @@ __FBSDID("$FreeBSD$");
#include <dev/mps/mpi/mpi2_ioc.h>
#include <dev/mps/mpi/mpi2_cnfg.h>
#include <dev/mps/mpi/mpi2_init.h>
+#include <dev/mps/mpi/mpi2_tool.h>
+#include <dev/mps/mps_ioctl.h>
#include <dev/mps/mpsvar.h>
#include <dev/mps/mps_table.h>
diff --git a/sys/dev/mps/mps_user.c b/sys/dev/mps/mps_user.c
index 7ca90c1..75bb7ad 100644
--- a/sys/dev/mps/mps_user.c
+++ b/sys/dev/mps/mps_user.c
@@ -27,7 +27,36 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * LSI MPS-Fusion Host Adapter FreeBSD userland interface
+ * LSI MPT-Fusion Host Adapter FreeBSD userland interface
+ */
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ *
+ * $FreeBSD$
*/
#include <sys/cdefs.h>
@@ -35,6 +64,7 @@ __FBSDID("$FreeBSD$");
#include "opt_compat.h"
+/* TODO Move headers to mpsvar */
#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
@@ -49,6 +79,9 @@ __FBSDID("$FreeBSD$");
#include <sys/sysctl.h>
#include <sys/ioccom.h>
#include <sys/endian.h>
+#include <sys/queue.h>
+#include <sys/kthread.h>
+#include <sys/taskqueue.h>
#include <sys/proc.h>
#include <sys/sysent.h>
@@ -56,15 +89,21 @@ __FBSDID("$FreeBSD$");
#include <machine/resource.h>
#include <sys/rman.h>
+#include <cam/cam.h>
#include <cam/scsi/scsi_all.h>
#include <dev/mps/mpi/mpi2_type.h>
#include <dev/mps/mpi/mpi2.h>
#include <dev/mps/mpi/mpi2_ioc.h>
#include <dev/mps/mpi/mpi2_cnfg.h>
+#include <dev/mps/mpi/mpi2_init.h>
+#include <dev/mps/mpi/mpi2_tool.h>
+#include <dev/mps/mps_ioctl.h>
#include <dev/mps/mpsvar.h>
#include <dev/mps/mps_table.h>
-#include <dev/mps/mps_ioctl.h>
+#include <dev/mps/mps_sas.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
static d_open_t mps_open;
static d_close_t mps_close;
@@ -103,8 +142,52 @@ static int mps_user_setup_request(struct mps_command *,
struct mps_usr_command *);
static int mps_user_command(struct mps_softc *, struct mps_usr_command *);
+static int mps_user_pass_thru(struct mps_softc *sc, mps_pass_thru_t *data);
+static void mps_user_get_adapter_data(struct mps_softc *sc,
+ mps_adapter_data_t *data);
+static void mps_user_read_pci_info(struct mps_softc *sc,
+ mps_pci_info_t *data);
+static uint8_t mps_get_fw_diag_buffer_number(struct mps_softc *sc,
+ uint32_t unique_id);
+static int mps_post_fw_diag_buffer(struct mps_softc *sc,
+ mps_fw_diagnostic_buffer_t *pBuffer, uint32_t *return_code);
+static int mps_release_fw_diag_buffer(struct mps_softc *sc,
+ mps_fw_diagnostic_buffer_t *pBuffer, uint32_t *return_code,
+ uint32_t diag_type);
+static int mps_diag_register(struct mps_softc *sc,
+ mps_fw_diag_register_t *diag_register, uint32_t *return_code);
+static int mps_diag_unregister(struct mps_softc *sc,
+ mps_fw_diag_unregister_t *diag_unregister, uint32_t *return_code);
+static int mps_diag_query(struct mps_softc *sc, mps_fw_diag_query_t *diag_query,
+ uint32_t *return_code);
+static int mps_diag_read_buffer(struct mps_softc *sc,
+ mps_diag_read_buffer_t *diag_read_buffer, uint8_t *ioctl_buf,
+ uint32_t *return_code);
+static int mps_diag_release(struct mps_softc *sc,
+ mps_fw_diag_release_t *diag_release, uint32_t *return_code);
+static int mps_do_diag_action(struct mps_softc *sc, uint32_t action,
+ uint8_t *diag_action, uint32_t length, uint32_t *return_code);
+static int mps_user_diag_action(struct mps_softc *sc, mps_diag_action_t *data);
+static void mps_user_event_query(struct mps_softc *sc, mps_event_query_t *data);
+static void mps_user_event_enable(struct mps_softc *sc,
+ mps_event_enable_t *data);
+static int mps_user_event_report(struct mps_softc *sc,
+ mps_event_report_t *data);
+static int mps_user_reg_access(struct mps_softc *sc, mps_reg_access_t *data);
+static int mps_user_btdh(struct mps_softc *sc, mps_btdh_mapping_t *data);
+
static MALLOC_DEFINE(M_MPSUSER, "mps_user", "Buffers for mps(4) ioctls");
+/* Macros from compat/freebsd32/freebsd32.h */
+#define PTRIN(v) (void *)(uintptr_t)(v)
+#define PTROUT(v) (uint32_t)(uintptr_t)(v)
+
+#define CP(src,dst,fld) do { (dst).fld = (src).fld; } while (0)
+#define PTRIN_CP(src,dst,fld) \
+ do { (dst).fld = PTRIN((src).fld); } while (0)
+#define PTROUT_CP(src,dst,fld) \
+ do { (dst).fld = PTROUT((src).fld); } while (0)
+
int
mps_attach_user(struct mps_softc *sc)
{
@@ -625,18 +708,17 @@ mps_user_command(struct mps_softc *sc, struct mps_usr_command *cmd)
cm->cm_length = 0;
}
- cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE | MPS_CM_FLAGS_WAKEUP;
+ cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE;
cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
mps_lock(sc);
- err = mps_map_command(sc, cm);
+ err = mps_wait_command(sc, cm, 0);
- if (err != 0 && err != EINPROGRESS) {
+ if (err) {
mps_printf(sc, "%s: invalid request: error %d\n",
__func__, err);
goto Ret;
}
- msleep(cm, &sc->mps_mtx, 0, "mpsuser", 0);
rpl = (MPI2_DEFAULT_REPLY *)cm->cm_reply;
sz = rpl->MsgLength * 4;
@@ -664,7 +746,1302 @@ Ret:
if (buf != NULL)
free(buf, M_MPSUSER);
return (err);
-}
+}
+
+static int
+mps_user_pass_thru(struct mps_softc *sc, mps_pass_thru_t *data)
+{
+ MPI2_REQUEST_HEADER *hdr, tmphdr;
+ MPI2_DEFAULT_REPLY *rpl;
+ struct mps_command *cm = NULL;
+ int err = 0, dir = 0, sz;
+ uint8_t function = 0;
+ u_int sense_len;
+
+ /*
+ * Only allow one passthru command at a time. Use the MPS_FLAGS_BUSY
+ * bit to denote that a passthru is being processed.
+ */
+ mps_lock(sc);
+ if (sc->mps_flags & MPS_FLAGS_BUSY) {
+ mps_dprint(sc, MPS_INFO, "%s: Only one passthru command "
+ "allowed at a single time.", __func__);
+ mps_unlock(sc);
+ return (EBUSY);
+ }
+ sc->mps_flags |= MPS_FLAGS_BUSY;
+ mps_unlock(sc);
+
+ /*
+ * Do some validation on data direction. Valid cases are:
+ * 1) DataSize is 0 and direction is NONE
+ * 2) DataSize is non-zero and one of:
+ * a) direction is READ or
+ * b) direction is WRITE or
+ * c) direction is BOTH and DataOutSize is non-zero
+ * If valid and the direction is BOTH, change the direction to READ.
+ * if valid and the direction is not BOTH, make sure DataOutSize is 0.
+ */
+ if (((data->DataSize == 0) &&
+ (data->DataDirection == MPS_PASS_THRU_DIRECTION_NONE)) ||
+ ((data->DataSize != 0) &&
+ ((data->DataDirection == MPS_PASS_THRU_DIRECTION_READ) ||
+ (data->DataDirection == MPS_PASS_THRU_DIRECTION_WRITE) ||
+ ((data->DataDirection == MPS_PASS_THRU_DIRECTION_BOTH) &&
+ (data->DataOutSize != 0))))) {
+ if (data->DataDirection == MPS_PASS_THRU_DIRECTION_BOTH)
+ data->DataDirection = MPS_PASS_THRU_DIRECTION_READ;
+ else
+ data->DataOutSize = 0;
+ } else
+ return (EINVAL);
+
+ mps_dprint(sc, MPS_INFO, "%s: req 0x%jx %d rpl 0x%jx %d "
+ "data in 0x%jx %d data out 0x%jx %d data dir %d\n", __func__,
+ data->PtrRequest, data->RequestSize, data->PtrReply,
+ data->ReplySize, data->PtrData, data->DataSize,
+ data->PtrDataOut, data->DataOutSize, data->DataDirection);
+
+ /*
+ * copy in the header so we know what we're dealing with before we
+ * commit to allocating a command for it.
+ */
+ err = copyin(PTRIN(data->PtrRequest), &tmphdr, data->RequestSize);
+ if (err != 0)
+ goto RetFreeUnlocked;
+
+ if (data->RequestSize > (int)sc->facts->IOCRequestFrameSize * 4) {
+ err = EINVAL;
+ goto RetFreeUnlocked;
+ }
+
+ function = tmphdr.Function;
+ mps_dprint(sc, MPS_INFO, "%s: Function %02X MsgFlags %02X\n", __func__,
+ function, tmphdr.MsgFlags);
+
+ /*
+ * Handle a passthru TM request.
+ */
+ if (function == MPI2_FUNCTION_SCSI_TASK_MGMT) {
+ MPI2_SCSI_TASK_MANAGE_REQUEST *task;
+
+ mps_lock(sc);
+ cm = mpssas_alloc_tm(sc);
+ if (cm == NULL) {
+ err = EINVAL;
+ goto Ret;
+ }
+
+ /* Copy the header in. Only a small fixup is needed. */
+ task = (MPI2_SCSI_TASK_MANAGE_REQUEST *)cm->cm_req;
+ bcopy(&tmphdr, task, data->RequestSize);
+ task->TaskMID = cm->cm_desc.Default.SMID;
+
+ cm->cm_data = NULL;
+ cm->cm_desc.HighPriority.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY;
+ cm->cm_complete = NULL;
+ cm->cm_complete_data = NULL;
+
+ err = mps_wait_command(sc, cm, 0);
+
+ if (err != 0) {
+ err = EIO;
+ mps_dprint(sc, MPS_FAULT, "%s: task management failed",
+ __func__);
+ }
+ /*
+ * Copy the reply data and sense data to user space.
+ */
+ if (cm->cm_reply != NULL) {
+ rpl = (MPI2_DEFAULT_REPLY *)cm->cm_reply;
+ sz = rpl->MsgLength * 4;
+
+ if (sz > data->ReplySize) {
+ mps_printf(sc, "%s: reply buffer too small: %d, "
+ "required: %d\n", __func__, data->ReplySize, sz);
+ err = EINVAL;
+ } else {
+ mps_unlock(sc);
+ copyout(cm->cm_reply, PTRIN(data->PtrReply),
+ data->ReplySize);
+ mps_lock(sc);
+ }
+ }
+ mpssas_free_tm(sc, cm);
+ goto Ret;
+ }
+
+ mps_lock(sc);
+ cm = mps_alloc_command(sc);
+
+ if (cm == NULL) {
+ mps_printf(sc, "%s: no mps requests\n", __func__);
+ err = ENOMEM;
+ goto Ret;
+ }
+ mps_unlock(sc);
+
+ hdr = (MPI2_REQUEST_HEADER *)cm->cm_req;
+ bcopy(&tmphdr, hdr, data->RequestSize);
+
+ /*
+ * Do some checking to make sure the IOCTL request contains a valid
+ * request. Then set the SGL info.
+ */
+ mpi_init_sge(cm, hdr, (void *)((uint8_t *)hdr + data->RequestSize));
+
+ /*
+ * Set up for read, write or both. From check above, DataOutSize will
+ * be 0 if direction is READ or WRITE, but it will have some non-zero
+ * value if the direction is BOTH. So, just use the biggest size to get
+ * the cm_data buffer size. If direction is BOTH, 2 SGLs need to be set
+ * up; the first is for the request and the second will contain the
+ * response data. cm_out_len needs to be set here and this will be used
+ * when the SGLs are set up.
+ */
+ cm->cm_data = NULL;
+ cm->cm_length = MAX(data->DataSize, data->DataOutSize);
+ cm->cm_out_len = data->DataOutSize;
+ cm->cm_flags = 0;
+ if (cm->cm_length != 0) {
+ cm->cm_data = malloc(cm->cm_length, M_MPSUSER, M_WAITOK |
+ M_ZERO);
+ if (cm->cm_data == NULL) {
+ mps_dprint(sc, MPS_FAULT, "%s: alloc failed for IOCTL "
+ "passthru length %d\n", __func__, cm->cm_length);
+ } else {
+ cm->cm_flags = MPS_CM_FLAGS_DATAIN;
+ if (data->DataOutSize) {
+ cm->cm_flags |= MPS_CM_FLAGS_DATAOUT;
+ err = copyin(PTRIN(data->PtrDataOut),
+ cm->cm_data, data->DataOutSize);
+ } else if (data->DataDirection ==
+ MPS_PASS_THRU_DIRECTION_WRITE) {
+ cm->cm_flags = MPS_CM_FLAGS_DATAOUT;
+ err = copyin(PTRIN(data->PtrData),
+ cm->cm_data, data->DataSize);
+ }
+ if (err != 0)
+ mps_dprint(sc, MPS_FAULT, "%s: failed to copy "
+ "IOCTL data from user space\n", __func__);
+ }
+ }
+ cm->cm_flags |= MPS_CM_FLAGS_SGE_SIMPLE;
+ cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+
+ /*
+ * Set up Sense buffer and SGL offset for IO passthru. SCSI IO request
+ * uses SCSI IO descriptor.
+ */
+ if ((function == MPI2_FUNCTION_SCSI_IO_REQUEST) ||
+ (function == MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH)) {
+ MPI2_SCSI_IO_REQUEST *scsi_io_req;
+
+ scsi_io_req = (MPI2_SCSI_IO_REQUEST *)hdr;
+ /*
+ * Put SGE for data and data_out buffer at the end of
+ * scsi_io_request message header (64 bytes in total).
+ * Following above SGEs, the residual space will be used by
+ * sense data.
+ */
+ scsi_io_req->SenseBufferLength = (uint8_t)(data->RequestSize -
+ 64);
+ scsi_io_req->SenseBufferLowAddress = cm->cm_sense_busaddr;
+
+ /*
+ * Set SGLOffset0 value. This is the number of dwords that SGL
+ * is offset from the beginning of MPI2_SCSI_IO_REQUEST struct.
+ */
+ scsi_io_req->SGLOffset0 = 24;
+
+ /*
+ * Setup descriptor info. RAID passthrough must use the
+ * default request descriptor which is already set, so if this
+ * is a SCSI IO request, change the descriptor to SCSI IO.
+ * Also, if this is a SCSI IO request, handle the reply in the
+ * mpssas_scsio_complete function.
+ */
+ if (function == MPI2_FUNCTION_SCSI_IO_REQUEST) {
+ cm->cm_desc.SCSIIO.RequestFlags =
+ MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO;
+ cm->cm_desc.SCSIIO.DevHandle = scsi_io_req->DevHandle;
+
+ /*
+ * Make sure the DevHandle is not 0 because this is a
+ * likely error.
+ */
+ if (scsi_io_req->DevHandle == 0) {
+ err = EINVAL;
+ goto RetFreeUnlocked;
+ }
+ }
+ }
+
+ mps_lock(sc);
+
+ err = mps_wait_command(sc, cm, 0);
+
+ if (err) {
+ mps_printf(sc, "%s: invalid request: error %d\n", __func__,
+ err);
+ mps_unlock(sc);
+ goto RetFreeUnlocked;
+ }
+
+ /*
+ * Sync the DMA data, if any. Then copy the data to user space.
+ */
+ if (cm->cm_data != NULL) {
+ if (cm->cm_flags & MPS_CM_FLAGS_DATAIN)
+ dir = BUS_DMASYNC_POSTREAD;
+ else if (cm->cm_flags & MPS_CM_FLAGS_DATAOUT)
+ dir = BUS_DMASYNC_POSTWRITE;;
+ bus_dmamap_sync(sc->buffer_dmat, cm->cm_dmamap, dir);
+ bus_dmamap_unload(sc->buffer_dmat, cm->cm_dmamap);
+
+ if (cm->cm_flags & MPS_CM_FLAGS_DATAIN) {
+ mps_unlock(sc);
+ err = copyout(cm->cm_data,
+ PTRIN(data->PtrData), data->DataSize);
+ mps_lock(sc);
+ if (err != 0)
+ mps_dprint(sc, MPS_FAULT, "%s: failed to copy "
+ "IOCTL data to user space\n", __func__);
+ }
+ }
+
+ /*
+ * Copy the reply data and sense data to user space.
+ */
+ if (cm->cm_reply != NULL) {
+ rpl = (MPI2_DEFAULT_REPLY *)cm->cm_reply;
+ sz = rpl->MsgLength * 4;
+
+ if (sz > data->ReplySize) {
+ mps_printf(sc, "%s: reply buffer too small: %d, "
+ "required: %d\n", __func__, data->ReplySize, sz);
+ err = EINVAL;
+ } else {
+ mps_unlock(sc);
+ copyout(cm->cm_reply, PTRIN(data->PtrReply),
+ data->ReplySize);
+ mps_lock(sc);
+ }
+
+ if ((function == MPI2_FUNCTION_SCSI_IO_REQUEST) ||
+ (function == MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH)) {
+ if (((MPI2_SCSI_IO_REPLY *)rpl)->SCSIState &
+ MPI2_SCSI_STATE_AUTOSENSE_VALID) {
+ sense_len =
+ MIN(((MPI2_SCSI_IO_REPLY *)rpl)->SenseCount,
+ sizeof(struct scsi_sense_data));
+ mps_unlock(sc);
+ copyout(cm->cm_sense, cm->cm_req + 64, sense_len);
+ mps_lock(sc);
+ }
+ }
+ }
+ mps_unlock(sc);
+
+RetFreeUnlocked:
+ mps_lock(sc);
+
+ if (cm != NULL) {
+ if (cm->cm_data)
+ free(cm->cm_data, M_MPSUSER);
+ mps_free_command(sc, cm);
+ }
+Ret:
+ sc->mps_flags &= ~MPS_FLAGS_BUSY;
+ mps_unlock(sc);
+
+ return (err);
+}
+
+static void
+mps_user_get_adapter_data(struct mps_softc *sc, mps_adapter_data_t *data)
+{
+ Mpi2ConfigReply_t mpi_reply;
+ Mpi2BiosPage3_t config_page;
+
+ /*
+ * Use the PCI interface functions to get the Bus, Device, and Function
+ * information.
+ */
+ data->PciInformation.u.bits.BusNumber = pci_get_bus(sc->mps_dev);
+ data->PciInformation.u.bits.DeviceNumber = pci_get_slot(sc->mps_dev);
+ data->PciInformation.u.bits.FunctionNumber =
+ pci_get_function(sc->mps_dev);
+
+ /*
+ * Get the FW version that should already be saved in IOC Facts.
+ */
+ data->MpiFirmwareVersion = sc->facts->FWVersion.Word;
+
+ /*
+ * General device info.
+ */
+ data->AdapterType = MPSIOCTL_ADAPTER_TYPE_SAS2;
+ if (sc->mps_flags & MPS_FLAGS_WD_AVAILABLE)
+ data->AdapterType = MPSIOCTL_ADAPTER_TYPE_SAS2_SSS6200;
+ data->PCIDeviceHwId = pci_get_device(sc->mps_dev);
+ data->PCIDeviceHwRev = pci_read_config(sc->mps_dev, PCIR_REVID, 1);
+ data->SubSystemId = pci_get_subdevice(sc->mps_dev);
+ data->SubsystemVendorId = pci_get_subvendor(sc->mps_dev);
+
+ /*
+ * Get the driver version.
+ */
+ strcpy((char *)&data->DriverVersion[0], MPS_DRIVER_VERSION);
+
+ /*
+ * Need to get BIOS Config Page 3 for the BIOS Version.
+ */
+ data->BiosVersion = 0;
+ if (mps_config_get_bios_pg3(sc, &mpi_reply, &config_page))
+ printf("%s: Error while retrieving BIOS Version\n", __func__);
+ else
+ data->BiosVersion = config_page.BiosVersion;
+}
+
+static void
+mps_user_read_pci_info(struct mps_softc *sc, mps_pci_info_t *data)
+{
+ int i;
+
+ /*
+ * Use the PCI interface functions to get the Bus, Device, and Function
+ * information.
+ */
+ data->BusNumber = pci_get_bus(sc->mps_dev);
+ data->DeviceNumber = pci_get_slot(sc->mps_dev);
+ data->FunctionNumber = pci_get_function(sc->mps_dev);
+
+ /*
+ * Now get the interrupt vector and the pci header. The vector can
+ * only be 0 right now. The header is the first 256 bytes of config
+ * space.
+ */
+ data->InterruptVector = 0;
+ for (i = 0; i < sizeof (data->PciHeader); i++) {
+ data->PciHeader[i] = pci_read_config(sc->mps_dev, i, 1);
+ }
+}
+
+static uint8_t
+mps_get_fw_diag_buffer_number(struct mps_softc *sc, uint32_t unique_id)
+{
+ uint8_t index;
+
+ for (index = 0; index < MPI2_DIAG_BUF_TYPE_COUNT; index++) {
+ if (sc->fw_diag_buffer_list[index].unique_id == unique_id) {
+ return (index);
+ }
+ }
+
+ return (MPS_FW_DIAGNOSTIC_UID_NOT_FOUND);
+}
+
+static int
+mps_post_fw_diag_buffer(struct mps_softc *sc,
+ mps_fw_diagnostic_buffer_t *pBuffer, uint32_t *return_code)
+{
+ MPI2_DIAG_BUFFER_POST_REQUEST *req;
+ MPI2_DIAG_BUFFER_POST_REPLY *reply;
+ struct mps_command *cm = NULL;
+ int i, status;
+
+ /*
+ * If buffer is not enabled, just leave.
+ */
+ *return_code = MPS_FW_DIAG_ERROR_POST_FAILED;
+ if (!pBuffer->enabled) {
+ return (MPS_DIAG_FAILURE);
+ }
+
+ /*
+ * Clear some flags initially.
+ */
+ pBuffer->force_release = FALSE;
+ pBuffer->valid_data = FALSE;
+ pBuffer->owned_by_firmware = FALSE;
+
+ /*
+ * Get a command.
+ */
+ cm = mps_alloc_command(sc);
+ if (cm == NULL) {
+ mps_printf(sc, "%s: no mps requests\n", __func__);
+ return (MPS_DIAG_FAILURE);
+ }
+
+ /*
+ * Build the request for releasing the FW Diag Buffer and send it.
+ */
+ req = (MPI2_DIAG_BUFFER_POST_REQUEST *)cm->cm_req;
+ req->Function = MPI2_FUNCTION_DIAG_BUFFER_POST;
+ req->BufferType = pBuffer->buffer_type;
+ req->ExtendedType = pBuffer->extended_type;
+ req->BufferLength = pBuffer->size;
+ for (i = 0; i < (sizeof(req->ProductSpecific) / 4); i++)
+ req->ProductSpecific[i] = pBuffer->product_specific[i];
+ mps_from_u64(sc->fw_diag_busaddr, &req->BufferAddress);
+ cm->cm_data = NULL;
+ cm->cm_length = 0;
+ cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+ cm->cm_complete_data = NULL;
+
+ /*
+ * Send command synchronously.
+ */
+ status = mps_wait_command(sc, cm, 0);
+ if (status) {
+ mps_printf(sc, "%s: invalid request: error %d\n", __func__,
+ status);
+ status = MPS_DIAG_FAILURE;
+ goto done;
+ }
+
+ /*
+ * Process POST reply.
+ */
+ reply = (MPI2_DIAG_BUFFER_POST_REPLY *)cm->cm_reply;
+ if (reply->IOCStatus != MPI2_IOCSTATUS_SUCCESS) {
+ status = MPS_DIAG_FAILURE;
+ mps_dprint(sc, MPS_FAULT, "%s: post of FW Diag Buffer failed "
+ "with IOCStatus = 0x%x, IOCLogInfo = 0x%x and "
+ "TransferLength = 0x%x\n", __func__, reply->IOCStatus,
+ reply->IOCLogInfo, reply->TransferLength);
+ goto done;
+ }
+
+ /*
+ * Post was successful.
+ */
+ pBuffer->valid_data = TRUE;
+ pBuffer->owned_by_firmware = TRUE;
+ *return_code = MPS_FW_DIAG_ERROR_SUCCESS;
+ status = MPS_DIAG_SUCCESS;
+
+done:
+ mps_free_command(sc, cm);
+ return (status);
+}
+
+static int
+mps_release_fw_diag_buffer(struct mps_softc *sc,
+ mps_fw_diagnostic_buffer_t *pBuffer, uint32_t *return_code,
+ uint32_t diag_type)
+{
+ MPI2_DIAG_RELEASE_REQUEST *req;
+ MPI2_DIAG_RELEASE_REPLY *reply;
+ struct mps_command *cm = NULL;
+ int status;
+
+ /*
+ * If buffer is not enabled, just leave.
+ */
+ *return_code = MPS_FW_DIAG_ERROR_RELEASE_FAILED;
+ if (!pBuffer->enabled) {
+ mps_dprint(sc, MPS_INFO, "%s: This buffer type is not supported "
+ "by the IOC", __func__);
+ return (MPS_DIAG_FAILURE);
+ }
+
+ /*
+ * Clear some flags initially.
+ */
+ pBuffer->force_release = FALSE;
+ pBuffer->valid_data = FALSE;
+ pBuffer->owned_by_firmware = FALSE;
+
+ /*
+ * Get a command.
+ */
+ cm = mps_alloc_command(sc);
+ if (cm == NULL) {
+ mps_printf(sc, "%s: no mps requests\n", __func__);
+ return (MPS_DIAG_FAILURE);
+ }
+
+ /*
+ * Build the request for releasing the FW Diag Buffer and send it.
+ */
+ req = (MPI2_DIAG_RELEASE_REQUEST *)cm->cm_req;
+ req->Function = MPI2_FUNCTION_DIAG_RELEASE;
+ req->BufferType = pBuffer->buffer_type;
+ cm->cm_data = NULL;
+ cm->cm_length = 0;
+ cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+ cm->cm_complete_data = NULL;
+
+ /*
+ * Send command synchronously.
+ */
+ status = mps_wait_command(sc, cm, 0);
+ if (status) {
+ mps_printf(sc, "%s: invalid request: error %d\n", __func__,
+ status);
+ status = MPS_DIAG_FAILURE;
+ goto done;
+ }
+
+ /*
+ * Process RELEASE reply.
+ */
+ reply = (MPI2_DIAG_RELEASE_REPLY *)cm->cm_reply;
+ if ((reply->IOCStatus != MPI2_IOCSTATUS_SUCCESS) ||
+ pBuffer->owned_by_firmware) {
+ status = MPS_DIAG_FAILURE;
+ mps_dprint(sc, MPS_FAULT, "%s: release of FW Diag Buffer "
+ "failed with IOCStatus = 0x%x and IOCLogInfo = 0x%x\n",
+ __func__, reply->IOCStatus, reply->IOCLogInfo);
+ goto done;
+ }
+
+ /*
+ * Release was successful.
+ */
+ *return_code = MPS_FW_DIAG_ERROR_SUCCESS;
+ status = MPS_DIAG_SUCCESS;
+
+ /*
+ * If this was for an UNREGISTER diag type command, clear the unique ID.
+ */
+ if (diag_type == MPS_FW_DIAG_TYPE_UNREGISTER) {
+ pBuffer->unique_id = MPS_FW_DIAG_INVALID_UID;
+ }
+
+done:
+ return (status);
+}
+
+static int
+mps_diag_register(struct mps_softc *sc, mps_fw_diag_register_t *diag_register,
+ uint32_t *return_code)
+{
+ mps_fw_diagnostic_buffer_t *pBuffer;
+ uint8_t extended_type, buffer_type, i;
+ uint32_t buffer_size;
+ uint32_t unique_id;
+ int status;
+
+ extended_type = diag_register->ExtendedType;
+ buffer_type = diag_register->BufferType;
+ buffer_size = diag_register->RequestedBufferSize;
+ unique_id = diag_register->UniqueId;
+
+ /*
+ * Check for valid buffer type
+ */
+ if (buffer_type >= MPI2_DIAG_BUF_TYPE_COUNT) {
+ *return_code = MPS_FW_DIAG_ERROR_INVALID_PARAMETER;
+ return (MPS_DIAG_FAILURE);
+ }
+
+ /*
+ * Get the current buffer and look up the unique ID. The unique ID
+ * should not be found. If it is, the ID is already in use.
+ */
+ i = mps_get_fw_diag_buffer_number(sc, unique_id);
+ pBuffer = &sc->fw_diag_buffer_list[buffer_type];
+ if (i != MPS_FW_DIAGNOSTIC_UID_NOT_FOUND) {
+ *return_code = MPS_FW_DIAG_ERROR_INVALID_UID;
+ return (MPS_DIAG_FAILURE);
+ }
+
+ /*
+ * The buffer's unique ID should not be registered yet, and the given
+ * unique ID cannot be 0.
+ */
+ if ((pBuffer->unique_id != MPS_FW_DIAG_INVALID_UID) ||
+ (unique_id == MPS_FW_DIAG_INVALID_UID)) {
+ *return_code = MPS_FW_DIAG_ERROR_INVALID_UID;
+ return (MPS_DIAG_FAILURE);
+ }
+
+ /*
+ * If this buffer is already posted as immediate, just change owner.
+ */
+ if (pBuffer->immediate && pBuffer->owned_by_firmware &&
+ (pBuffer->unique_id == MPS_FW_DIAG_INVALID_UID)) {
+ pBuffer->immediate = FALSE;
+ pBuffer->unique_id = unique_id;
+ return (MPS_DIAG_SUCCESS);
+ }
+
+ /*
+ * Post a new buffer after checking if it's enabled. The DMA buffer
+ * that is allocated will be contiguous (nsegments = 1).
+ */
+ if (!pBuffer->enabled) {
+ *return_code = MPS_FW_DIAG_ERROR_NO_BUFFER;
+ return (MPS_DIAG_FAILURE);
+ }
+ if (bus_dma_tag_create( sc->mps_parent_dmat, /* parent */
+ 1, 0, /* algnmnt, boundary */
+ BUS_SPACE_MAXADDR_32BIT,/* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ buffer_size, /* maxsize */
+ 1, /* nsegments */
+ buffer_size, /* maxsegsize */
+ 0, /* flags */
+ NULL, NULL, /* lockfunc, lockarg */
+ &sc->fw_diag_dmat)) {
+ device_printf(sc->mps_dev, "Cannot allocate FW diag buffer DMA "
+ "tag\n");
+ return (ENOMEM);
+ }
+ if (bus_dmamem_alloc(sc->fw_diag_dmat, (void **)&sc->fw_diag_buffer,
+ BUS_DMA_NOWAIT, &sc->fw_diag_map)) {
+ device_printf(sc->mps_dev, "Cannot allocate FW diag buffer "
+ "memory\n");
+ return (ENOMEM);
+ }
+ bzero(sc->fw_diag_buffer, buffer_size);
+ bus_dmamap_load(sc->fw_diag_dmat, sc->fw_diag_map, sc->fw_diag_buffer,
+ buffer_size, mps_memaddr_cb, &sc->fw_diag_busaddr, 0);
+ pBuffer->size = buffer_size;
+
+ /*
+ * Copy the given info to the diag buffer and post the buffer.
+ */
+ pBuffer->buffer_type = buffer_type;
+ pBuffer->immediate = FALSE;
+ if (buffer_type == MPI2_DIAG_BUF_TYPE_TRACE) {
+ for (i = 0; i < (sizeof (pBuffer->product_specific) / 4);
+ i++) {
+ pBuffer->product_specific[i] =
+ diag_register->ProductSpecific[i];
+ }
+ }
+ pBuffer->extended_type = extended_type;
+ pBuffer->unique_id = unique_id;
+ status = mps_post_fw_diag_buffer(sc, pBuffer, return_code);
+
+ /*
+ * In case there was a failure, free the DMA buffer.
+ */
+ if (status == MPS_DIAG_FAILURE) {
+ if (sc->fw_diag_busaddr != 0)
+ bus_dmamap_unload(sc->fw_diag_dmat, sc->fw_diag_map);
+ if (sc->fw_diag_buffer != NULL)
+ bus_dmamem_free(sc->fw_diag_dmat, sc->fw_diag_buffer,
+ sc->fw_diag_map);
+ if (sc->fw_diag_dmat != NULL)
+ bus_dma_tag_destroy(sc->fw_diag_dmat);
+ }
+
+ return (status);
+}
+
+static int
+mps_diag_unregister(struct mps_softc *sc,
+ mps_fw_diag_unregister_t *diag_unregister, uint32_t *return_code)
+{
+ mps_fw_diagnostic_buffer_t *pBuffer;
+ uint8_t i;
+ uint32_t unique_id;
+ int status;
+
+ unique_id = diag_unregister->UniqueId;
+
+ /*
+ * Get the current buffer and look up the unique ID. The unique ID
+ * should be there.
+ */
+ i = mps_get_fw_diag_buffer_number(sc, unique_id);
+ if (i == MPS_FW_DIAGNOSTIC_UID_NOT_FOUND) {
+ *return_code = MPS_FW_DIAG_ERROR_INVALID_UID;
+ return (MPS_DIAG_FAILURE);
+ }
+
+ pBuffer = &sc->fw_diag_buffer_list[i];
+
+ /*
+ * Try to release the buffer from FW before freeing it. If release
+ * fails, don't free the DMA buffer in case FW tries to access it
+ * later. If buffer is not owned by firmware, can't release it.
+ */
+ if (!pBuffer->owned_by_firmware) {
+ status = MPS_DIAG_SUCCESS;
+ } else {
+ status = mps_release_fw_diag_buffer(sc, pBuffer, return_code,
+ MPS_FW_DIAG_TYPE_UNREGISTER);
+ }
+
+ /*
+ * At this point, return the current status no matter what happens with
+ * the DMA buffer.
+ */
+ pBuffer->unique_id = MPS_FW_DIAG_INVALID_UID;
+ if (status == MPS_DIAG_SUCCESS) {
+ if (sc->fw_diag_busaddr != 0)
+ bus_dmamap_unload(sc->fw_diag_dmat, sc->fw_diag_map);
+ if (sc->fw_diag_buffer != NULL)
+ bus_dmamem_free(sc->fw_diag_dmat, sc->fw_diag_buffer,
+ sc->fw_diag_map);
+ if (sc->fw_diag_dmat != NULL)
+ bus_dma_tag_destroy(sc->fw_diag_dmat);
+ }
+
+ return (status);
+}
+
+static int
+mps_diag_query(struct mps_softc *sc, mps_fw_diag_query_t *diag_query,
+ uint32_t *return_code)
+{
+ mps_fw_diagnostic_buffer_t *pBuffer;
+ uint8_t i;
+ uint32_t unique_id;
+
+ unique_id = diag_query->UniqueId;
+
+ /*
+ * If ID is valid, query on ID.
+ * If ID is invalid, query on buffer type.
+ */
+ if (unique_id == MPS_FW_DIAG_INVALID_UID) {
+ i = diag_query->BufferType;
+ if (i >= MPI2_DIAG_BUF_TYPE_COUNT) {
+ *return_code = MPS_FW_DIAG_ERROR_INVALID_UID;
+ return (MPS_DIAG_FAILURE);
+ }
+ } else {
+ i = mps_get_fw_diag_buffer_number(sc, unique_id);
+ if (i == MPS_FW_DIAGNOSTIC_UID_NOT_FOUND) {
+ *return_code = MPS_FW_DIAG_ERROR_INVALID_UID;
+ return (MPS_DIAG_FAILURE);
+ }
+ }
+
+ /*
+ * Fill query structure with the diag buffer info.
+ */
+ pBuffer = &sc->fw_diag_buffer_list[i];
+ diag_query->BufferType = pBuffer->buffer_type;
+ diag_query->ExtendedType = pBuffer->extended_type;
+ if (diag_query->BufferType == MPI2_DIAG_BUF_TYPE_TRACE) {
+ for (i = 0; i < (sizeof(diag_query->ProductSpecific) / 4);
+ i++) {
+ diag_query->ProductSpecific[i] =
+ pBuffer->product_specific[i];
+ }
+ }
+ diag_query->TotalBufferSize = pBuffer->size;
+ diag_query->DriverAddedBufferSize = 0;
+ diag_query->UniqueId = pBuffer->unique_id;
+ diag_query->ApplicationFlags = 0;
+ diag_query->DiagnosticFlags = 0;
+
+ /*
+ * Set/Clear application flags
+ */
+ if (pBuffer->immediate) {
+ diag_query->ApplicationFlags &= ~MPS_FW_DIAG_FLAG_APP_OWNED;
+ } else {
+ diag_query->ApplicationFlags |= MPS_FW_DIAG_FLAG_APP_OWNED;
+ }
+ if (pBuffer->valid_data || pBuffer->owned_by_firmware) {
+ diag_query->ApplicationFlags |= MPS_FW_DIAG_FLAG_BUFFER_VALID;
+ } else {
+ diag_query->ApplicationFlags &= ~MPS_FW_DIAG_FLAG_BUFFER_VALID;
+ }
+ if (pBuffer->owned_by_firmware) {
+ diag_query->ApplicationFlags |=
+ MPS_FW_DIAG_FLAG_FW_BUFFER_ACCESS;
+ } else {
+ diag_query->ApplicationFlags &=
+ ~MPS_FW_DIAG_FLAG_FW_BUFFER_ACCESS;
+ }
+
+ return (MPS_DIAG_SUCCESS);
+}
+
+static int
+mps_diag_read_buffer(struct mps_softc *sc,
+ mps_diag_read_buffer_t *diag_read_buffer, uint8_t *ioctl_buf,
+ uint32_t *return_code)
+{
+ mps_fw_diagnostic_buffer_t *pBuffer;
+ uint8_t i, *pData;
+ uint32_t unique_id;
+ int status;
+
+ unique_id = diag_read_buffer->UniqueId;
+
+ /*
+ * Get the current buffer and look up the unique ID. The unique ID
+ * should be there.
+ */
+ i = mps_get_fw_diag_buffer_number(sc, unique_id);
+ if (i == MPS_FW_DIAGNOSTIC_UID_NOT_FOUND) {
+ *return_code = MPS_FW_DIAG_ERROR_INVALID_UID;
+ return (MPS_DIAG_FAILURE);
+ }
+
+ pBuffer = &sc->fw_diag_buffer_list[i];
+
+ /*
+ * Make sure requested read is within limits
+ */
+ if (diag_read_buffer->StartingOffset + diag_read_buffer->BytesToRead >
+ pBuffer->size) {
+ *return_code = MPS_FW_DIAG_ERROR_INVALID_PARAMETER;
+ return (MPS_DIAG_FAILURE);
+ }
+
+ /*
+ * Copy the requested data from DMA to the diag_read_buffer. The DMA
+ * buffer that was allocated is one contiguous buffer.
+ */
+ pData = (uint8_t *)(sc->fw_diag_buffer +
+ diag_read_buffer->StartingOffset);
+ if (copyout(pData, ioctl_buf, diag_read_buffer->BytesToRead) != 0)
+ return (MPS_DIAG_FAILURE);
+ diag_read_buffer->Status = 0;
+
+ /*
+ * Set or clear the Force Release flag.
+ */
+ if (pBuffer->force_release) {
+ diag_read_buffer->Flags |= MPS_FW_DIAG_FLAG_FORCE_RELEASE;
+ } else {
+ diag_read_buffer->Flags &= ~MPS_FW_DIAG_FLAG_FORCE_RELEASE;
+ }
+
+ /*
+ * If buffer is to be reregistered, make sure it's not already owned by
+ * firmware first.
+ */
+ status = MPS_DIAG_SUCCESS;
+ if (!pBuffer->owned_by_firmware) {
+ if (diag_read_buffer->Flags & MPS_FW_DIAG_FLAG_REREGISTER) {
+ status = mps_post_fw_diag_buffer(sc, pBuffer,
+ return_code);
+ }
+ }
+
+ return (status);
+}
+
+static int
+mps_diag_release(struct mps_softc *sc, mps_fw_diag_release_t *diag_release,
+ uint32_t *return_code)
+{
+ mps_fw_diagnostic_buffer_t *pBuffer;
+ uint8_t i;
+ uint32_t unique_id;
+ int status;
+
+ unique_id = diag_release->UniqueId;
+
+ /*
+ * Get the current buffer and look up the unique ID. The unique ID
+ * should be there.
+ */
+ i = mps_get_fw_diag_buffer_number(sc, unique_id);
+ if (i == MPS_FW_DIAGNOSTIC_UID_NOT_FOUND) {
+ *return_code = MPS_FW_DIAG_ERROR_INVALID_UID;
+ return (MPS_DIAG_FAILURE);
+ }
+
+ pBuffer = &sc->fw_diag_buffer_list[i];
+
+ /*
+ * If buffer is not owned by firmware, it's already been released.
+ */
+ if (!pBuffer->owned_by_firmware) {
+ *return_code = MPS_FW_DIAG_ERROR_ALREADY_RELEASED;
+ return (MPS_DIAG_FAILURE);
+ }
+
+ /*
+ * Release the buffer.
+ */
+ status = mps_release_fw_diag_buffer(sc, pBuffer, return_code,
+ MPS_FW_DIAG_TYPE_RELEASE);
+ return (status);
+}
+
+static int
+mps_do_diag_action(struct mps_softc *sc, uint32_t action, uint8_t *diag_action,
+ uint32_t length, uint32_t *return_code)
+{
+ mps_fw_diag_register_t diag_register;
+ mps_fw_diag_unregister_t diag_unregister;
+ mps_fw_diag_query_t diag_query;
+ mps_diag_read_buffer_t diag_read_buffer;
+ mps_fw_diag_release_t diag_release;
+ int status = MPS_DIAG_SUCCESS;
+ uint32_t original_return_code;
+
+ original_return_code = *return_code;
+ *return_code = MPS_FW_DIAG_ERROR_SUCCESS;
+
+ switch (action) {
+ case MPS_FW_DIAG_TYPE_REGISTER:
+ if (!length) {
+ *return_code =
+ MPS_FW_DIAG_ERROR_INVALID_PARAMETER;
+ status = MPS_DIAG_FAILURE;
+ break;
+ }
+ if (copyin(diag_action, &diag_register,
+ sizeof(diag_register)) != 0)
+ return (MPS_DIAG_FAILURE);
+ status = mps_diag_register(sc, &diag_register,
+ return_code);
+ break;
+
+ case MPS_FW_DIAG_TYPE_UNREGISTER:
+ if (length < sizeof(diag_unregister)) {
+ *return_code =
+ MPS_FW_DIAG_ERROR_INVALID_PARAMETER;
+ status = MPS_DIAG_FAILURE;
+ break;
+ }
+ if (copyin(diag_action, &diag_unregister,
+ sizeof(diag_unregister)) != 0)
+ return (MPS_DIAG_FAILURE);
+ status = mps_diag_unregister(sc, &diag_unregister,
+ return_code);
+ break;
+
+ case MPS_FW_DIAG_TYPE_QUERY:
+ if (length < sizeof (diag_query)) {
+ *return_code =
+ MPS_FW_DIAG_ERROR_INVALID_PARAMETER;
+ status = MPS_DIAG_FAILURE;
+ break;
+ }
+ if (copyin(diag_action, &diag_query, sizeof(diag_query))
+ != 0)
+ return (MPS_DIAG_FAILURE);
+ status = mps_diag_query(sc, &diag_query, return_code);
+ if (status == MPS_DIAG_SUCCESS)
+ if (copyout(&diag_query, diag_action,
+ sizeof (diag_query)) != 0)
+ return (MPS_DIAG_FAILURE);
+ break;
+
+ case MPS_FW_DIAG_TYPE_READ_BUFFER:
+ if (copyin(diag_action, &diag_read_buffer,
+ sizeof(diag_read_buffer)) != 0)
+ return (MPS_DIAG_FAILURE);
+ if (length < diag_read_buffer.BytesToRead) {
+ *return_code =
+ MPS_FW_DIAG_ERROR_INVALID_PARAMETER;
+ status = MPS_DIAG_FAILURE;
+ break;
+ }
+ status = mps_diag_read_buffer(sc, &diag_read_buffer,
+ PTRIN(diag_read_buffer.PtrDataBuffer),
+ return_code);
+ if (status == MPS_DIAG_SUCCESS) {
+ if (copyout(&diag_read_buffer, diag_action,
+ sizeof(diag_read_buffer) -
+ sizeof(diag_read_buffer.PtrDataBuffer)) !=
+ 0)
+ return (MPS_DIAG_FAILURE);
+ }
+ break;
+
+ case MPS_FW_DIAG_TYPE_RELEASE:
+ if (length < sizeof(diag_release)) {
+ *return_code =
+ MPS_FW_DIAG_ERROR_INVALID_PARAMETER;
+ status = MPS_DIAG_FAILURE;
+ break;
+ }
+ if (copyin(diag_action, &diag_release,
+ sizeof(diag_release)) != 0)
+ return (MPS_DIAG_FAILURE);
+ status = mps_diag_release(sc, &diag_release,
+ return_code);
+ break;
+
+ default:
+ *return_code = MPS_FW_DIAG_ERROR_INVALID_PARAMETER;
+ status = MPS_DIAG_FAILURE;
+ break;
+ }
+
+ if ((status == MPS_DIAG_FAILURE) &&
+ (original_return_code == MPS_FW_DIAG_NEW) &&
+ (*return_code != MPS_FW_DIAG_ERROR_SUCCESS))
+ status = MPS_DIAG_SUCCESS;
+
+ return (status);
+}
+
+static int
+mps_user_diag_action(struct mps_softc *sc, mps_diag_action_t *data)
+{
+ int status;
+
+ /*
+ * Only allow one diag action at one time.
+ */
+ if (sc->mps_flags & MPS_FLAGS_BUSY) {
+ mps_dprint(sc, MPS_INFO, "%s: Only one FW diag command "
+ "allowed at a single time.", __func__);
+ return (EBUSY);
+ }
+ sc->mps_flags |= MPS_FLAGS_BUSY;
+
+ /*
+ * Send diag action request
+ */
+ if (data->Action == MPS_FW_DIAG_TYPE_REGISTER ||
+ data->Action == MPS_FW_DIAG_TYPE_UNREGISTER ||
+ data->Action == MPS_FW_DIAG_TYPE_QUERY ||
+ data->Action == MPS_FW_DIAG_TYPE_READ_BUFFER ||
+ data->Action == MPS_FW_DIAG_TYPE_RELEASE) {
+ status = mps_do_diag_action(sc, data->Action,
+ PTRIN(data->PtrDiagAction), data->Length,
+ &data->ReturnCode);
+ } else
+ status = EINVAL;
+
+ sc->mps_flags &= ~MPS_FLAGS_BUSY;
+ return (status);
+}
+
+/*
+ * Copy the event recording mask and the event queue size out. For
+ * clarification, the event recording mask (events_to_record) is not the same
+ * thing as the event mask (event_mask). events_to_record has a bit set for
+ * every event type that is to be recorded by the driver, and event_mask has a
+ * bit cleared for every event that is allowed into the driver from the IOC.
+ * They really have nothing to do with each other.
+ */
+static void
+mps_user_event_query(struct mps_softc *sc, mps_event_query_t *data)
+{
+ uint8_t i;
+
+ mps_lock(sc);
+ data->Entries = MPS_EVENT_QUEUE_SIZE;
+
+ for (i = 0; i < 4; i++) {
+ data->Types[i] = sc->events_to_record[i];
+ }
+ mps_unlock(sc);
+}
+
+/*
+ * Set the driver's event mask according to what's been given. See
+ * mps_user_event_query for explanation of the event recording mask and the IOC
+ * event mask. It's the app's responsibility to enable event logging by setting
+ * the bits in events_to_record. Initially, no events will be logged.
+ */
+static void
+mps_user_event_enable(struct mps_softc *sc, mps_event_enable_t *data)
+{
+ uint8_t i;
+
+ mps_lock(sc);
+ for (i = 0; i < 4; i++) {
+ sc->events_to_record[i] = data->Types[i];
+ }
+ mps_unlock(sc);
+}
+
+/*
+ * Copy out the events that have been recorded, up to the max events allowed.
+ */
+static int
+mps_user_event_report(struct mps_softc *sc, mps_event_report_t *data)
+{
+ int status = 0;
+ uint32_t size;
+
+ mps_lock(sc);
+ size = data->Size;
+ if ((size >= sizeof(sc->recorded_events)) && (status == 0)) {
+ mps_unlock(sc);
+ if (copyout((void *)sc->recorded_events,
+ PTRIN(data->PtrEvents), size) != 0)
+ status = EFAULT;
+ mps_lock(sc);
+ } else {
+ /*
+ * data->Size value is not large enough to copy event data.
+ */
+ status = EFAULT;
+ }
+
+ /*
+ * Change size value to match the number of bytes that were copied.
+ */
+ if (status == 0)
+ data->Size = sizeof(sc->recorded_events);
+ mps_unlock(sc);
+
+ return (status);
+}
+
+/*
+ * Record events into the driver from the IOC if they are not masked.
+ */
+void
+mpssas_record_event(struct mps_softc *sc,
+ MPI2_EVENT_NOTIFICATION_REPLY *event_reply)
+{
+ uint32_t event;
+ int i, j;
+ uint16_t event_data_len;
+ boolean_t sendAEN = FALSE;
+
+ event = event_reply->Event;
+
+ /*
+ * Generate a system event to let anyone who cares know that a
+ * LOG_ENTRY_ADDED event has occurred. This is sent no matter what the
+ * event mask is set to.
+ */
+ if (event == MPI2_EVENT_LOG_ENTRY_ADDED) {
+ sendAEN = TRUE;
+ }
+
+ /*
+ * Record the event only if its corresponding bit is set in
+ * events_to_record. event_index is the index into recorded_events and
+ * event_number is the overall number of an event being recorded since
+ * start-of-day. event_index will roll over; event_number will never
+ * roll over.
+ */
+ i = (uint8_t)(event / 32);
+ j = (uint8_t)(event % 32);
+ if ((i < 4) && ((1 << j) & sc->events_to_record[i])) {
+ i = sc->event_index;
+ sc->recorded_events[i].Type = event;
+ sc->recorded_events[i].Number = ++sc->event_number;
+ bzero(sc->recorded_events[i].Data, MPS_MAX_EVENT_DATA_LENGTH *
+ 4);
+ event_data_len = event_reply->EventDataLength;
+
+ if (event_data_len > 0) {
+ /*
+ * Limit data to size in m_event entry
+ */
+ if (event_data_len > MPS_MAX_EVENT_DATA_LENGTH) {
+ event_data_len = MPS_MAX_EVENT_DATA_LENGTH;
+ }
+ for (j = 0; j < event_data_len; j++) {
+ sc->recorded_events[i].Data[j] =
+ event_reply->EventData[j];
+ }
+
+ /*
+ * check for index wrap-around
+ */
+ if (++i == MPS_EVENT_QUEUE_SIZE) {
+ i = 0;
+ }
+ sc->event_index = (uint8_t)i;
+
+ /*
+ * Set flag to send the event.
+ */
+ sendAEN = TRUE;
+ }
+ }
+
+ /*
+ * Generate a system event if flag is set to let anyone who cares know
+ * that an event has occurred.
+ */
+ if (sendAEN) {
+//SLM-how to send a system event (see kqueue, kevent)
+// (void) ddi_log_sysevent(mpt->m_dip, DDI_VENDOR_LSI, "MPT_SAS",
+// "SAS", NULL, NULL, DDI_NOSLEEP);
+ }
+}
+
+static int
+mps_user_reg_access(struct mps_softc *sc, mps_reg_access_t *data)
+{
+ int status = 0;
+
+ switch (data->Command) {
+ /*
+ * IO access is not supported.
+ */
+ case REG_IO_READ:
+ case REG_IO_WRITE:
+ mps_dprint(sc, MPS_INFO, "IO access is not supported. "
+ "Use memory access.");
+ status = EINVAL;
+ break;
+
+ case REG_MEM_READ:
+ data->RegData = mps_regread(sc, data->RegOffset);
+ break;
+
+ case REG_MEM_WRITE:
+ mps_regwrite(sc, data->RegOffset, data->RegData);
+ break;
+
+ default:
+ status = EINVAL;
+ break;
+ }
+
+ return (status);
+}
+
+static int
+mps_user_btdh(struct mps_softc *sc, mps_btdh_mapping_t *data)
+{
+ uint8_t bt2dh = FALSE;
+ uint8_t dh2bt = FALSE;
+ uint16_t dev_handle, bus, target;
+
+ bus = data->Bus;
+ target = data->TargetID;
+ dev_handle = data->DevHandle;
+
+ /*
+ * When DevHandle is 0xFFFF and Bus/Target are not 0xFFFF, use Bus/
+ * Target to get DevHandle. When Bus/Target are 0xFFFF and DevHandle is
+ * not 0xFFFF, use DevHandle to get Bus/Target. Anything else is
+ * invalid.
+ */
+ if ((bus == 0xFFFF) && (target == 0xFFFF) && (dev_handle != 0xFFFF))
+ dh2bt = TRUE;
+ if ((dev_handle == 0xFFFF) && (bus != 0xFFFF) && (target != 0xFFFF))
+ bt2dh = TRUE;
+ if (!dh2bt && !bt2dh)
+ return (EINVAL);
+
+ /*
+ * Only handle bus of 0. Make sure target is within range.
+ */
+ if (bt2dh) {
+ if (bus != 0)
+ return (EINVAL);
+
+ if (target > sc->max_devices) {
+ mps_dprint(sc, MPS_FAULT, "Target ID is out of range "
+ "for Bus/Target to DevHandle mapping.");
+ return (EINVAL);
+ }
+ dev_handle = sc->mapping_table[target].dev_handle;
+ if (dev_handle)
+ data->DevHandle = dev_handle;
+ } else {
+ bus = 0;
+ target = mps_mapping_get_sas_id_from_handle(sc, dev_handle);
+ data->Bus = bus;
+ data->TargetID = target;
+ }
+
+ return (0);
+}
static int
mps_ioctl(struct cdev *dev, u_long cmd, void *arg, int flag,
@@ -674,7 +2051,7 @@ mps_ioctl(struct cdev *dev, u_long cmd, void *arg, int flag,
struct mps_cfg_page_req *page_req;
struct mps_ext_cfg_page_req *ext_page_req;
void *mps_page;
- int error;
+ int error, reset_loop;
mps_page = NULL;
sc = dev->si_drv1;
@@ -730,6 +2107,98 @@ mps_ioctl(struct cdev *dev, u_long cmd, void *arg, int flag,
case MPSIO_MPS_COMMAND:
error = mps_user_command(sc, (struct mps_usr_command *)arg);
break;
+ case MPTIOCTL_PASS_THRU:
+ /*
+ * The user has requested to pass through a command to be
+ * executed by the MPT firmware. Call our routine which does
+ * this. Only allow one passthru IOCTL at one time.
+ */
+ error = mps_user_pass_thru(sc, (mps_pass_thru_t *)arg);
+ break;
+ case MPTIOCTL_GET_ADAPTER_DATA:
+ /*
+ * The user has requested to read adapter data. Call our
+ * routine which does this.
+ */
+ error = 0;
+ mps_user_get_adapter_data(sc, (mps_adapter_data_t *)arg);
+ break;
+ case MPTIOCTL_GET_PCI_INFO:
+ /*
+ * The user has requested to read pci info. Call
+ * our routine which does this.
+ */
+ mps_lock(sc);
+ error = 0;
+ mps_user_read_pci_info(sc, (mps_pci_info_t *)arg);
+ mps_unlock(sc);
+ break;
+ case MPTIOCTL_RESET_ADAPTER:
+ mps_lock(sc);
+ sc->port_enable_complete = 0;
+ error = mps_reinit(sc);
+ mps_unlock(sc);
+ /*
+ * Wait no more than 5 minutes for Port Enable to complete
+ */
+ for (reset_loop = 0; (reset_loop < MPS_DIAG_RESET_TIMEOUT) &&
+ (!sc->port_enable_complete); reset_loop++) {
+ DELAY(1000);
+ }
+ if (reset_loop == MPS_DIAG_RESET_TIMEOUT) {
+ printf("Port Enable did not complete after Diag "
+ "Reset.\n");
+ }
+ break;
+ case MPTIOCTL_DIAG_ACTION:
+ /*
+ * The user has done a diag buffer action. Call our routine
+ * which does this. Only allow one diag action at one time.
+ */
+ mps_lock(sc);
+ error = mps_user_diag_action(sc, (mps_diag_action_t *)arg);
+ mps_unlock(sc);
+ break;
+ case MPTIOCTL_EVENT_QUERY:
+ /*
+ * The user has done an event query. Call our routine which does
+ * this.
+ */
+ error = 0;
+ mps_user_event_query(sc, (mps_event_query_t *)arg);
+ break;
+ case MPTIOCTL_EVENT_ENABLE:
+ /*
+ * The user has done an event enable. Call our routine which
+ * does this.
+ */
+ error = 0;
+ mps_user_event_enable(sc, (mps_event_enable_t *)arg);
+ break;
+ case MPTIOCTL_EVENT_REPORT:
+ /*
+ * The user has done an event report. Call our routine which
+ * does this.
+ */
+ error = mps_user_event_report(sc, (mps_event_report_t *)arg);
+ break;
+ case MPTIOCTL_REG_ACCESS:
+ /*
+ * The user has requested register access. Call our routine
+ * which does this.
+ */
+ mps_lock(sc);
+ error = mps_user_reg_access(sc, (mps_reg_access_t *)arg);
+ mps_unlock(sc);
+ break;
+ case MPTIOCTL_BTDH_MAPPING:
+ /*
+ * The user has requested to translate a bus/target to a
+ * DevHandle or a DevHandle to a bus/target. Call our routine
+ * which does this.
+ */
+ error = mps_user_btdh(sc, (mps_btdh_mapping_t *)arg);
+ break;
default:
error = ENOIOCTL;
break;
@@ -743,16 +2212,6 @@ mps_ioctl(struct cdev *dev, u_long cmd, void *arg, int flag,
#ifdef COMPAT_FREEBSD32
-/* Macros from compat/freebsd32/freebsd32.h */
-#define PTRIN(v) (void *)(uintptr_t)(v)
-#define PTROUT(v) (uint32_t)(uintptr_t)(v)
-
-#define CP(src,dst,fld) do { (dst).fld = (src).fld; } while (0)
-#define PTRIN_CP(src,dst,fld) \
- do { (dst).fld = PTRIN((src).fld); } while (0)
-#define PTROUT_CP(src,dst,fld) \
- do { (dst).fld = PTROUT((src).fld); } while (0)
-
struct mps_cfg_page_req32 {
MPI2_CONFIG_PAGE_HEADER header;
uint32_t page_address;
diff --git a/sys/dev/mps/mpsvar.h b/sys/dev/mps/mpsvar.h
index 0de654e..c6f83df 100644
--- a/sys/dev/mps/mpsvar.h
+++ b/sys/dev/mps/mpsvar.h
@@ -22,13 +22,44 @@
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
- *
+ *
+ * $FreeBSD$
+ */
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ *
* $FreeBSD$
*/
#ifndef _MPSVAR_H
#define _MPSVAR_H
+#define MPS_DRIVER_VERSION "11.255.03.00-fbsd"
+
#define MPS_DB_MAX_WAIT 2500
#define MPS_REQ_FRAMES 1024
@@ -41,12 +72,137 @@
#define MPS_SGE32_SIZE 8
#define MPS_SGC_SIZE 8
+#define CAN_SLEEP 1
+#define NO_SLEEP 0
+
#define MPS_PERIODIC_DELAY 1 /* 1 second heartbeat/watchdog check */
+#define MPS_SCSI_RI_INVALID_FRAME (0x00000002)
+
+/*
+ * host mapping related macro definitions
+ */
+#define MPS_MAPTABLE_BAD_IDX 0xFFFFFFFF
+#define MPS_DPM_BAD_IDX 0xFFFF
+#define MPS_ENCTABLE_BAD_IDX 0xFF
+#define MPS_MAX_MISSING_COUNT 0x0F
+#define MPS_DEV_RESERVED 0x20000000
+#define MPS_MAP_IN_USE 0x10000000
+#define MPS_RAID_CHANNEL 1
+#define MPS_MAP_BAD_ID 0xFFFFFFFF
+
+/*
+ * WarpDrive controller
+ */
+#define MPS_CHIP_WD_DEVICE_ID 0x007E
+#define MPS_WD_LSI_OEM 0x80
+#define MPS_WD_HIDE_EXPOSE_MASK 0x03
+#define MPS_WD_HIDE_ALWAYS 0x00
+#define MPS_WD_EXPOSE_ALWAYS 0x01
+#define MPS_WD_HIDE_IF_VOLUME 0x02
+#define MPS_WD_RETRY 0x01
+#define MPS_MAN_PAGE10_SIZE 0x5C /* Hardcode for now */
+#define MPS_MAX_DISKS_IN_VOL 10
+
+/*
+ * WarpDrive Event Logging
+ */
+#define MPI2_WD_LOG_ENTRY 0x8002
+#define MPI2_WD_SSD_THROTTLING 0x0041
+#define MPI2_WD_DRIVE_LIFE_WARN 0x0043
+#define MPI2_WD_DRIVE_LIFE_DEAD 0x0044
+#define MPI2_WD_RAIL_MON_FAIL 0x004D
+
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+
+/**
+ * struct dev_mapping_table - device mapping information
+ * @physical_id: SAS address for drives or WWID for RAID volumes
+ * @device_info: bitfield provides detailed info about the device
+ * @phy_bits: bitfields indicating controller phys
+ * @dpm_entry_num: index of this device in device persistent map table
+ * @dev_handle: device handle for the device pointed by this entry
+ * @channel: target channel
+ * @id: target id
+ * @missing_count: number of times the device not detected by driver
+ * @hide_flag: Hide this physical disk/not (foreign configuration)
+ * @init_complete: Whether the start of the day checks completed or not
+ */
+struct dev_mapping_table {
+ u64 physical_id;
+ u32 device_info;
+ u32 phy_bits;
+ u16 dpm_entry_num;
+ u16 dev_handle;
+ u8 reserved1;
+ u8 channel;
+ u16 id;
+ u8 missing_count;
+ u8 init_complete;
+ u8 TLR_bits;
+ u8 reserved2;
+};
+
+/**
+ * struct enc_mapping_table - mapping information about an enclosure
+ * @enclosure_id: Logical ID of this enclosure
+ * @start_index: index to the entry in dev_mapping_table
+ * @phy_bits: bitfields indicating controller phys
+ * @dpm_entry_num: index of this enclosure in device persistent map table
+ * @enc_handle: device handle for the enclosure pointed by this entry
+ * @num_slots: number of slots in the enclosure
+ * @start_slot: Starting slot id
+ * @missing_count: number of times the device not detected by driver
+ * @removal_flag: used to mark the device for removal
+ * @skip_search: used as a flag to include/exclude enclosure for search
+ * @init_complete: Whether the start of the day checks completed or not
+ */
+struct enc_mapping_table {
+ u64 enclosure_id;
+ u32 start_index;
+ u32 phy_bits;
+ u16 dpm_entry_num;
+ u16 enc_handle;
+ u16 num_slots;
+ u16 start_slot;
+ u8 missing_count;
+ u8 removal_flag;
+ u8 skip_search;
+ u8 init_complete;
+};
+
+/**
+ * struct map_removal_table - entries to be removed from mapping table
+ * @dpm_entry_num: index of this device in device persistent map table
+ * @dev_handle: device handle for the device pointed by this entry
+ */
+struct map_removal_table{
+ u16 dpm_entry_num;
+ u16 dev_handle;
+};
+
+typedef struct mps_fw_diagnostic_buffer {
+ size_t size;
+ uint8_t extended_type;
+ uint8_t buffer_type;
+ uint8_t force_release;
+ uint32_t product_specific[23];
+ uint8_t immediate;
+ uint8_t enabled;
+ uint8_t valid_data;
+ uint8_t owned_by_firmware;
+ uint32_t unique_id;
+} mps_fw_diagnostic_buffer_t;
+
struct mps_softc;
struct mps_command;
struct mpssas_softc;
+union ccb;
struct mpssas_target;
+struct mps_column_map;
MALLOC_DECLARE(M_MPT2);
@@ -63,13 +219,16 @@ struct mps_chain {
/*
* This needs to be at least 2 to support SMP passthrough.
*/
-#define MPS_IOVEC_COUNT 2
+#define MPS_IOVEC_COUNT 2
struct mps_command {
TAILQ_ENTRY(mps_command) cm_link;
+ TAILQ_ENTRY(mps_command) cm_recovery;
struct mps_softc *cm_sc;
+ union ccb *cm_ccb;
void *cm_data;
u_int cm_length;
+ u_int cm_out_len;
struct uio cm_uio;
struct iovec cm_iovec[MPS_IOVEC_COUNT];
u_int cm_max_segs;
@@ -82,6 +241,7 @@ struct mps_command {
void *cm_complete_data;
struct mpssas_target *cm_targ;
MPI2_REQUEST_DESCRIPTOR_UNION cm_desc;
+ u_int cm_lun;
u_int cm_flags;
#define MPS_CM_FLAGS_POLLED (1 << 0)
#define MPS_CM_FLAGS_COMPLETE (1 << 1)
@@ -89,7 +249,7 @@ struct mps_command {
#define MPS_CM_FLAGS_DATAOUT (1 << 3)
#define MPS_CM_FLAGS_DATAIN (1 << 4)
#define MPS_CM_FLAGS_WAKEUP (1 << 5)
-#define MPS_CM_FLAGS_ACTIVE (1 << 6)
+#define MPS_CM_FLAGS_DD_IO (1 << 6)
#define MPS_CM_FLAGS_USE_UIO (1 << 7)
#define MPS_CM_FLAGS_SMP_PASS (1 << 8)
#define MPS_CM_FLAGS_CHAIN_FAILED (1 << 9)
@@ -106,6 +266,11 @@ struct mps_command {
struct callout cm_callout;
};
+struct mps_column_map {
+ uint16_t dev_handle;
+ uint8_t phys_disk_num;
+};
+
struct mps_event_handle {
TAILQ_ENTRY(mps_event_handle) eh_list;
mps_evt_callback_t *callback;
@@ -121,17 +286,24 @@ struct mps_softc {
#define MPS_FLAGS_MSI (1 << 1)
#define MPS_FLAGS_BUSY (1 << 2)
#define MPS_FLAGS_SHUTDOWN (1 << 3)
-#define MPS_FLAGS_ATTACH_DONE (1 << 4)
+#define MPS_FLAGS_DIAGRESET (1 << 4)
+#define MPS_FLAGS_ATTACH_DONE (1 << 5)
+#define MPS_FLAGS_WD_AVAILABLE (1 << 6)
u_int mps_debug;
- u_int allow_multiple_tm_cmds;
+ u_int disable_msix;
+ u_int disable_msi;
int tm_cmds_active;
int io_cmds_active;
int io_cmds_highwater;
int chain_free;
+ int max_chains;
int chain_free_lowwater;
+#if __FreeBSD_version >= 900030
uint64_t chain_alloc_fail;
+#endif
struct sysctl_ctx_list sysctl_ctx;
struct sysctl_oid *sysctl_tree;
+ char fw_version[16];
struct mps_command *commands;
struct mps_chain *chains;
struct callout periodic;
@@ -139,9 +311,9 @@ struct mps_softc {
struct mpssas_softc *sassc;
TAILQ_HEAD(, mps_command) req_list;
+ TAILQ_HEAD(, mps_command) high_priority_req_list;
TAILQ_HEAD(, mps_chain) chain_list;
TAILQ_HEAD(, mps_command) tm_list;
- TAILQ_HEAD(, mps_command) io_list;
int replypostindex;
int replyfreeindex;
@@ -196,6 +368,73 @@ struct mps_softc {
bus_addr_t free_busaddr;
bus_dma_tag_t queues_dmat;
bus_dmamap_t queues_map;
+
+ uint8_t *fw_diag_buffer;
+ bus_addr_t fw_diag_busaddr;
+ bus_dma_tag_t fw_diag_dmat;
+ bus_dmamap_t fw_diag_map;
+
+ uint8_t ir_firmware;
+
+ /* static config pages */
+ Mpi2IOCPage8_t ioc_pg8;
+
+ /* host mapping support */
+ struct dev_mapping_table *mapping_table;
+ struct enc_mapping_table *enclosure_table;
+ struct map_removal_table *removal_table;
+ uint8_t *dpm_entry_used;
+ uint8_t *dpm_flush_entry;
+ Mpi2DriverMappingPage0_t *dpm_pg0;
+ uint16_t max_devices;
+ uint16_t max_enclosures;
+ uint16_t max_expanders;
+ uint8_t max_volumes;
+ uint8_t num_enc_table_entries;
+ uint8_t num_rsvd_entries;
+ uint8_t num_channels;
+ uint16_t max_dpm_entries;
+ uint8_t is_dpm_enable;
+ uint8_t track_mapping_events;
+ uint32_t pending_map_events;
+ uint8_t mt_full_retry;
+ uint8_t mt_add_device_failed;
+
+ /* FW diag Buffer List */
+ mps_fw_diagnostic_buffer_t
+ fw_diag_buffer_list[MPI2_DIAG_BUF_TYPE_COUNT];
+
+ /* Event Recording IOCTL support */
+ uint32_t events_to_record[4];
+ mps_event_entry_t recorded_events[MPS_EVENT_QUEUE_SIZE];
+ uint8_t event_index;
+ uint32_t event_number;
+
+ /* EEDP and TLR support */
+ uint8_t eedp_enabled;
+ uint8_t control_TLR;
+
+ /* Shutdown Event Handler */
+ eventhandler_tag shutdown_eh;
+
+ /* To track topo events during reset */
+#define MPS_DIAG_RESET_TIMEOUT 300000
+ uint8_t wait_for_port_enable;
+ uint8_t port_enable_complete;
+
+ /* WD controller */
+ uint8_t WD_valid_config;
+ uint8_t WD_hide_expose;
+
+ /* Direct Drive for WarpDrive */
+ uint8_t DD_num_phys_disks;
+ uint16_t DD_dev_handle;
+ uint32_t DD_stripe_size;
+ uint32_t DD_stripe_exponent;
+ uint32_t DD_block_size;
+ uint16_t DD_block_exponent;
+ uint64_t DD_max_lba;
+ struct mps_column_map DD_column_map[MPS_MAX_DISKS_IN_VOL];
};
struct mps_config_params {
@@ -210,6 +449,13 @@ struct mps_config_params {
void *cbdata;
};
+struct scsi_read_capacity_eedp
+{
+ uint8_t addr[8];
+ uint8_t length[4];
+ uint8_t protect;
+};
+
static __inline uint32_t
mps_regread(struct mps_softc *sc, uint32_t offset)
{
@@ -225,7 +471,6 @@ mps_regwrite(struct mps_softc *sc, uint32_t offset, uint32_t val)
static __inline void
mps_free_reply(struct mps_softc *sc, uint32_t busaddr)
{
-
if (++sc->replyfreeindex >= sc->fqdepth)
sc->replyfreeindex = 0;
sc->free_queue[sc->replyfreeindex] = busaddr;
@@ -242,8 +487,11 @@ mps_alloc_chain(struct mps_softc *sc)
sc->chain_free--;
if (sc->chain_free < sc->chain_free_lowwater)
sc->chain_free_lowwater = sc->chain_free;
- } else
+ }
+#if __FreeBSD_version >= 900030
+ else
sc->chain_alloc_fail++;
+#endif
return (chain);
}
@@ -262,15 +510,16 @@ mps_free_command(struct mps_softc *sc, struct mps_command *cm)
{
struct mps_chain *chain, *chain_temp;
- if (cm->cm_reply != NULL) {
+ if (cm->cm_reply != NULL)
mps_free_reply(sc, cm->cm_reply_data);
- cm->cm_reply = NULL;
- }
+ cm->cm_reply = NULL;
cm->cm_flags = 0;
cm->cm_complete = NULL;
cm->cm_complete_data = NULL;
- cm->cm_targ = 0;
+ cm->cm_ccb = NULL;
+ cm->cm_targ = NULL;
cm->cm_max_segs = 0;
+ cm->cm_lun = 0;
cm->cm_state = MPS_CM_STATE_FREE;
TAILQ_FOREACH_SAFE(chain, &cm->cm_chain_list, chain_link, chain_temp) {
TAILQ_REMOVE(&cm->cm_chain_list, chain, chain_link);
@@ -295,6 +544,43 @@ mps_alloc_command(struct mps_softc *sc)
}
static __inline void
+mps_free_high_priority_command(struct mps_softc *sc, struct mps_command *cm)
+{
+ struct mps_chain *chain, *chain_temp;
+
+ if (cm->cm_reply != NULL)
+ mps_free_reply(sc, cm->cm_reply_data);
+ cm->cm_reply = NULL;
+ cm->cm_flags = 0;
+ cm->cm_complete = NULL;
+ cm->cm_complete_data = NULL;
+ cm->cm_ccb = NULL;
+ cm->cm_targ = NULL;
+ cm->cm_lun = 0;
+ cm->cm_state = MPS_CM_STATE_FREE;
+ TAILQ_FOREACH_SAFE(chain, &cm->cm_chain_list, chain_link, chain_temp) {
+ TAILQ_REMOVE(&cm->cm_chain_list, chain, chain_link);
+ mps_free_chain(sc, chain);
+ }
+ TAILQ_INSERT_TAIL(&sc->high_priority_req_list, cm, cm_link);
+}
+
+static __inline struct mps_command *
+mps_alloc_high_priority_command(struct mps_softc *sc)
+{
+ struct mps_command *cm;
+
+ cm = TAILQ_FIRST(&sc->high_priority_req_list);
+ if (cm == NULL)
+ return (NULL);
+
+ TAILQ_REMOVE(&sc->high_priority_req_list, cm, cm_link);
+ KASSERT(cm->cm_state == MPS_CM_STATE_FREE, ("mps: Allocating busy command\n"));
+ cm->cm_state = MPS_CM_STATE_BUSY;
+ return (cm);
+}
+
+static __inline void
mps_lock(struct mps_softc *sc)
{
mtx_lock(&sc->mps_mtx);
@@ -315,6 +601,12 @@ mps_unlock(struct mps_softc *sc)
#define mps_printf(sc, args...) \
device_printf((sc)->mps_dev, ##args)
+#define mps_vprintf(sc, args...) \
+do { \
+ if (bootverbose) \
+ mps_printf(sc, ##args); \
+} while (0)
+
#define mps_dprint(sc, level, msg, args...) \
do { \
if (sc->mps_debug & level) \
@@ -375,7 +667,9 @@ mps_unmask_intr(struct mps_softc *sc)
mps_regwrite(sc, MPI2_HOST_INTERRUPT_MASK_OFFSET, mask);
}
-int mps_pci_setup_interrupts(struct mps_softc *);
+int mps_pci_setup_interrupts(struct mps_softc *sc);
+int mps_pci_restore(struct mps_softc *sc);
+
int mps_attach(struct mps_softc *sc);
int mps_free(struct mps_softc *sc);
void mps_intr(void *);
@@ -383,23 +677,97 @@ void mps_intr_msi(void *);
void mps_intr_locked(void *);
int mps_register_events(struct mps_softc *, uint8_t *, mps_evt_callback_t *,
void *, struct mps_event_handle **);
+int mps_restart(struct mps_softc *);
int mps_update_events(struct mps_softc *, struct mps_event_handle *, uint8_t *);
int mps_deregister_events(struct mps_softc *, struct mps_event_handle *);
-int mps_request_polled(struct mps_softc *sc, struct mps_command *cm);
-void mps_enqueue_request(struct mps_softc *, struct mps_command *);
int mps_push_sge(struct mps_command *, void *, size_t, int);
int mps_add_dmaseg(struct mps_command *, vm_paddr_t, size_t, u_int, int);
int mps_attach_sas(struct mps_softc *sc);
int mps_detach_sas(struct mps_softc *sc);
-int mps_map_command(struct mps_softc *sc, struct mps_command *cm);
int mps_read_config_page(struct mps_softc *, struct mps_config_params *);
int mps_write_config_page(struct mps_softc *, struct mps_config_params *);
void mps_memaddr_cb(void *, bus_dma_segment_t *, int , int );
void mpi_init_sge(struct mps_command *cm, void *req, void *sge);
int mps_attach_user(struct mps_softc *);
void mps_detach_user(struct mps_softc *);
+void mpssas_record_event(struct mps_softc *sc,
+ MPI2_EVENT_NOTIFICATION_REPLY *event_reply);
+
+int mps_map_command(struct mps_softc *sc, struct mps_command *cm);
+int mps_wait_command(struct mps_softc *sc, struct mps_command *cm, int timeout);
+int mps_request_polled(struct mps_softc *sc, struct mps_command *cm);
+
+int mps_config_get_bios_pg3(struct mps_softc *sc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2BiosPage3_t *config_page);
+int mps_config_get_raid_volume_pg0(struct mps_softc *sc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2RaidVolPage0_t *config_page, u32 page_address);
+int mps_config_get_ioc_pg8(struct mps_softc *sc, Mpi2ConfigReply_t *,
+ Mpi2IOCPage8_t *);
+int mps_config_get_man_pg10(struct mps_softc *sc, Mpi2ConfigReply_t *mpi_reply);
+int mps_config_get_sas_device_pg0(struct mps_softc *, Mpi2ConfigReply_t *,
+ Mpi2SasDevicePage0_t *, u32 , u16 );
+int mps_config_get_dpm_pg0(struct mps_softc *, Mpi2ConfigReply_t *,
+ Mpi2DriverMappingPage0_t *, u16 );
+int mps_config_get_raid_volume_pg1(struct mps_softc *sc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2RaidVolPage1_t *config_page, u32 form,
+ u16 handle);
+int mps_config_get_volume_wwid(struct mps_softc *sc, u16 volume_handle,
+ u64 *wwid);
+int mps_config_get_raid_pd_pg0(struct mps_softc *sc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2RaidPhysDiskPage0_t *config_page,
+ u32 page_address);
+void mpssas_ir_shutdown(struct mps_softc *sc);
+
+int mps_reinit(struct mps_softc *sc);
+void mpssas_handle_reinit(struct mps_softc *sc);
+
+void mps_base_static_config_pages(struct mps_softc *sc);
+void mps_wd_config_pages(struct mps_softc *sc);
+
+int mps_mapping_initialize(struct mps_softc *);
+void mps_mapping_topology_change_event(struct mps_softc *,
+ Mpi2EventDataSasTopologyChangeList_t *);
+int mps_mapping_is_reinit_required(struct mps_softc *);
+void mps_mapping_free_memory(struct mps_softc *sc);
+int mps_config_set_dpm_pg0(struct mps_softc *, Mpi2ConfigReply_t *,
+ Mpi2DriverMappingPage0_t *, u16 );
+void mps_mapping_exit(struct mps_softc *);
+void mps_mapping_check_devices(struct mps_softc *, int);
+int mps_mapping_allocate_memory(struct mps_softc *sc);
+unsigned int mps_mapping_get_sas_id(struct mps_softc *, uint64_t , u16);
+unsigned int mps_mapping_get_sas_id_from_handle(struct mps_softc *sc,
+ u16 handle);
+unsigned int mps_mapping_get_raid_id(struct mps_softc *sc, u64 wwid,
+ u16 handle);
+unsigned int mps_mapping_get_raid_id_from_handle(struct mps_softc *sc,
+ u16 volHandle);
+void mps_mapping_enclosure_dev_status_change_event(struct mps_softc *,
+ Mpi2EventDataSasEnclDevStatusChange_t *event_data);
+void mps_mapping_ir_config_change_event(struct mps_softc *sc,
+ Mpi2EventDataIrConfigChangeList_t *event_data);
+
+void mpssas_evt_handler(struct mps_softc *sc, uintptr_t data,
+ MPI2_EVENT_NOTIFICATION_REPLY *event);
+void mpssas_prepare_remove(struct mpssas_softc *sassc, uint16_t handle);
+int mpssas_startup(struct mps_softc *sc);
SYSCTL_DECL(_hw_mps);
+/* Compatibility shims for different OS versions */
+#if __FreeBSD_version >= 800001
+#define mps_kproc_create(func, farg, proc_ptr, flags, stackpgs, fmtstr, arg) \
+ kproc_create(func, farg, proc_ptr, flags, stackpgs, fmtstr, arg)
+#define mps_kproc_exit(arg) kproc_exit(arg)
+#else
+#define mps_kproc_create(func, farg, proc_ptr, flags, stackpgs, fmtstr, arg) \
+ kthread_create(func, farg, proc_ptr, flags, stackpgs, fmtstr, arg)
+#define mps_kproc_exit(arg) kthread_exit(arg)
+#endif
+
+#if defined(CAM_PRIORITY_XPT)
+#define MPS_PRIORITY_XPT CAM_PRIORITY_XPT
+#else
+#define MPS_PRIORITY_XPT 5
+#endif
#endif
diff --git a/sys/modules/mps/Makefile b/sys/modules/mps/Makefile
index 49e65da..040b04e 100644
--- a/sys/modules/mps/Makefile
+++ b/sys/modules/mps/Makefile
@@ -4,10 +4,11 @@
KMOD= mps
SRCS= mps_pci.c mps.c mps_sas.c mps_table.c mps_user.c
-SRCS+= opt_mps.h opt_cam.h opt_compat.h
+SRCS+= mps_config.c mps_mapping.c mps_sas_lsi.c
+SRCS+= opt_cam.h opt_compat.h
SRCS+= device_if.h bus_if.h pci_if.h
#CFLAGS += -DMPS_DEBUG
-DEBUG += -g
+DEBUG_FLAGS += -g
.include <bsd.kmod.mk>
OpenPOWER on IntegriCloud