From 32d73a71849067bab55e932e89e3cf75657c9891 Mon Sep 17 00:00:00 2001 From: Sascha Wildner Date: Tue, 7 Feb 2012 09:42:32 +0100 Subject: [PATCH] mps(4): Sync with FreeBSD. This a still in progress port of the new, LSI-supported version of the mps(4) driver from FreeBSD. Some of the changes are (from FreeBSD's commit msg): - 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. This commit also moves the driver from sys/dev/disk to sys/dev/raid. It still fails attaching at this stage. Work on it will continue in master. --- share/man/man4/mps.4 | 32 +- sys/conf/files | 13 +- sys/dev/disk/Makefile | 2 +- sys/dev/disk/mps/Makefile | 10 - sys/dev/disk/mps/mps_ioctl.h | 106 - sys/dev/disk/mps/mps_sas.c | 2016 ---------- sys/dev/disk/mps/mps_user.c | 943 ----- sys/dev/disk/mps/mpsvar.h | 389 -- sys/dev/raid/Makefile | 2 +- sys/dev/raid/mps/Makefile | 14 + sys/dev/{disk => raid}/mps/mpi/mpi2.h | 62 +- sys/dev/{disk => raid}/mps/mpi/mpi2_cnfg.h | 321 +- sys/dev/{disk => raid}/mps/mpi/mpi2_hbd.h | 42 +- .../{disk => raid}/mps/mpi/mpi2_history.txt | 149 +- sys/dev/{disk => raid}/mps/mpi/mpi2_init.h | 61 +- sys/dev/{disk => raid}/mps/mpi/mpi2_ioc.h | 162 +- sys/dev/{disk => raid}/mps/mpi/mpi2_ra.h | 33 +- sys/dev/{disk => raid}/mps/mpi/mpi2_raid.h | 37 +- sys/dev/{disk => raid}/mps/mpi/mpi2_sas.h | 45 +- sys/dev/{disk => raid}/mps/mpi/mpi2_targ.h | 39 +- sys/dev/{disk => raid}/mps/mpi/mpi2_tool.h | 87 +- sys/dev/{disk => raid}/mps/mpi/mpi2_type.h | 33 +- sys/dev/{disk => raid}/mps/mps.c | 826 ++++- sys/dev/raid/mps/mps_config.c | 1390 +++++++ sys/dev/raid/mps/mps_ioctl.h | 387 ++ sys/dev/raid/mps/mps_mapping.c | 2265 ++++++++++++ .../mps_table.h => raid/mps/mps_mapping.h} | 60 +- sys/dev/{disk => raid}/mps/mps_pci.c | 168 +- sys/dev/raid/mps/mps_sas.c | 3234 +++++++++++++++++ sys/dev/raid/mps/mps_sas.h | 161 + sys/dev/raid/mps/mps_sas_lsi.c | 862 +++++ sys/dev/{disk => raid}/mps/mps_table.c | 41 +- sys/dev/{disk => raid}/mps/mps_table.h | 0 sys/dev/raid/mps/mps_user.c | 2399 ++++++++++++ sys/dev/raid/mps/mpsvar.h | 768 ++++ 35 files changed, 13238 insertions(+), 3921 deletions(-) delete mode 100644 sys/dev/disk/mps/Makefile delete mode 100644 sys/dev/disk/mps/mps_ioctl.h delete mode 100644 sys/dev/disk/mps/mps_sas.c delete mode 100644 sys/dev/disk/mps/mps_user.c delete mode 100644 sys/dev/disk/mps/mpsvar.h create mode 100644 sys/dev/raid/mps/Makefile rename sys/dev/{disk => raid}/mps/mpi/mpi2.h (94%) rename sys/dev/{disk => raid}/mps/mpi/mpi2_cnfg.h (90%) rename sys/dev/{disk => raid}/mps/mpi/mpi2_hbd.h (72%) rename sys/dev/{disk => raid}/mps/mpi/mpi2_history.txt (75%) rename sys/dev/{disk => raid}/mps/mpi/mpi2_init.h (91%) rename sys/dev/{disk => raid}/mps/mpi/mpi2_ioc.h (89%) rename sys/dev/{disk => raid}/mps/mpi/mpi2_ra.h (71%) rename sys/dev/{disk => raid}/mps/mpi/mpi2_raid.h (89%) rename sys/dev/{disk => raid}/mps/mpi/mpi2_sas.h (86%) rename sys/dev/{disk => raid}/mps/mpi/mpi2_targ.h (92%) rename sys/dev/{disk => raid}/mps/mpi/mpi2_tool.h (82%) rename sys/dev/{disk => raid}/mps/mpi/mpi2_type.h (58%) rename sys/dev/{disk => raid}/mps/mps.c (70%) create mode 100644 sys/dev/raid/mps/mps_config.c create mode 100644 sys/dev/raid/mps/mps_ioctl.h create mode 100644 sys/dev/raid/mps/mps_mapping.c copy sys/dev/{disk/mps/mps_table.h => raid/mps/mps_mapping.h} (51%) rename sys/dev/{disk => raid}/mps/mps_pci.c (69%) create mode 100644 sys/dev/raid/mps/mps_sas.c create mode 100644 sys/dev/raid/mps/mps_sas.h create mode 100644 sys/dev/raid/mps/mps_sas_lsi.c rename sys/dev/{disk => raid}/mps/mps_table.c (94%) rename sys/dev/{disk => raid}/mps/mps_table.h (100%) create mode 100644 sys/dev/raid/mps/mps_user.c create mode 100644 sys/dev/raid/mps/mpsvar.h diff --git a/share/man/man4/mps.4 b/share/man/man4/mps.4 index 8516e19a2d..bcdfa7e89a 100644 --- a/share/man/man4/mps.4 +++ b/share/man/man4/mps.4 @@ -32,7 +32,7 @@ .\" Author: Ken Merry .\" .\" $Id: //depot/SpectraBSD/head/share/man/man4/mps.4#4 $ -.\" $FreeBSD: src/share/man/man4/mps.4,v 1.3 2010/10/07 21:56:10 ken Exp $ +.\" $FreeBSD: src/share/man/man4/mps.4,v 1.5 2011/11/11 12:06:09 brueffer Exp $ .\" .Dd September 13, 2010 .Dt MPS 4 @@ -60,9 +60,9 @@ driver provides support for LSI Logic Fusion-MPT 2 .Tn SAS controllers. .Sh HARDWARE -The following controllers are supported by the +The .Nm -driver +driver supports the following controllers: .Pp .Bl -bullet -compact .It @@ -143,6 +143,19 @@ Enable prints for controller events. .Xr sa 4 , .Xr scsi 4 , .Xr targ 4 +.Sh HISTORY +The +.Nm +driver first appeared in +.Fx 9.0 . +.Sh AUTHORS +.An -nosplit +The +.Nm +driver was originally written by +.An Scott Long Aq scottl@FreeBSD.org . +This man page was written by +.An Ken Merry Aq ken@FreeBSD.org . .Sh BUGS This driver is still in development, it has only been tested on the amd64 architecture and has some known shortcomings: @@ -158,16 +171,3 @@ times, without user intervention. .It The error recovery code isn't complete. .El -.Sh HISTORY -The -.Nm -driver first appeared in -.Fx 9.0 . -.Sh AUTHORS -.An -nosplit -The -.Nm -driver was originally written by -.An Scott Long Aq scottl@FreeBSD.org . -This man page was written by -.An Ken Merry Aq ken@FreeBSD.org . diff --git a/sys/conf/files b/sys/conf/files index a26134c8a2..ddaca2d616 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -633,11 +633,14 @@ bus/mmc/mmc.c optional mmc bus/mmc/mmcbr_if.m standard bus/mmc/mmcbus_if.m standard dev/disk/mmcsd/mmcsd.c optional mmcsd -dev/disk/mps/mps.c optional mps -dev/disk/mps/mps_pci.c optional mps pci -dev/disk/mps/mps_sas.c optional mps -dev/disk/mps/mps_table.c optional mps -dev/disk/mps/mps_user.c optional mps +dev/raid/mps/mps.c optional mps +dev/raid/mps/mps_config.c optional mps +dev/raid/mps/mps_mapping.c optional mps +dev/raid/mps/mps_pci.c optional mps pci +dev/raid/mps/mps_sas.c optional mps +dev/raid/mps/mps_sas_lsi.c optional mps +dev/raid/mps/mps_table.c optional mps +dev/raid/mps/mps_user.c optional mps dev/disk/mpt/mpt.c optional mpt dev/disk/mpt/mpt_cam.c optional mpt dev/disk/mpt/mpt_debug.c optional mpt diff --git a/sys/dev/disk/Makefile b/sys/dev/disk/Makefile index f1b2d16359..0f9e02a28c 100644 --- a/sys/dev/disk/Makefile +++ b/sys/dev/disk/Makefile @@ -1,6 +1,6 @@ .include "${.CURDIR}/../../platform/${MACHINE_PLATFORM}/Makefile.inc" -SUBDIR= ahci aic aic7xxx ccd dm iscsi isp ispfw md mmcsd mps mpt ncv nsp +SUBDIR= ahci aic aic7xxx ccd dm iscsi isp ispfw md mmcsd mpt ncv nsp SUBDIR+= sbp sdhci sili stg trm vn vpo .for dir in ${SUBDIR} diff --git a/sys/dev/disk/mps/Makefile b/sys/dev/disk/mps/Makefile deleted file mode 100644 index 77b1a1eb71..0000000000 --- a/sys/dev/disk/mps/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -# $FreeBSD: src/sys/modules/mps/Makefile,v 1.3 2010/10/17 20:01:56 scottl Exp $ - -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+= device_if.h bus_if.h pci_if.h - -#CFLAGS += -DMPS_DEBUG - -.include diff --git a/sys/dev/disk/mps/mps_ioctl.h b/sys/dev/disk/mps/mps_ioctl.h deleted file mode 100644 index d804d05a52..0000000000 --- a/sys/dev/disk/mps/mps_ioctl.h +++ /dev/null @@ -1,106 +0,0 @@ -/*- - * Copyright (c) 2008 Yahoo!, Inc. - * All rights reserved. - * Written by: John Baldwin - * - * 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. - * 3. Neither the name of the author nor the names of any co-contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * 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 userland interface - * - * $FreeBSD: src/sys/dev/mps/mps_ioctl.h,v 1.2 2010/10/11 21:26:24 mdf Exp $ - */ - -#ifndef _MPS_IOCTL_H_ -#define _MPS_IOCTL_H_ - -#include -#include -#include -#include - -/* - * For the read header requests, the header should include the page - * type or extended page type, page number, and page version. The - * buffer and length are unused. The completed header is returned in - * the 'header' member. - * - * For the read page and write page requests, 'buf' should point to a - * buffer of 'len' bytes which holds the entire page (including the - * header). - * - * All requests specify the page address in 'page_address'. - */ -struct mps_cfg_page_req { - MPI2_CONFIG_PAGE_HEADER header; - uint32_t page_address; - void *buf; - int len; - uint16_t ioc_status; -}; - -struct mps_ext_cfg_page_req { - MPI2_CONFIG_EXTENDED_PAGE_HEADER header; - uint32_t page_address; - void *buf; - int len; - uint16_t ioc_status; -}; - -struct mps_raid_action { - uint8_t action; - uint8_t volume_bus; - uint8_t volume_id; - uint8_t phys_disk_num; - uint32_t action_data_word; - void *buf; - int len; - uint32_t volume_status; - uint32_t action_data[4]; - uint16_t action_status; - uint16_t ioc_status; - uint8_t write; -}; - -struct mps_usr_command { - void *req; - uint32_t req_len; - void *rpl; - uint32_t rpl_len; - void *buf; - int len; - uint32_t flags; -}; - -#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) -#define MPSIO_READ_EXT_CFG_PAGE _IOWR('M', 203, struct mps_ext_cfg_page_req) -#define MPSIO_WRITE_CFG_PAGE _IOWR('M', 204, struct mps_cfg_page_req) -#define MPSIO_RAID_ACTION _IOWR('M', 205, struct mps_raid_action) -#define MPSIO_MPS_COMMAND _IOWR('M', 210, struct mps_usr_command) - -#endif /* !_MPS_IOCTL_H_ */ diff --git a/sys/dev/disk/mps/mps_sas.c b/sys/dev/disk/mps/mps_sas.c deleted file mode 100644 index bc34cecbb1..0000000000 --- a/sys/dev/disk/mps/mps_sas.c +++ /dev/null @@ -1,2016 +0,0 @@ -/*- - * Copyright (c) 2009 Yahoo! Inc. - * 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. - * - * $FreeBSD: src/sys/dev/mps/mps_sas.c,v 1.7 2010/12/11 00:36:35 ken Exp $ - */ - -/* Communications core for LSI MPT2 */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if __FreeBSD_version >= 900026 -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include - -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; -}; - -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; -}; - -#define MPSSAS_DISCOVERY_TIMEOUT 20 -#define MPSSAS_MAX_DISCOVERY_TIMEOUTS 10 /* 200 seconds */ - -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_bus_scan_cb(struct cam_periph *periph, union ccb *ccb); -static void mpssas_startup(void *data); -static void mpssas_discovery_end(struct mpssas_softc *sassc); -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_action_scsiio(struct mpssas_softc *, union ccb *); -static void mpssas_scsiio_complete(struct mps_softc *, struct mps_command *); -#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 *); -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 __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); -} - -static struct mpssas_target * -mpssas_alloc_target(struct mpssas_softc *sassc, struct mpssas_target *probe) -{ - 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; - } - - target = mpssas_find_target(sassc, start, 0); - - /* - * 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); - - return (target); -} - -static struct mpssas_target * -mpssas_find_target(struct mpssas_softc *sassc, int start, uint16_t handle) -{ - 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 (NULL); -} - -/* - * 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) -{ - struct mpssas_devprobe *probe; - struct mps_config_params *params; - MPI2_CONFIG_EXTENDED_PAGE_HEADER *hdr; - int error; - - mps_dprint(sc, MPS_TRACE, "%s\n", __func__); - - probe = kmalloc(sizeof(*probe), M_MPSSAS, M_NOWAIT | M_ZERO); - if (probe == NULL) { - mps_dprint(sc, MPS_FAULT, "Out of memory starting probe\n"); - return; - } - params = &probe->params; - hdr = ¶ms->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) { - kfree(probe, M_MPSSAS); - mps_dprint(sc, MPS_FAULT, "Failure starting device probe\n"); - return; - } -} - -static void -mpssas_probe_device_complete(struct mps_softc *sc, - struct mps_config_params *params) -{ - MPI2_CONFIG_EXTENDED_PAGE_HEADER *hdr; - struct mpssas_devprobe *probe; - int error; - - mps_dprint(sc, MPS_TRACE, "%s\n", __func__); - - hdr = ¶ms->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); - kfree(probe, M_MPSSAS); - return; - } - params->action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; - params->length = hdr->ExtPageLength * 4; - params->buffer = kmalloc(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); - kfree(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); - kfree(params->buffer, M_MPSSAS); - kfree(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; - } - params->action = MPI2_CONFIG_ACTION_PAGE_HEADER; - hdr->ExtPageLength = 0; - hdr->PageVersion = 0; - params->buffer = NULL; - params->length = 0; - kfree(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); - kfree(params->buffer, M_MPSSAS); - kfree(probe, M_MPSSAS); - return; - } - - 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; - } - kfree(params->buffer, M_MPSSAS); - - 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); - kfree(probe, M_MPSSAS); - return; - } - if ((targ = mpssas_alloc_target(sassc, &probe->target)) == NULL) { - mps_printf(sc, "Target table overflow, handle 0x%04x\n", - handle); - kfree(probe, M_MPSSAS); - return; - } - - *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); - - kfree(probe, M_MPSSAS); - mpssas_announce_device(sassc, targ); - break; - } - default: - kprintf("what?\n"); - } -} - -/* - * 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) -{ - 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) - /* We don't know about this device? */ - return; - - sc = sassc->sc; - cm = mps_alloc_command(sc); - if (cm == NULL) { - mps_printf(sc, "command alloc failure in mpssas_prepare_remove\n"); - return; - } - - req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)cm->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; - cm->cm_complete = mpssas_remove_device; - cm->cm_targ = targ; - mpssas_issue_tm_request(sc, cm); -} - -static void -mpssas_remove_device(struct mps_softc *sc, struct mps_command *cm) -{ - MPI2_SCSI_TASK_MANAGE_REPLY *reply; - MPI2_SAS_IOUNIT_CONTROL_REQUEST *req; - struct mpssas_target *targ; - uint16_t handle; - - 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); - - if (reply->IOCStatus != MPI2_IOCSTATUS_SUCCESS) { - mps_printf(sc, "Failure 0x%x reseting device 0x%04x\n", - reply->IOCStatus, handle); - mps_free_command(sc, cm); - return; - } - - mps_printf(sc, "Reset aborted %d commands\n", - (u_int)reply->TerminationCount); - mps_free_reply(sc, cm->cm_reply_data); - - /* Reuse the existing command */ - req = (MPI2_SAS_IOUNIT_CONTROL_REQUEST *)cm->cm_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; - - mps_map_command(sc, cm); - - mps_dprint(sc, MPS_INFO, "clearing target handle 0x%04x\n", handle); - targ = mpssas_find_target(sc->sassc, 0, handle); - if (targ != NULL) { - targ->handle = 0x0; - mpssas_announce_device(sc->sassc, targ); - } -} - -static void -mpssas_remove_complete(struct mps_softc *sc, struct mps_command *cm) -{ - MPI2_SAS_IOUNIT_CONTROL_REPLY *reply; - - 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; - - data = (MPI2_EVENT_DATA_SAS_DISCOVERY *)&event->EventData; - - 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; - } - 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) - kprintf("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; - } - } - - break; - } - case MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE: - break; - default: - break; - } - - mps_free_reply(sc, data); -} - -static int -mpssas_register_events(struct mps_softc *sc) -{ - uint8_t events[16]; - - bzero(events, 16); - setbit(events, MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE); - setbit(events, MPI2_EVENT_SAS_DISCOVERY); - setbit(events, MPI2_EVENT_SAS_BROADCAST_PRIMITIVE); - setbit(events, MPI2_EVENT_SAS_INIT_DEVICE_STATUS_CHANGE); - 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); - - mps_register_events(sc, events, mpssas_evt_handler, NULL, - &sc->sassc->mpssas_eh); - - return (0); -} - -int -mps_attach_sas(struct mps_softc *sc) -{ - struct mpssas_softc *sassc; - int error = 0; - int num_sim_reqs; - - mps_dprint(sc, MPS_TRACE, "%s\n", __func__); - - sassc = kmalloc(sizeof(struct mpssas_softc), M_MPT2, M_WAITOK|M_ZERO); - sassc->targets = kmalloc(sizeof(struct mpssas_target) * - sc->facts->MaxTargets, M_MPT2, M_WAITOK|M_ZERO); - 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) { - mps_dprint(sc, MPS_FAULT, "Cannot allocate SIMQ\n"); - error = ENOMEM; - goto out; - } - - sassc->sim = cam_sim_alloc(mpssas_action, mpssas_poll, "mps", sassc, - device_get_unit(sc->mps_dev), &sc->mps_lock, num_sim_reqs, - num_sim_reqs, sassc->devq); - if (sassc->sim == NULL) { - mps_dprint(sc, MPS_FAULT, "Cannot allocate SIM\n"); - error = EINVAL; - goto out; - } - - /* - * 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, 0)) != 0) { - mps_dprint(sc, MPS_FAULT, "Error %d registering SCSI bus\n", - error); - mps_unlock(sc); - goto out; - } - - /* - * Assume that discovery events will start right away. Freezing - * the simq will prevent the CAM boottime scanner from running - * before discovery is complete. - */ - sassc->flags = MPSSAS_IN_STARTUP | MPSSAS_IN_DISCOVERY; - xpt_freeze_simq(sassc->sim, 1); - - mps_unlock(sc); - - callout_init(&sassc->discovery_callout); - sassc->discovery_timeouts = 0; - - mpssas_register_events(sc); -out: - if (error) - mps_detach_sas(sc); - return (error); -} - -int -mps_detach_sas(struct mps_softc *sc) -{ - struct mpssas_softc *sassc; - - mps_dprint(sc, MPS_TRACE, "%s\n", __func__); - - if (sc->sassc == NULL) - return (0); - - sassc = sc->sassc; - - /* 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); - - mps_lock(sc); - - if (sassc->sim != NULL) { - xpt_bus_deregister(cam_sim_path(sassc->sim)); - cam_sim_free(sassc->sim); - } - mps_unlock(sc); - - if (sassc->devq != NULL) - cam_simq_release(sassc->devq); - - kfree(sassc->targets, M_MPT2); - kfree(sassc, M_MPT2); - sc->sassc = NULL; - - return (0); -} - -static void -mpssas_discovery_end(struct mpssas_softc *sassc) -{ - struct mps_softc *sc = sassc->sc; - - mps_dprint(sc, MPS_TRACE, "%s\n", __func__); - - 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) -{ - union ccb *ccb; - int bus, tid, lun; - - /* - * 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(); - 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_setup_ccb(&ccb->ccb_h, ccb->ccb_h.path, 5/*priority (low)*/); - ccb->ccb_h.func_code = XPT_SCAN_BUS; - ccb->ccb_h.cbfcnp = mpssas_bus_scan_cb; - ccb->crcn.flags = CAM_FLAG_NONE; - xpt_action(ccb); -} - -static void -mpssas_bus_scan_cb(struct cam_periph *periph, union ccb *ccb) -{ - xpt_free_path(ccb->ccb_h.path); - kfree(ccb, M_TEMP); -} - -static void -mpssas_startup(void *data) -{ - struct mpssas_softc *sassc = data; - - mps_dprint(sassc->sc, MPS_TRACE, "%s\n", __func__); - - mps_lock(sassc->sc); - if ((sassc->flags & MPSSAS_IN_DISCOVERY) == 0) { - mpssas_discovery_end(sassc); - } else { - if (sassc->discovery_timeouts < MPSSAS_MAX_DISCOVERY_TIMEOUTS) { - sassc->flags |= MPSSAS_DISCOVERY_TIMEOUT_PENDING; - callout_reset(&sassc->discovery_callout, - MPSSAS_DISCOVERY_TIMEOUT * hz, - mpssas_discovery_timeout, sassc); - sassc->discovery_timeouts++; - } else { - mps_dprint(sassc->sc, MPS_FAULT, - "Discovery timed out, continuing.\n"); - sassc->flags &= ~MPSSAS_IN_DISCOVERY; - 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 -mpssas_action(struct cam_sim *sim, union ccb *ccb) -{ - struct mpssas_softc *sassc; - - sassc = cam_sim_softc(sim); - - mps_dprint(sassc->sc, MPS_TRACE, "%s func 0x%x\n", __func__, - ccb->ccb_h.func_code); - - switch (ccb->ccb_h.func_code) { - case XPT_PATH_INQ: - { - struct ccb_pathinq *cpi = &ccb->cpi; - - cpi->version_num = 1; - cpi->hba_inquiry = PI_SDTR_ABLE|PI_TAG_ABLE|PI_WIDE_16; - cpi->target_sprt = 0; - cpi->hba_misc = PIM_NOBUSRESET; - cpi->hba_eng_cnt = 0; - cpi->max_target = sassc->sc->facts->MaxTargets - 1; - cpi->max_lun = 0; - cpi->initiator_id = 255; - strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); - strncpy(cpi->hba_vid, "LSILogic", HBA_IDLEN); - strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); - cpi->unit_number = cam_sim_unit(sim); - cpi->bus_id = cam_sim_bus(sim); - cpi->base_transfer_speed = 150000; - cpi->transport = XPORT_SAS; - cpi->transport_version = 0; - cpi->protocol = PROTO_SCSI; - cpi->protocol_version = SCSI_REV_SPC; - cpi->ccb_h.status = CAM_REQ_CMP; - break; - } - case XPT_GET_TRAN_SETTINGS: - { - struct ccb_trans_settings *cts; - struct ccb_trans_settings_sas *sas; - struct ccb_trans_settings_scsi *scsi; - struct mpssas_target *targ; - - cts = &ccb->cts; - sas = &cts->xport_specific.sas; - scsi = &cts->proto_specific.scsi; - - targ = &sassc->targets[cts->ccb_h.target_id]; - if (targ->handle == 0x0) { - cts->ccb_h.status = CAM_TID_INVALID; - break; - } - - cts->protocol_version = SCSI_REV_SPC2; - cts->transport = XPORT_SAS; - cts->transport_version = 0; - - sas->valid = CTS_SAS_VALID_SPEED; - switch (targ->linkrate) { - case 0x08: - sas->bitrate = 150000; - break; - case 0x09: - sas->bitrate = 300000; - break; - case 0x0a: - sas->bitrate = 600000; - break; - default: - sas->valid = 0; - } - - cts->protocol = PROTO_SCSI; - scsi->valid = CTS_SCSI_VALID_TQ; - scsi->flags = CTS_SCSI_FLAGS_TAG_ENB; - - cts->ccb_h.status = CAM_REQ_CMP; - break; - } - case XPT_CALC_GEOMETRY: - cam_calc_geometry(&ccb->ccg, /*extended*/1); - ccb->ccb_h.status = CAM_REQ_CMP; - break; - case XPT_RESET_DEV: - mpssas_action_resetdev(sassc, ccb); - return; - case XPT_RESET_BUS: - case XPT_ABORT: - case XPT_TERM_IO: - ccb->ccb_h.status = CAM_REQ_CMP; - break; - case XPT_SCSI_IO: - mpssas_action_scsiio(sassc, ccb); - return; -#if __FreeBSD_version >= 900026 - case XPT_SMP_IO: - mpssas_action_smpio(sassc, ccb); - return; -#endif /* __FreeBSD_version >= 900026 */ - default: - ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; - break; - } - xpt_done(ccb); - -} - -#if 0 -static void -mpssas_resettimeout_complete(struct mps_softc *sc, struct mps_command *cm) -{ - 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; - - mps_free_command(sc, cm); - mpssas_unfreeze_device(sassc, targ); - - if (code != MPI2_SCSITASKMGMT_RSP_TM_COMPLETE) { - mps_reset_controller(sc); - } - - return; -} -#endif - -static void -mpssas_scsiio_timeout(void *data) -{ - 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 - - cm = (struct mps_command *)data; - sc = cm->cm_sc; - - /* - * 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; - } - - ccb = cm->cm_complete_data; - targ = cm->cm_targ; - if (targ == NULL) - /* Driver bug */ - targ = &sc->sassc->targets[ccb->ccb_h.target_id]; - - 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. - */ -#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 - - /* 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; - - /* - * recycle the command into recovery so that there's no risk of - * command allocation failure. - */ - cm->cm_state = MPS_CM_STATE_TIMEDOUT; - mpssas_recovery(sc, cm); - mps_unlock(sc); -} - -static void -mpssas_abort_complete(struct mps_softc *sc, struct mps_command *cm) -{ - MPI2_SCSI_TASK_MANAGE_REQUEST *req; - - req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)cm->cm_req; - - mps_printf(sc, "%s: abort request on handle %#04x SMID %d " - "complete\n", __func__, req->DevHandle, req->TaskMID); - - mpssas_complete_tm_request(sc, cm, /*free_cm*/ 1); -} - -static void -mpssas_recovery(struct mps_softc *sc, struct mps_command *abort_cm) -{ - struct mps_command *cm; - MPI2_SCSI_TASK_MANAGE_REQUEST *req, *orig_req; - - cm = mps_alloc_command(sc); - if (cm == NULL) { - mps_printf(sc, "%s: command allocation failure\n", __func__); - return; - } - - cm->cm_targ = abort_cm->cm_targ; - cm->cm_complete = mpssas_abort_complete; - - 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; - - cm->cm_data = NULL; - cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; - - mpssas_issue_tm_request(sc, cm); - -} - -/* - * Can return 0 or EINPROGRESS on success. Any other value means failure. - */ -static int -mpssas_map_tm_request(struct mps_softc *sc, struct mps_command *cm) -{ - int error; - - error = 0; - - cm->cm_flags |= MPS_CM_FLAGS_ACTIVE; - error = mps_map_command(sc, cm); - if ((error == 0) - || (error == EINPROGRESS)) - sc->tm_cmds_active++; - - return (error); -} - -static void -mpssas_issue_tm_request(struct mps_softc *sc, struct mps_command *cm) -{ - int freeze_queue, send_command, error; - - freeze_queue = 0; - send_command = 0; - error = 0; - - KKASSERT(lockstatus(&sc->mps_lock, curthread) != 0); - - /* - * 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. - */ - 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); - - error = mpssas_map_tm_request(sc, cm); - - /* - * 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. - */ - if ((error != 0) - && (error != EINPROGRESS)) - mpssas_tm_complete(sc, cm, - MPI2_SCSITASKMGMT_RSP_TM_FAILED); - } -} - -static void -mpssas_tm_complete(struct mps_softc *sc, struct mps_command *cm, int error) -{ - MPI2_SCSI_TASK_MANAGE_REPLY *resp; - - resp = (MPI2_SCSI_TASK_MANAGE_REPLY *)cm->cm_reply; - - resp->ResponseCode = error; - - /* - * Call the callback for this command, it will be - * removed from the list and freed via the callback. - */ - cm->cm_complete(sc, cm); -} - -/* - * 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; - - error = 0; - - KKASSERT(lockstatus(&sc->mps_lock, curthread) != 0); - - TAILQ_REMOVE(&sc->tm_list, cm, cm_link); - cm->cm_flags &= ~MPS_CM_FLAGS_ACTIVE; - sc->tm_cmds_active--; - - if (free_cm != 0) - mps_free_command(sc, cm); - - 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: - - /* - * 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; - - /* This command is active, no need to send it again */ - if (cm2->cm_flags & MPS_CM_FLAGS_ACTIVE) - continue; - - req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)cm2->cm_req; - - mps_printf(sc, "%s: sending deferred task management " - "request for handle %#04x SMID %d\n", __func__, - req->DevHandle, req->TaskMID); - - error = mpssas_map_tm_request(sc, cm2); - - /* - * 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); - - /* - * 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 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; - } - } - - return (error); -} - -static void -mpssas_action_scsiio(struct mpssas_softc *sassc, union ccb *ccb) -{ - MPI2_SCSI_IO_REQUEST *req; - struct ccb_scsiio *csio; - struct mps_softc *sc; - struct mpssas_target *targ; - struct mps_command *cm; - - mps_dprint(sassc->sc, MPS_TRACE, "%s\n", __func__); - - sc = sassc->sc; - - csio = &ccb->csio; - targ = &sassc->targets[csio->ccb_h.target_id]; - if (targ->handle == 0x0) { - csio->ccb_h.status = CAM_SEL_TIMEOUT; - xpt_done(ccb); - return; - } - - cm = mps_alloc_command(sc); - if (cm == NULL) { - if ((sassc->flags & MPSSAS_QUEUE_FROZEN) == 0) { - xpt_freeze_simq(sassc->sim, 1); - sassc->flags |= MPSSAS_QUEUE_FROZEN; - } - ccb->ccb_h.status &= ~CAM_SIM_QUEUED; - ccb->ccb_h.status |= CAM_REQUEUE_REQ; - xpt_done(ccb); - return; - } - - req = (MPI2_SCSI_IO_REQUEST *)cm->cm_req; - req->DevHandle = targ->handle; - req->Function = MPI2_FUNCTION_SCSI_IO_REQUEST; - req->MsgFlags = 0; - req->SenseBufferLowAddress = cm->cm_sense_busaddr; - req->SenseBufferLength = MPS_SENSE_LEN; - req->SGLFlags = 0; - req->ChainOffset = 0; - req->SGLOffset0 = 24; /* 32bit word offset to the SGL */ - req->SGLOffset1= 0; - req->SGLOffset2= 0; - req->SGLOffset3= 0; - req->SkipCount = 0; - req->DataLength = csio->dxfer_len; - req->BidirectionalDataLength = 0; - req->IoFlags = csio->cdb_len; - req->EEDPFlags = 0; - - /* Note: BiDirectional transfers are not supported */ - switch (csio->ccb_h.flags & CAM_DIR_MASK) { - case CAM_DIR_IN: - req->Control = MPI2_SCSIIO_CONTROL_READ; - cm->cm_flags |= MPS_CM_FLAGS_DATAIN; - break; - case CAM_DIR_OUT: - req->Control = MPI2_SCSIIO_CONTROL_WRITE; - cm->cm_flags |= MPS_CM_FLAGS_DATAOUT; - break; - case CAM_DIR_NONE: - default: - req->Control = MPI2_SCSIIO_CONTROL_NODATATRANSFER; - break; - } - - /* - * It looks like the hardware doesn't require an explicit tag - * number for each transaction. SAM Task Management not supported - * at the moment. - */ - switch (csio->tag_action) { - case MSG_HEAD_OF_Q_TAG: - req->Control |= MPI2_SCSIIO_CONTROL_HEADOFQ; - break; - case MSG_ORDERED_Q_TAG: - req->Control |= MPI2_SCSIIO_CONTROL_ORDEREDQ; - break; - case MSG_ACA_TASK: - req->Control |= MPI2_SCSIIO_CONTROL_ACAQ; - break; - case CAM_TAG_ACTION_NONE: - case MSG_SIMPLE_Q_TAG: - default: - req->Control |= MPI2_SCSIIO_CONTROL_SIMPLEQ; - break; - } - - if (MPS_SET_LUN(req->LUN, csio->ccb_h.target_lun) != 0) { - mps_free_command(sc, cm); - ccb->ccb_h.status = CAM_LUN_INVALID; - xpt_done(ccb); - return; - } - - if (csio->ccb_h.flags & CAM_CDB_POINTER) - bcopy(csio->cdb_io.cdb_ptr, &req->CDB.CDB32[0], csio->cdb_len); - else - bcopy(csio->cdb_io.cdb_bytes, &req->CDB.CDB32[0],csio->cdb_len); - req->IoFlags = csio->cdb_len; - - /* - * XXX need to handle S/G lists and physical addresses here. - */ - cm->cm_data = csio->data_ptr; - cm->cm_length = csio->dxfer_len; - cm->cm_sge = &req->SGL; - cm->cm_sglsize = (32 - 24) * 4; - cm->cm_desc.SCSIIO.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO; - cm->cm_desc.SCSIIO.DevHandle = targ->handle; - cm->cm_complete = mpssas_scsiio_complete; - cm->cm_complete_data = ccb; - cm->cm_targ = targ; - - callout_reset(&cm->cm_callout, (ccb->ccb_h.timeout * hz) / 1000, - mpssas_scsiio_timeout, cm); - - mps_map_command(sc, cm); - return; -} - -static void -mpssas_scsiio_complete(struct mps_softc *sc, struct mps_command *cm) -{ - MPI2_SCSI_IO_REPLY *rep; - union ccb *ccb; - struct mpssas_softc *sassc; - u_int sense_len; - int dir = 0; - - mps_dprint(sc, MPS_TRACE, "%s\n", __func__); - - callout_stop(&cm->cm_callout); - - sassc = sc->sassc; - ccb = cm->cm_complete_data; - rep = (MPI2_SCSI_IO_REPLY *)cm->cm_reply; - - 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 (sassc->flags & MPSSAS_QUEUE_FROZEN) { - ccb->ccb_h.flags |= CAM_RELEASE_SIMQ; - sassc->flags &= ~MPSSAS_QUEUE_FROZEN; - } - - /* Take the fast path to completion */ - if (cm->cm_reply == NULL) { - ccb->ccb_h.status = CAM_REQ_CMP; - ccb->csio.scsi_status = SCSI_STATUS_OK; - mps_free_command(sc, cm); - xpt_done(ccb); - 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, (u_int)rep->TransferCount); - - 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; - /* 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; - break; - case MPI2_IOCSTATUS_SCSI_INVALID_DEVHANDLE: - case MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE: - ccb->ccb_h.status = CAM_DEV_NOT_THERE; - break; - case MPI2_IOCSTATUS_SCSI_TASK_TERMINATED: - /* - * This is one of the responses that comes back when an I/O - * has been aborted. If it is because of a timeout that we - * initiated, just set the status to CAM_CMD_TIMEOUT. - * Otherwise set it to CAM_REQ_ABORTED. The effect on the - * command is the same (it gets retried, subject to the - * retry counter), the only difference is what gets printed - * on the console. - */ - if (cm->cm_state == MPS_CM_STATE_TIMEDOUT) - ccb->ccb_h.status = CAM_CMD_TIMEOUT; - else - ccb->ccb_h.status = CAM_REQ_ABORTED; - break; - case MPI2_IOCSTATUS_SCSI_IOC_TERMINATED: - case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED: - ccb->ccb_h.status = CAM_REQ_ABORTED; - break; - case MPI2_IOCSTATUS_INVALID_SGL: - mps_print_scsiio_cmd(sc, cm); - ccb->ccb_h.status = CAM_UNREC_HBA_ERROR; - break; - case MPI2_IOCSTATUS_INVALID_FUNCTION: - case MPI2_IOCSTATUS_INTERNAL_ERROR: - case MPI2_IOCSTATUS_INVALID_VPID: - case MPI2_IOCSTATUS_INVALID_FIELD: - case MPI2_IOCSTATUS_INVALID_STATE: - case MPI2_IOCSTATUS_OP_STATE_NOT_SUPPORTED: - case MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR: - case MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR: - case MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: - case MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED: - default: - ccb->ccb_h.status = CAM_REQ_CMP_ERR; - } - - - 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) { - sense_len = MIN(rep->SenseCount, - sizeof(struct scsi_sense_data)); - if (sense_len < rep->SenseCount) - ccb->csio.sense_resid = rep->SenseCount - sense_len; - 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; - - mps_free_command(sc, cm); - xpt_done(ccb); -} - -#if __FreeBSD_version >= 900026 -static void -mpssas_smpio_complete(struct mps_softc *sc, struct mps_command *cm) -{ - MPI2_SMP_PASSTHROUGH_REPLY *rpl; - MPI2_SMP_PASSTHROUGH_REQUEST *req; - uint64_t sasaddr; - union ccb *ccb; - - ccb = cm->cm_complete_data; - rpl = (MPI2_SMP_PASSTHROUGH_REPLY *)cm->cm_reply; - if (rpl == NULL) { - mps_dprint(sc, MPS_INFO, "%s: NULL cm_reply!\n", __func__); - ccb->ccb_h.status = CAM_REQ_CMP_ERR; - goto bailout; - } - - req = (MPI2_SMP_PASSTHROUGH_REQUEST *)cm->cm_req; - sasaddr = le32toh(req->SASAddress.Low); - sasaddr |= ((uint64_t)(le32toh(req->SASAddress.High))) << 32; - - if ((rpl->IOCStatus & MPI2_IOCSTATUS_MASK) != MPI2_IOCSTATUS_SUCCESS || - rpl->SASStatus != MPI2_SASSTATUS_SUCCESS) { - mps_dprint(sc, MPS_INFO, "%s: IOCStatus %04x SASStatus %02x\n", - __func__, rpl->IOCStatus, rpl->SASStatus); - ccb->ccb_h.status = CAM_REQ_CMP_ERR; - goto bailout; - } - - mps_dprint(sc, MPS_INFO, "%s: SMP request to SAS address " - "%#jx completed successfully\n", __func__, - (uintmax_t)sasaddr); - - if (ccb->smpio.smp_response[2] == SMP_FR_ACCEPTED) - ccb->ccb_h.status = CAM_REQ_CMP; - else - ccb->ccb_h.status = CAM_SMP_STATUS_ERROR; - -bailout: - /* - * We sync in both directions because we had DMAs in the S/G list - * in both directions. - */ - bus_dmamap_sync(sc->buffer_dmat, cm->cm_dmamap, - BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); - bus_dmamap_unload(sc->buffer_dmat, cm->cm_dmamap); - mps_free_command(sc, cm); - xpt_done(ccb); -} - -static void -mpssas_send_smpcmd(struct mpssas_softc *sassc, union ccb *ccb, uint64_t sasaddr) -{ - struct mps_command *cm; - uint8_t *request, *response; - MPI2_SMP_PASSTHROUGH_REQUEST *req; - struct mps_softc *sc; - struct sglist *sg; - int error; - - sc = sassc->sc; - sg = NULL; - error = 0; - - /* - * XXX We don't yet support physical addresses here. - */ - if (ccb->ccb_h.flags & (CAM_DATA_PHYS|CAM_SG_LIST_PHYS)) { - mps_printf(sc, "%s: physical addresses not supported\n", - __func__); - ccb->ccb_h.status = CAM_REQ_INVALID; - xpt_done(ccb); - return; - } - - /* - * If the user wants to send an S/G list, check to make sure they - * have single buffers. - */ - if (ccb->ccb_h.flags & CAM_SCATTER_VALID) { - /* - * The chip does not support more than one buffer for the - * request or response. - */ - if ((ccb->smpio.smp_request_sglist_cnt > 1) - || (ccb->smpio.smp_response_sglist_cnt > 1)) { - mps_printf(sc, "%s: multiple request or response " - "buffer segments not supported for SMP\n", - __func__); - ccb->ccb_h.status = CAM_REQ_INVALID; - xpt_done(ccb); - return; - } - - /* - * The CAM_SCATTER_VALID flag was originally implemented - * for the XPT_SCSI_IO CCB, which only has one data pointer. - * We have two. So, just take that flag to mean that we - * might have S/G lists, and look at the S/G segment count - * to figure out whether that is the case for each individual - * buffer. - */ - if (ccb->smpio.smp_request_sglist_cnt != 0) { - bus_dma_segment_t *req_sg; - - req_sg = (bus_dma_segment_t *)ccb->smpio.smp_request; - request = (uint8_t *)req_sg[0].ds_addr; - } else - request = ccb->smpio.smp_request; - - if (ccb->smpio.smp_response_sglist_cnt != 0) { - bus_dma_segment_t *rsp_sg; - - rsp_sg = (bus_dma_segment_t *)ccb->smpio.smp_response; - response = (uint8_t *)rsp_sg[0].ds_addr; - } else - response = ccb->smpio.smp_response; - } else { - request = ccb->smpio.smp_request; - response = ccb->smpio.smp_response; - } - - cm = mps_alloc_command(sc); - if (cm == NULL) { - mps_printf(sc, "%s: cannot allocate command\n", __func__); - ccb->ccb_h.status = CAM_RESRC_UNAVAIL; - xpt_done(ccb); - return; - } - - req = (MPI2_SMP_PASSTHROUGH_REQUEST *)cm->cm_req; - bzero(req, sizeof(*req)); - req->Function = MPI2_FUNCTION_SMP_PASSTHROUGH; - - /* Allow the chip to use any route to this SAS address. */ - req->PhysicalPort = 0xff; - - req->RequestDataLength = ccb->smpio.smp_request_len; - req->SGLFlags = - MPI2_SGLFLAGS_SYSTEM_ADDRESS_SPACE | MPI2_SGLFLAGS_SGL_TYPE_MPI; - - mps_dprint(sc, MPS_INFO, "%s: sending SMP request to SAS " - "address %#jx\n", __func__, (uintmax_t)sasaddr); - - mpi_init_sge(cm, req, &req->SGL); - - /* - * Set up a uio to pass into mps_map_command(). This allows us to - * do one map command, and one busdma call in there. - */ - cm->cm_uio.uio_iov = cm->cm_iovec; - cm->cm_uio.uio_iovcnt = 2; - cm->cm_uio.uio_segflg = UIO_SYSSPACE; - - /* - * The read/write flag isn't used by busdma, but set it just in - * case. This isn't exactly accurate, either, since we're going in - * both directions. - */ - cm->cm_uio.uio_rw = UIO_WRITE; - - cm->cm_iovec[0].iov_base = request; - cm->cm_iovec[0].iov_len = req->RequestDataLength; - cm->cm_iovec[1].iov_base = response; - cm->cm_iovec[1].iov_len = ccb->smpio.smp_response_len; - - cm->cm_uio.uio_resid = cm->cm_iovec[0].iov_len + - cm->cm_iovec[1].iov_len; - - /* - * Trigger a warning message in mps_data_cb() for the user if we - * wind up exceeding two S/G segments. The chip expects one - * segment for the request and another for the response. - */ - cm->cm_max_segs = 2; - - cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; - cm->cm_complete = mpssas_smpio_complete; - cm->cm_complete_data = ccb; - - /* - * Tell the mapping code that we're using a uio, and that this is - * an SMP passthrough request. There is a little special-case - * logic there (in mps_data_cb()) to handle the bidirectional - * transfer. - */ - cm->cm_flags |= MPS_CM_FLAGS_USE_UIO | MPS_CM_FLAGS_SMP_PASS | - MPS_CM_FLAGS_DATAIN | MPS_CM_FLAGS_DATAOUT; - - /* The chip data format is little endian. */ - req->SASAddress.High = htole32(sasaddr >> 32); - req->SASAddress.Low = htole32(sasaddr); - - /* - * XXX Note that we don't have a timeout/abort mechanism here. - * From the manual, it looks like task management requests only - * work for SCSI IO and SATA passthrough requests. We may need to - * have a mechanism to retry requests in the event of a chip reset - * at least. Hopefully the chip will insure that any errors short - * of that are relayed back to the driver. - */ - error = mps_map_command(sc, cm); - if ((error != 0) && (error != EINPROGRESS)) { - mps_printf(sc, "%s: error %d returned from mps_map_command()\n", - __func__, error); - goto bailout_error; - } - - return; - -bailout_error: - mps_free_command(sc, cm); - ccb->ccb_h.status = CAM_RESRC_UNAVAIL; - xpt_done(ccb); - return; - -} - -static void -mpssas_action_smpio(struct mpssas_softc *sassc, union ccb *ccb) -{ - struct mps_softc *sc; - struct mpssas_target *targ; - uint64_t sasaddr = 0; - - sc = sassc->sc; - - /* - * Make sure the target exists. - */ - targ = &sassc->targets[ccb->ccb_h.target_id]; - if (targ->handle == 0x0) { - mps_printf(sc, "%s: target %d does not exist!\n", __func__, - ccb->ccb_h.target_id); - ccb->ccb_h.status = CAM_SEL_TIMEOUT; - xpt_done(ccb); - return; - } - - /* - * If this device has an embedded SMP target, we'll talk to it - * directly. - * figure out what the expander's address is. - */ - if ((targ->devinfo & MPI2_SAS_DEVICE_INFO_SMP_TARGET) != 0) - sasaddr = targ->sasaddr; - - /* - * If we don't have a SAS address for the expander yet, try - * grabbing it from the page 0x83 information cached in the - * transport layer for this target. LSI expanders report the - * expander SAS address as the port-associated SAS address in - * Inquiry VPD page 0x83. Maxim expanders don't report it in page - * 0x83. - * - * XXX KDM disable this for now, but leave it commented out so that - * it is obvious that this is another possible way to get the SAS - * address. - * - * The parent handle method below is a little more reliable, and - * the other benefit is that it works for devices other than SES - * devices. So you can send a SMP request to a da(4) device and it - * will get routed to the expander that device is attached to. - * (Assuming the da(4) device doesn't contain an SMP target...) - */ -#if 0 - if (sasaddr == 0) - sasaddr = xpt_path_sas_addr(ccb->ccb_h.path); -#endif - - /* - * If we still don't have a SAS address for the expander, look for - * the parent device of this device, which is probably the expander. - */ - if (sasaddr == 0) { - struct mpssas_target *parent_target; - - if (targ->parent_handle == 0x0) { - mps_printf(sc, "%s: handle %d does not have a valid " - "parent handle!\n", __func__, targ->handle); - ccb->ccb_h.status = CAM_REQ_INVALID; - goto bailout; - } - parent_target = mpssas_find_target(sassc, 0, - targ->parent_handle); - - if (parent_target == NULL) { - mps_printf(sc, "%s: handle %d does not have a valid " - "parent target!\n", __func__, targ->handle); - ccb->ccb_h.status = CAM_REQ_INVALID; - goto bailout; - } - - if ((parent_target->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, parent_target->handle); - ccb->ccb_h.status = CAM_REQ_INVALID; - goto bailout; - - } - - sasaddr = parent_target->sasaddr; - } - - if (sasaddr == 0) { - mps_printf(sc, "%s: unable to find SAS address for handle %d\n", - __func__, targ->handle); - ccb->ccb_h.status = CAM_REQ_INVALID; - goto bailout; - } - mpssas_send_smpcmd(sassc, ccb, sasaddr); - - return; - -bailout: - xpt_done(ccb); - -} - -#endif /* __FreeBSD_version >= 900026 */ - -static void -mpssas_action_resetdev(struct mpssas_softc *sassc, union ccb *ccb) -{ - struct mps_softc *sc; - struct mps_command *cm; - 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; - } - - cm = mps_alloc_command(sc); - if (cm == NULL) { - mps_printf(sc, "%s: cannot alloc command\n", __func__); - 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; - 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); -} - -static void -mpssas_resetdev_complete(struct mps_softc *sc, struct mps_command *cm) -{ - MPI2_SCSI_TASK_MANAGE_REPLY *resp; - union ccb *ccb; - - mps_dprint(sc, MPS_TRACE, "%s\n", __func__); - - resp = (MPI2_SCSI_TASK_MANAGE_REPLY *)cm->cm_reply; - ccb = cm->cm_complete_data; - - kprintf("resetdev complete IOCStatus= 0x%x ResponseCode= 0x%x\n", - resp->IOCStatus, resp->ResponseCode); - - if (resp->ResponseCode == MPI2_SCSITASKMGMT_RSP_TM_COMPLETE) - ccb->ccb_h.status = CAM_REQ_CMP; - else - ccb->ccb_h.status = CAM_REQ_CMP_ERR; - - mpssas_complete_tm_request(sc, cm, /*free_cm*/ 1); - - xpt_done(ccb); -} - -static void -mpssas_poll(struct cam_sim *sim) -{ - struct mpssas_softc *sassc; - - sassc = cam_sim_softc(sim); - mps_intr_locked(sassc->sc); -} - -static void -mpssas_freeze_device(struct mpssas_softc *sassc, struct mpssas_target *targ) -{ -} - -static void -mpssas_unfreeze_device(struct mpssas_softc *sassc, struct mpssas_target *targ) -{ -} diff --git a/sys/dev/disk/mps/mps_user.c b/sys/dev/disk/mps/mps_user.c deleted file mode 100644 index 920eea7003..0000000000 --- a/sys/dev/disk/mps/mps_user.c +++ /dev/null @@ -1,943 +0,0 @@ -/*- - * Copyright (c) 2008 Yahoo!, Inc. - * All rights reserved. - * Written by: John Baldwin - * - * 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. - * 3. Neither the name of the author nor the names of any co-contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * 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 MPS-Fusion Host Adapter FreeBSD userland interface - * - * $FreeBSD: src/sys/dev/mps/mps_user.c,v 1.9 2010/11/30 22:39:46 ken Exp $ - */ - -#include "opt_compat.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -static d_open_t mps_open; -static d_close_t mps_close; -static d_ioctl_t mps_ioctl_devsw; - -static struct dev_ops mps_ops = { - { "mps", 0, 0 }, - .d_open = mps_open, - .d_close = mps_close, - .d_ioctl = mps_ioctl_devsw, -}; - -typedef int (mps_user_f)(struct mps_command *, struct mps_usr_command *); -static mps_user_f mpi_pre_ioc_facts; -static mps_user_f mpi_pre_port_facts; -static mps_user_f mpi_pre_fw_download; -static mps_user_f mpi_pre_fw_upload; -static mps_user_f mpi_pre_sata_passthrough; -static mps_user_f mpi_pre_smp_passthrough; -static mps_user_f mpi_pre_config; -static mps_user_f mpi_pre_sas_io_unit_control; - -static int mps_user_read_cfg_header(struct mps_softc *, - struct mps_cfg_page_req *); -static int mps_user_read_cfg_page(struct mps_softc *, - struct mps_cfg_page_req *, void *); -static int mps_user_read_extcfg_header(struct mps_softc *, - struct mps_ext_cfg_page_req *); -static int mps_user_read_extcfg_page(struct mps_softc *, - struct mps_ext_cfg_page_req *, void *); -static int mps_user_write_cfg_page(struct mps_softc *, - struct mps_cfg_page_req *, void *); -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 MALLOC_DEFINE(M_MPSUSER, "mps_user", "Buffers for mps(4) ioctls"); - -int -mps_attach_user(struct mps_softc *sc) -{ - int unit; - - unit = device_get_unit(sc->mps_dev); - sc->mps_cdev = make_dev(&mps_ops, unit, UID_ROOT, GID_OPERATOR, 0640, - "mps%d", unit); - if (sc->mps_cdev == NULL) { - return (ENOMEM); - } - sc->mps_cdev->si_drv1 = sc; - return (0); -} - -void -mps_detach_user(struct mps_softc *sc) -{ - - /* XXX: do a purge of pending requests? */ - destroy_dev(sc->mps_cdev); - dev_ops_remove_minor(&mps_ops, device_get_unit(sc->mps_dev)); - -} - -static int -mps_open(struct dev_open_args *ap) -{ - - return (0); -} - -static int -mps_close(struct dev_close_args *ap) -{ - - return (0); -} - -static int -mps_user_read_cfg_header(struct mps_softc *sc, - struct mps_cfg_page_req *page_req) -{ - MPI2_CONFIG_PAGE_HEADER *hdr; - struct mps_config_params params; - int error; - - hdr = ¶ms.hdr.Struct; - params.action = MPI2_CONFIG_ACTION_PAGE_HEADER; - params.page_address = le32toh(page_req->page_address); - hdr->PageVersion = 0; - hdr->PageLength = 0; - hdr->PageNumber = page_req->header.PageNumber; - hdr->PageType = page_req->header.PageType; - params.buffer = NULL; - params.length = 0; - params.callback = NULL; - - if ((error = mps_read_config_page(sc, ¶ms)) != 0) { - /* - * Leave the request. Without resetting the chip, it's - * still owned by it and we'll just get into trouble - * freeing it now. Mark it as abandoned so that if it - * shows up later it can be freed. - */ - mps_printf(sc, "read_cfg_header timed out\n"); - return (ETIMEDOUT); - } - - page_req->ioc_status = htole16(params.status); - if ((page_req->ioc_status & MPI2_IOCSTATUS_MASK) == - MPI2_IOCSTATUS_SUCCESS) { - bcopy(hdr, &page_req->header, sizeof(page_req->header)); - } - - return (0); -} - -static int -mps_user_read_cfg_page(struct mps_softc *sc, struct mps_cfg_page_req *page_req, - void *buf) -{ - MPI2_CONFIG_PAGE_HEADER *reqhdr, *hdr; - struct mps_config_params params; - int error; - - reqhdr = buf; - hdr = ¶ms.hdr.Struct; - hdr->PageVersion = reqhdr->PageVersion; - hdr->PageLength = reqhdr->PageLength; - hdr->PageNumber = reqhdr->PageNumber; - hdr->PageType = reqhdr->PageType & MPI2_CONFIG_PAGETYPE_MASK; - params.action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; - params.page_address = le32toh(page_req->page_address); - params.buffer = buf; - params.length = le32toh(page_req->len); - params.callback = NULL; - - if ((error = mps_read_config_page(sc, ¶ms)) != 0) { - mps_printf(sc, "mps_user_read_cfg_page timed out\n"); - return (ETIMEDOUT); - } - - page_req->ioc_status = htole16(params.status); - return (0); -} - -static int -mps_user_read_extcfg_header(struct mps_softc *sc, - struct mps_ext_cfg_page_req *ext_page_req) -{ - MPI2_CONFIG_EXTENDED_PAGE_HEADER *hdr; - struct mps_config_params params; - int error; - - hdr = ¶ms.hdr.Ext; - params.action = MPI2_CONFIG_ACTION_PAGE_HEADER; - hdr->PageVersion = ext_page_req->header.PageVersion; - hdr->ExtPageLength = 0; - hdr->PageNumber = ext_page_req->header.PageNumber; - hdr->ExtPageType = ext_page_req->header.ExtPageType; - params.page_address = le32toh(ext_page_req->page_address); - if ((error = mps_read_config_page(sc, ¶ms)) != 0) { - /* - * Leave the request. Without resetting the chip, it's - * still owned by it and we'll just get into trouble - * freeing it now. Mark it as abandoned so that if it - * shows up later it can be freed. - */ - mps_printf(sc, "mps_user_read_extcfg_header timed out\n"); - return (ETIMEDOUT); - } - - ext_page_req->ioc_status = htole16(params.status); - if ((ext_page_req->ioc_status & MPI2_IOCSTATUS_MASK) == - MPI2_IOCSTATUS_SUCCESS) { - ext_page_req->header.PageVersion = hdr->PageVersion; - ext_page_req->header.PageNumber = hdr->PageNumber; - ext_page_req->header.PageType = hdr->PageType; - ext_page_req->header.ExtPageLength = hdr->ExtPageLength; - ext_page_req->header.ExtPageType = hdr->ExtPageType; - } - - return (0); -} - -static int -mps_user_read_extcfg_page(struct mps_softc *sc, - struct mps_ext_cfg_page_req *ext_page_req, void *buf) -{ - MPI2_CONFIG_EXTENDED_PAGE_HEADER *reqhdr, *hdr; - struct mps_config_params params; - int error; - - reqhdr = buf; - hdr = ¶ms.hdr.Ext; - params.action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; - params.page_address = le32toh(ext_page_req->page_address); - hdr->PageVersion = reqhdr->PageVersion; - hdr->PageNumber = reqhdr->PageNumber; - hdr->ExtPageType = reqhdr->ExtPageType; - hdr->ExtPageLength = reqhdr->ExtPageLength; - params.buffer = buf; - params.length = le32toh(ext_page_req->len); - params.callback = NULL; - - if ((error = mps_read_config_page(sc, ¶ms)) != 0) { - mps_printf(sc, "mps_user_read_extcfg_page timed out\n"); - return (ETIMEDOUT); - } - - ext_page_req->ioc_status = htole16(params.status); - return (0); -} - -static int -mps_user_write_cfg_page(struct mps_softc *sc, - struct mps_cfg_page_req *page_req, void *buf) -{ - MPI2_CONFIG_PAGE_HEADER *reqhdr, *hdr; - struct mps_config_params params; - u_int hdr_attr; - int error; - - reqhdr = buf; - hdr = ¶ms.hdr.Struct; - hdr_attr = reqhdr->PageType & MPI2_CONFIG_PAGEATTR_MASK; - if (hdr_attr != MPI2_CONFIG_PAGEATTR_CHANGEABLE && - hdr_attr != MPI2_CONFIG_PAGEATTR_PERSISTENT) { - mps_printf(sc, "page type 0x%x not changeable\n", - reqhdr->PageType & MPI2_CONFIG_PAGETYPE_MASK); - return (EINVAL); - } - - /* - * There isn't any point in restoring stripped out attributes - * if you then mask them going down to issue the request. - */ - - hdr->PageVersion = reqhdr->PageVersion; - hdr->PageLength = reqhdr->PageLength; - hdr->PageNumber = reqhdr->PageNumber; - hdr->PageType = reqhdr->PageType; - params.action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT; - params.page_address = le32toh(page_req->page_address); - params.buffer = buf; - params.length = le32toh(page_req->len); - params.callback = NULL; - - if ((error = mps_write_config_page(sc, ¶ms)) != 0) { - mps_printf(sc, "mps_write_cfg_page timed out\n"); - return (ETIMEDOUT); - } - - page_req->ioc_status = htole16(params.status); - return (0); -} - -void -mpi_init_sge(struct mps_command *cm, void *req, void *sge) -{ - int off, space; - - space = (int)cm->cm_sc->facts->IOCRequestFrameSize * 4; - off = (uintptr_t)sge - (uintptr_t)req; - - KASSERT(off < space, ("bad pointers %p %p, off %d, space %d", - req, sge, off, space)); - - cm->cm_sge = sge; - cm->cm_sglsize = space - off; -} - -/* - * Prepare the mps_command for an IOC_FACTS request. - */ -static int -mpi_pre_ioc_facts(struct mps_command *cm, struct mps_usr_command *cmd) -{ - MPI2_IOC_FACTS_REQUEST *req = (void *)cm->cm_req; - MPI2_IOC_FACTS_REPLY *rpl; - - if (cmd->req_len != sizeof *req) - return (EINVAL); - if (cmd->rpl_len != sizeof *rpl) - return (EINVAL); - - cm->cm_sge = NULL; - cm->cm_sglsize = 0; - return (0); -} - -/* - * Prepare the mps_command for a PORT_FACTS request. - */ -static int -mpi_pre_port_facts(struct mps_command *cm, struct mps_usr_command *cmd) -{ - MPI2_PORT_FACTS_REQUEST *req = (void *)cm->cm_req; - MPI2_PORT_FACTS_REPLY *rpl; - - if (cmd->req_len != sizeof *req) - return (EINVAL); - if (cmd->rpl_len != sizeof *rpl) - return (EINVAL); - - cm->cm_sge = NULL; - cm->cm_sglsize = 0; - return (0); -} - -/* - * Prepare the mps_command for a FW_DOWNLOAD request. - */ -static int -mpi_pre_fw_download(struct mps_command *cm, struct mps_usr_command *cmd) -{ - MPI2_FW_DOWNLOAD_REQUEST *req = (void *)cm->cm_req; - MPI2_FW_DOWNLOAD_REPLY *rpl; - MPI2_FW_DOWNLOAD_TCSGE tc; - int error; - - /* - * This code assumes there is room in the request's SGL for - * the TransactionContext plus at least a SGL chain element. - */ - CTASSERT(sizeof req->SGL >= sizeof tc + MPS_SGC_SIZE); - - if (cmd->req_len != sizeof *req) - return (EINVAL); - if (cmd->rpl_len != sizeof *rpl) - return (EINVAL); - - if (cmd->len == 0) - return (EINVAL); - - error = copyin(cmd->buf, cm->cm_data, cmd->len); - if (error != 0) - return (error); - - mpi_init_sge(cm, req, &req->SGL); - bzero(&tc, sizeof tc); - - /* - * For now, the F/W image must be provided in a single request. - */ - if ((req->MsgFlags & MPI2_FW_DOWNLOAD_MSGFLGS_LAST_SEGMENT) == 0) - return (EINVAL); - if (req->TotalImageSize != cmd->len) - return (EINVAL); - - /* - * The value of the first two elements is specified in the - * Fusion-MPT Message Passing Interface document. - */ - tc.ContextSize = 0; - tc.DetailsLength = 12; - tc.ImageOffset = 0; - tc.ImageSize = cmd->len; - - cm->cm_flags |= MPS_CM_FLAGS_DATAOUT; - - return (mps_push_sge(cm, &tc, sizeof tc, 0)); -} - -/* - * Prepare the mps_command for a FW_UPLOAD request. - */ -static int -mpi_pre_fw_upload(struct mps_command *cm, struct mps_usr_command *cmd) -{ - MPI2_FW_UPLOAD_REQUEST *req = (void *)cm->cm_req; - MPI2_FW_UPLOAD_REPLY *rpl; - MPI2_FW_UPLOAD_TCSGE tc; - - /* - * This code assumes there is room in the request's SGL for - * the TransactionContext plus at least a SGL chain element. - */ - CTASSERT(sizeof req->SGL >= sizeof tc + MPS_SGC_SIZE); - - if (cmd->req_len != sizeof *req) - return (EINVAL); - if (cmd->rpl_len != sizeof *rpl) - return (EINVAL); - - mpi_init_sge(cm, req, &req->SGL); - if (cmd->len == 0) { - /* Perhaps just asking what the size of the fw is? */ - return (0); - } - - bzero(&tc, sizeof tc); - - /* - * The value of the first two elements is specified in the - * Fusion-MPT Message Passing Interface document. - */ - tc.ContextSize = 0; - tc.DetailsLength = 12; - /* - * XXX Is there any reason to fetch a partial image? I.e. to - * set ImageOffset to something other than 0? - */ - tc.ImageOffset = 0; - tc.ImageSize = cmd->len; - - return (mps_push_sge(cm, &tc, sizeof tc, 0)); -} - -/* - * Prepare the mps_command for a SATA_PASSTHROUGH request. - */ -static int -mpi_pre_sata_passthrough(struct mps_command *cm, struct mps_usr_command *cmd) -{ - MPI2_SATA_PASSTHROUGH_REQUEST *req = (void *)cm->cm_req; - MPI2_SATA_PASSTHROUGH_REPLY *rpl; - - if (cmd->req_len != sizeof *req) - return (EINVAL); - if (cmd->rpl_len != sizeof *rpl) - return (EINVAL); - - mpi_init_sge(cm, req, &req->SGL); - return (0); -} - -/* - * Prepare the mps_command for a SMP_PASSTHROUGH request. - */ -static int -mpi_pre_smp_passthrough(struct mps_command *cm, struct mps_usr_command *cmd) -{ - MPI2_SMP_PASSTHROUGH_REQUEST *req = (void *)cm->cm_req; - MPI2_SMP_PASSTHROUGH_REPLY *rpl; - - if (cmd->req_len != sizeof *req) - return (EINVAL); - if (cmd->rpl_len != sizeof *rpl) - return (EINVAL); - - mpi_init_sge(cm, req, &req->SGL); - return (0); -} - -/* - * Prepare the mps_command for a CONFIG request. - */ -static int -mpi_pre_config(struct mps_command *cm, struct mps_usr_command *cmd) -{ - MPI2_CONFIG_REQUEST *req = (void *)cm->cm_req; - MPI2_CONFIG_REPLY *rpl; - - if (cmd->req_len != sizeof *req) - return (EINVAL); - if (cmd->rpl_len != sizeof *rpl) - return (EINVAL); - - mpi_init_sge(cm, req, &req->PageBufferSGE); - return (0); -} - -/* - * Prepare the mps_command for a SAS_IO_UNIT_CONTROL request. - */ -static int -mpi_pre_sas_io_unit_control(struct mps_command *cm, - struct mps_usr_command *cmd) -{ - - cm->cm_sge = NULL; - cm->cm_sglsize = 0; - return (0); -} - -/* - * A set of functions to prepare an mps_command for the various - * supported requests. - */ -struct mps_user_func { - U8 Function; - mps_user_f *f_pre; -} mps_user_func_list[] = { - { MPI2_FUNCTION_IOC_FACTS, mpi_pre_ioc_facts }, - { MPI2_FUNCTION_PORT_FACTS, mpi_pre_port_facts }, - { MPI2_FUNCTION_FW_DOWNLOAD, mpi_pre_fw_download }, - { MPI2_FUNCTION_FW_UPLOAD, mpi_pre_fw_upload }, - { MPI2_FUNCTION_SATA_PASSTHROUGH, mpi_pre_sata_passthrough }, - { MPI2_FUNCTION_SMP_PASSTHROUGH, mpi_pre_smp_passthrough}, - { MPI2_FUNCTION_CONFIG, mpi_pre_config}, - { MPI2_FUNCTION_SAS_IO_UNIT_CONTROL, mpi_pre_sas_io_unit_control }, - { 0xFF, NULL } /* list end */ -}; - -static int -mps_user_setup_request(struct mps_command *cm, struct mps_usr_command *cmd) -{ - MPI2_REQUEST_HEADER *hdr = (MPI2_REQUEST_HEADER *)cm->cm_req; - struct mps_user_func *f; - - for (f = mps_user_func_list; f->f_pre != NULL; f++) { - if (hdr->Function == f->Function) - return (f->f_pre(cm, cmd)); - } - return (EINVAL); -} - -static int -mps_user_command(struct mps_softc *sc, struct mps_usr_command *cmd) -{ - MPI2_REQUEST_HEADER *hdr; - MPI2_DEFAULT_REPLY *rpl; - void *buf = NULL; - struct mps_command *cm = NULL; - int err = 0; - int sz; - - mps_lock(sc); - cm = mps_alloc_command(sc); - - if (cm == NULL) { - mps_printf(sc, "mps_user_command: no mps requests\n"); - err = ENOMEM; - goto Ret; - } - mps_unlock(sc); - - hdr = (MPI2_REQUEST_HEADER *)cm->cm_req; - - mps_dprint(sc, MPS_INFO, "mps_user_command: req %p %d rpl %p %d\n", - cmd->req, cmd->req_len, cmd->rpl, cmd->rpl_len ); - - if (cmd->req_len > (int)sc->facts->IOCRequestFrameSize * 4) { - err = EINVAL; - goto RetFreeUnlocked; - } - err = copyin(cmd->req, hdr, cmd->req_len); - if (err != 0) - goto RetFreeUnlocked; - - mps_dprint(sc, MPS_INFO, "mps_user_command: Function %02X " - "MsgFlags %02X\n", hdr->Function, hdr->MsgFlags ); - - err = mps_user_setup_request(cm, cmd); - if (err != 0) { - mps_printf(sc, "mps_user_command: unsupported function 0x%X\n", - hdr->Function ); - goto RetFreeUnlocked; - } - - if (cmd->len > 0) { - buf = kmalloc(cmd->len, M_MPSUSER, M_WAITOK|M_ZERO); - cm->cm_data = buf; - cm->cm_length = cmd->len; - } else { - cm->cm_data = NULL; - cm->cm_length = 0; - } - - cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE | MPS_CM_FLAGS_WAKEUP; - cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; - - mps_lock(sc); - err = mps_map_command(sc, cm); - - if (err != 0 && err != EINPROGRESS) { - mps_printf(sc, "%s: invalid request: error %d\n", - __func__, err); - goto Ret; - } - lksleep(cm, &sc->mps_lock, 0, "mpsuser", 0); - - rpl = (MPI2_DEFAULT_REPLY *)cm->cm_reply; - sz = rpl->MsgLength * 4; - - if (sz > cmd->rpl_len) { - mps_printf(sc, - "mps_user_command: reply buffer too small %d required %d\n", - cmd->rpl_len, sz ); - err = EINVAL; - sz = cmd->rpl_len; - } - - mps_unlock(sc); - copyout(rpl, cmd->rpl, sz); - if (buf != NULL) - copyout(buf, cmd->buf, cmd->len); - mps_dprint(sc, MPS_INFO, "mps_user_command: reply size %d\n", sz ); - -RetFreeUnlocked: - mps_lock(sc); - if (cm != NULL) - mps_free_command(sc, cm); -Ret: - mps_unlock(sc); - if (buf != NULL) - kfree(buf, M_MPSUSER); - return (err); -} - -static int -mps_ioctl(struct cdev *dev, u_long cmd, void *arg, int flag) -{ - struct mps_softc *sc; - struct mps_cfg_page_req *page_req; - struct mps_ext_cfg_page_req *ext_page_req; - void *mps_page; - int error; - - mps_page = NULL; - sc = dev->si_drv1; - page_req = (void *)arg; - ext_page_req = (void *)arg; - - switch (cmd) { - case MPSIO_READ_CFG_HEADER: - mps_lock(sc); - error = mps_user_read_cfg_header(sc, page_req); - mps_unlock(sc); - break; - case MPSIO_READ_CFG_PAGE: - mps_page = kmalloc(page_req->len, M_MPSUSER, M_WAITOK | M_ZERO); - error = copyin(page_req->buf, mps_page, - sizeof(MPI2_CONFIG_PAGE_HEADER)); - if (error) - break; - mps_lock(sc); - error = mps_user_read_cfg_page(sc, page_req, mps_page); - mps_unlock(sc); - if (error) - break; - error = copyout(mps_page, page_req->buf, page_req->len); - break; - case MPSIO_READ_EXT_CFG_HEADER: - mps_lock(sc); - error = mps_user_read_extcfg_header(sc, ext_page_req); - mps_unlock(sc); - break; - case MPSIO_READ_EXT_CFG_PAGE: - mps_page = kmalloc(ext_page_req->len, M_MPSUSER, M_WAITOK|M_ZERO); - error = copyin(ext_page_req->buf, mps_page, - sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER)); - if (error) - break; - mps_lock(sc); - error = mps_user_read_extcfg_page(sc, ext_page_req, mps_page); - mps_unlock(sc); - if (error) - break; - error = copyout(mps_page, ext_page_req->buf, ext_page_req->len); - break; - case MPSIO_WRITE_CFG_PAGE: - mps_page = kmalloc(page_req->len, M_MPSUSER, M_WAITOK|M_ZERO); - error = copyin(page_req->buf, mps_page, page_req->len); - if (error) - break; - mps_lock(sc); - error = mps_user_write_cfg_page(sc, page_req, mps_page); - mps_unlock(sc); - break; - case MPSIO_MPS_COMMAND: - error = mps_user_command(sc, (struct mps_usr_command *)arg); - break; - default: - error = ENOIOCTL; - break; - } - - if (mps_page != NULL) - kfree(mps_page, M_MPSUSER); - - return (error); -} - -#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; - uint32_t buf; - int len; - uint16_t ioc_status; -}; - -struct mps_ext_cfg_page_req32 { - MPI2_CONFIG_EXTENDED_PAGE_HEADER header; - uint32_t page_address; - uint32_t buf; - int len; - uint16_t ioc_status; -}; - -struct mps_raid_action32 { - uint8_t action; - uint8_t volume_bus; - uint8_t volume_id; - uint8_t phys_disk_num; - uint32_t action_data_word; - uint32_t buf; - int len; - uint32_t volume_status; - uint32_t action_data[4]; - uint16_t action_status; - uint16_t ioc_status; - uint8_t write; -}; - -struct mps_usr_command32 { - uint32_t req; - uint32_t req_len; - uint32_t rpl; - uint32_t rpl_len; - uint32_t buf; - int len; - uint32_t flags; -}; - -#define MPSIO_READ_CFG_HEADER32 _IOWR('M', 200, struct mps_cfg_page_req32) -#define MPSIO_READ_CFG_PAGE32 _IOWR('M', 201, struct mps_cfg_page_req32) -#define MPSIO_READ_EXT_CFG_HEADER32 _IOWR('M', 202, struct mps_ext_cfg_page_req32) -#define MPSIO_READ_EXT_CFG_PAGE32 _IOWR('M', 203, struct mps_ext_cfg_page_req32) -#define MPSIO_WRITE_CFG_PAGE32 _IOWR('M', 204, struct mps_cfg_page_req32) -#define MPSIO_RAID_ACTION32 _IOWR('M', 205, struct mps_raid_action32) -#define MPSIO_MPS_COMMAND32 _IOWR('M', 210, struct mps_usr_command32) - -static int -mps_ioctl32(struct cdev *dev, u_long cmd32, void *_arg, int flag, - struct thread *td) -{ - struct mps_cfg_page_req32 *page32 = _arg; - struct mps_ext_cfg_page_req32 *ext32 = _arg; - struct mps_raid_action32 *raid32 = _arg; - struct mps_usr_command32 *user32 = _arg; - union { - struct mps_cfg_page_req page; - struct mps_ext_cfg_page_req ext; - struct mps_raid_action raid; - struct mps_usr_command user; - } arg; - u_long cmd; - int error; - - switch (cmd32) { - case MPSIO_READ_CFG_HEADER32: - case MPSIO_READ_CFG_PAGE32: - case MPSIO_WRITE_CFG_PAGE32: - if (cmd32 == MPSIO_READ_CFG_HEADER32) - cmd = MPSIO_READ_CFG_HEADER; - else if (cmd32 == MPSIO_READ_CFG_PAGE32) - cmd = MPSIO_READ_CFG_PAGE; - else - cmd = MPSIO_WRITE_CFG_PAGE; - CP(*page32, arg.page, header); - CP(*page32, arg.page, page_address); - PTRIN_CP(*page32, arg.page, buf); - CP(*page32, arg.page, len); - CP(*page32, arg.page, ioc_status); - break; - - case MPSIO_READ_EXT_CFG_HEADER32: - case MPSIO_READ_EXT_CFG_PAGE32: - if (cmd32 == MPSIO_READ_EXT_CFG_HEADER32) - cmd = MPSIO_READ_EXT_CFG_HEADER; - else - cmd = MPSIO_READ_EXT_CFG_PAGE; - CP(*ext32, arg.ext, header); - CP(*ext32, arg.ext, page_address); - PTRIN_CP(*ext32, arg.ext, buf); - CP(*ext32, arg.ext, len); - CP(*ext32, arg.ext, ioc_status); - break; - - case MPSIO_RAID_ACTION32: - cmd = MPSIO_RAID_ACTION; - CP(*raid32, arg.raid, action); - CP(*raid32, arg.raid, volume_bus); - CP(*raid32, arg.raid, volume_id); - CP(*raid32, arg.raid, phys_disk_num); - CP(*raid32, arg.raid, action_data_word); - PTRIN_CP(*raid32, arg.raid, buf); - CP(*raid32, arg.raid, len); - CP(*raid32, arg.raid, volume_status); - bcopy(raid32->action_data, arg.raid.action_data, - sizeof arg.raid.action_data); - CP(*raid32, arg.raid, ioc_status); - CP(*raid32, arg.raid, write); - break; - - case MPSIO_MPS_COMMAND32: - cmd = MPSIO_MPS_COMMAND; - PTRIN_CP(*user32, arg.user, req); - CP(*user32, arg.user, req_len); - PTRIN_CP(*user32, arg.user, rpl); - CP(*user32, arg.user, rpl_len); - PTRIN_CP(*user32, arg.user, buf); - CP(*user32, arg.user, len); - CP(*user32, arg.user, flags); - break; - default: - return (ENOIOCTL); - } - - error = mps_ioctl(dev, cmd, &arg, flag, td); - if (error == 0 && (cmd32 & IOC_OUT) != 0) { - switch (cmd32) { - case MPSIO_READ_CFG_HEADER32: - case MPSIO_READ_CFG_PAGE32: - case MPSIO_WRITE_CFG_PAGE32: - CP(arg.page, *page32, header); - CP(arg.page, *page32, page_address); - PTROUT_CP(arg.page, *page32, buf); - CP(arg.page, *page32, len); - CP(arg.page, *page32, ioc_status); - break; - - case MPSIO_READ_EXT_CFG_HEADER32: - case MPSIO_READ_EXT_CFG_PAGE32: - CP(arg.ext, *ext32, header); - CP(arg.ext, *ext32, page_address); - PTROUT_CP(arg.ext, *ext32, buf); - CP(arg.ext, *ext32, len); - CP(arg.ext, *ext32, ioc_status); - break; - - case MPSIO_RAID_ACTION32: - CP(arg.raid, *raid32, action); - CP(arg.raid, *raid32, volume_bus); - CP(arg.raid, *raid32, volume_id); - CP(arg.raid, *raid32, phys_disk_num); - CP(arg.raid, *raid32, action_data_word); - PTROUT_CP(arg.raid, *raid32, buf); - CP(arg.raid, *raid32, len); - CP(arg.raid, *raid32, volume_status); - bcopy(arg.raid.action_data, raid32->action_data, - sizeof arg.raid.action_data); - CP(arg.raid, *raid32, ioc_status); - CP(arg.raid, *raid32, write); - break; - - case MPSIO_MPS_COMMAND32: - PTROUT_CP(arg.user, *user32, req); - CP(arg.user, *user32, req_len); - PTROUT_CP(arg.user, *user32, rpl); - CP(arg.user, *user32, rpl_len); - PTROUT_CP(arg.user, *user32, buf); - CP(arg.user, *user32, len); - CP(arg.user, *user32, flags); - break; - } - } - - return (error); -} -#endif /* COMPAT_FREEBSD32 */ - -static int -mps_ioctl_devsw(struct dev_ioctl_args *ap) -{ - cdev_t dev = ap->a_head.a_dev; - u_long com = ap->a_cmd; - caddr_t arg = ap->a_data; - int flag = ap->a_fflag; - -#ifdef COMPAT_FREEBSD32 - if (SV_CURPROC_FLAG(SV_ILP32)) - return (mps_ioctl32(dev, com, arg, flag, td)); -#endif - return (mps_ioctl(dev, com, arg, flag)); -} diff --git a/sys/dev/disk/mps/mpsvar.h b/sys/dev/disk/mps/mpsvar.h deleted file mode 100644 index b7461a35f8..0000000000 --- a/sys/dev/disk/mps/mpsvar.h +++ /dev/null @@ -1,389 +0,0 @@ -/*- - * Copyright (c) 2009 Yahoo! Inc. - * 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. - * - * $FreeBSD: src/sys/dev/mps/mpsvar.h,v 1.5 2010/12/10 21:45:10 ken Exp $ - */ - -#ifndef _MPSVAR_H -#define _MPSVAR_H - -#define MPS_DB_MAX_WAIT 2500 - -#define MPS_REQ_FRAMES 1024 -#define MPS_EVT_REPLY_FRAMES 32 -#define MPS_REPLY_FRAMES MPS_REQ_FRAMES -#define MPS_CHAIN_FRAMES 1024 -#define MPS_SENSE_LEN SSD_FULL_SIZE -#define MPS_MSI_COUNT 1 -#define MPS_SGE64_SIZE 12 -#define MPS_SGE32_SIZE 8 -#define MPS_SGC_SIZE 8 - -#define MPS_PERIODIC_DELAY 1 /* 1 second heartbeat/watchdog check */ - -struct mps_softc; -struct mps_command; -struct mpssas_softc; -struct mpssas_target; - -MALLOC_DECLARE(M_MPT2); - -typedef void mps_evt_callback_t(struct mps_softc *, uintptr_t, - MPI2_EVENT_NOTIFICATION_REPLY *reply); -typedef void mps_command_callback_t(struct mps_softc *, struct mps_command *cm); - -struct mps_chain { - TAILQ_ENTRY(mps_chain) chain_link; - MPI2_SGE_IO_UNION *chain; - uint32_t chain_busaddr; -}; - -/* - * This needs to be at least 2 to support SMP passthrough. - */ -#define MPS_IOVEC_COUNT 2 - -struct mps_command { - TAILQ_ENTRY(mps_command) cm_link; - struct mps_softc *cm_sc; - void *cm_data; - u_int cm_length; - struct uio cm_uio; - struct iovec cm_iovec[MPS_IOVEC_COUNT]; - u_int cm_max_segs; - u_int cm_sglsize; - MPI2_SGE_IO_UNION *cm_sge; - uint8_t *cm_req; - uint8_t *cm_reply; - uint32_t cm_reply_data; - mps_command_callback_t *cm_complete; - void *cm_complete_data; - struct mpssas_target *cm_targ; - MPI2_REQUEST_DESCRIPTOR_UNION cm_desc; - u_int cm_flags; -#define MPS_CM_FLAGS_POLLED (1 << 0) -#define MPS_CM_FLAGS_COMPLETE (1 << 1) -#define MPS_CM_FLAGS_SGE_SIMPLE (1 << 2) -#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_USE_UIO (1 << 7) -#define MPS_CM_FLAGS_SMP_PASS (1 << 8) - u_int cm_state; -#define MPS_CM_STATE_FREE 0 -#define MPS_CM_STATE_BUSY 1 -#define MPS_CM_STATE_TIMEDOUT 2 - bus_dmamap_t cm_dmamap; - struct scsi_sense_data *cm_sense; - TAILQ_HEAD(, mps_chain) cm_chain_list; - uint32_t cm_req_busaddr; - uint32_t cm_sense_busaddr; - struct callout cm_callout; -}; - -struct mps_event_handle { - TAILQ_ENTRY(mps_event_handle) eh_list; - mps_evt_callback_t *callback; - void *data; - uint8_t mask[16]; -}; - -struct mps_softc { - device_t mps_dev; - struct cdev *mps_cdev; - u_int mps_flags; -#define MPS_FLAGS_INTX (1 << 0) -#define MPS_FLAGS_MSI (1 << 1) -#define MPS_FLAGS_BUSY (1 << 2) -#define MPS_FLAGS_SHUTDOWN (1 << 3) - u_int mps_debug; - u_int allow_multiple_tm_cmds; - int tm_cmds_active; - struct sysctl_ctx_list sysctl_ctx; - struct sysctl_oid *sysctl_tree; - struct mps_command *commands; - struct mps_chain *chains; - struct callout periodic; - - struct mpssas_softc *sassc; - - TAILQ_HEAD(, mps_command) req_list; - TAILQ_HEAD(, mps_chain) chain_list; - TAILQ_HEAD(, mps_command) tm_list; - int replypostindex; - int replyfreeindex; - - struct resource *mps_regs_resource; - bus_space_handle_t mps_bhandle; - bus_space_tag_t mps_btag; - int mps_regs_rid; - - bus_dma_tag_t mps_parent_dmat; - bus_dma_tag_t buffer_dmat; - - MPI2_IOC_FACTS_REPLY *facts; - MPI2_PORT_FACTS_REPLY *pfacts; - int num_reqs; - int num_replies; - int fqdepth; /* Free queue */ - int pqdepth; /* Post queue */ - - uint8_t event_mask[16]; - TAILQ_HEAD(, mps_event_handle) event_list; - struct mps_event_handle *mps_log_eh; - - struct lock mps_lock; - struct intr_config_hook mps_ich; - struct resource *mps_irq[MPS_MSI_COUNT]; - void *mps_intrhand[MPS_MSI_COUNT]; - int mps_irq_rid[MPS_MSI_COUNT]; - - uint8_t *req_frames; - bus_addr_t req_busaddr; - bus_dma_tag_t req_dmat; - bus_dmamap_t req_map; - - uint8_t *reply_frames; - bus_addr_t reply_busaddr; - bus_dma_tag_t reply_dmat; - bus_dmamap_t reply_map; - - struct scsi_sense_data *sense_frames; - bus_addr_t sense_busaddr; - bus_dma_tag_t sense_dmat; - bus_dmamap_t sense_map; - - uint8_t *chain_frames; - bus_addr_t chain_busaddr; - bus_dma_tag_t chain_dmat; - bus_dmamap_t chain_map; - - MPI2_REPLY_DESCRIPTORS_UNION *post_queue; - bus_addr_t post_busaddr; - uint32_t *free_queue; - bus_addr_t free_busaddr; - bus_dma_tag_t queues_dmat; - bus_dmamap_t queues_map; -}; - -struct mps_config_params { - MPI2_CONFIG_EXT_PAGE_HEADER_UNION hdr; - u_int action; - u_int page_address; /* Attributes, not a phys address */ - u_int status; - void *buffer; - u_int length; - int timeout; - void (*callback)(struct mps_softc *, struct mps_config_params *); - void *cbdata; -}; - -static __inline uint32_t -mps_regread(struct mps_softc *sc, uint32_t offset) -{ - return (bus_space_read_4(sc->mps_btag, sc->mps_bhandle, offset)); -} - -static __inline void -mps_regwrite(struct mps_softc *sc, uint32_t offset, uint32_t val) -{ - bus_space_write_4(sc->mps_btag, sc->mps_bhandle, offset, 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; - mps_regwrite(sc, MPI2_REPLY_FREE_HOST_INDEX_OFFSET, sc->replyfreeindex); -} - -static __inline struct mps_chain * -mps_alloc_chain(struct mps_softc *sc) -{ - struct mps_chain *chain; - - if ((chain = TAILQ_FIRST(&sc->chain_list)) != NULL) - TAILQ_REMOVE(&sc->chain_list, chain, chain_link); - return (chain); -} - -static __inline void -mps_free_chain(struct mps_softc *sc, struct mps_chain *chain) -{ -#if 0 - bzero(chain->chain, 128); -#endif - TAILQ_INSERT_TAIL(&sc->chain_list, chain, chain_link); -} - -static __inline void -mps_free_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_targ = 0; - cm->cm_max_segs = 0; - cm->cm_state = MPS_CM_STATE_FREE; - TAILQ_FOREACH_MUTABLE(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->req_list, cm, cm_link); -} - -static __inline struct mps_command * -mps_alloc_command(struct mps_softc *sc) -{ - struct mps_command *cm; - - cm = TAILQ_FIRST(&sc->req_list); - if (cm == NULL) - return (NULL); - - TAILQ_REMOVE(&sc->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) -{ - lockmgr(&sc->mps_lock, LK_EXCLUSIVE); -} - -static __inline void -mps_unlock(struct mps_softc *sc) -{ - lockmgr(&sc->mps_lock, LK_RELEASE); -} - -#define MPS_INFO (1 << 0) -#define MPS_TRACE (1 << 1) -#define MPS_FAULT (1 << 2) -#define MPS_EVENT (1 << 3) -#define MPS_LOG (1 << 4) - -#define mps_printf(sc, args...) \ - device_printf((sc)->mps_dev, ##args) - -#define mps_dprint(sc, level, msg, args...) \ -do { \ - if (sc->mps_debug & level) \ - device_printf(sc->mps_dev, msg, ##args); \ -} while (0) - -#define mps_dprint_field(sc, level, msg, args...) \ -do { \ - if (sc->mps_debug & level) \ - kprintf("\t" msg, ##args); \ -} while (0) - -#define MPS_PRINTFIELD_START(sc, tag...) \ - mps_dprint((sc), MPS_INFO, ##tag); \ - mps_dprint_field((sc), MPS_INFO, ":\n") -#define MPS_PRINTFIELD_END(sc, tag) \ - mps_dprint((sc), MPS_INFO, tag "\n") -#define MPS_PRINTFIELD(sc, facts, attr, fmt) \ - mps_dprint_field((sc), MPS_INFO, #attr ": " #fmt "\n", (facts)->attr) - -#define MPS_EVENTFIELD_START(sc, tag...) \ - mps_dprint((sc), MPS_EVENT, ##tag); \ - mps_dprint_field((sc), MPS_EVENT, ":\n") -#define MPS_EVENTFIELD(sc, facts, attr, fmt) \ - mps_dprint_field((sc), MPS_EVENT, #attr ": " #fmt "\n", (facts)->attr) - -static __inline void -mps_from_u64(uint64_t data, U64 *mps) -{ - (mps)->High = (uint32_t)((data) >> 32); - (mps)->Low = (uint32_t)((data) & 0xffffffff); -} - -static __inline uint64_t -mps_to_u64(U64 *data) -{ - - return (((uint64_t)data->High << 32) | data->Low); -} - -static __inline void -mps_mask_intr(struct mps_softc *sc) -{ - uint32_t mask; - - mask = mps_regread(sc, MPI2_HOST_INTERRUPT_MASK_OFFSET); - mask |= MPI2_HIM_REPLY_INT_MASK; - mps_regwrite(sc, MPI2_HOST_INTERRUPT_MASK_OFFSET, mask); -} - -static __inline void -mps_unmask_intr(struct mps_softc *sc) -{ - uint32_t mask; - - mask = mps_regread(sc, MPI2_HOST_INTERRUPT_MASK_OFFSET); - mask &= ~MPI2_HIM_REPLY_INT_MASK; - mps_regwrite(sc, MPI2_HOST_INTERRUPT_MASK_OFFSET, mask); -} - -int mps_pci_setup_interrupts(struct mps_softc *); -int mps_attach(struct mps_softc *sc); -int mps_free(struct mps_softc *sc); -void mps_intr(void *); -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_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 *); - -SYSCTL_DECL(_hw_mps); - -#endif diff --git a/sys/dev/raid/Makefile b/sys/dev/raid/Makefile index 3ce204ec00..44461cdce9 100644 --- a/sys/dev/raid/Makefile +++ b/sys/dev/raid/Makefile @@ -1,4 +1,4 @@ SUBDIR= aac amr arcmsr asr ciss hptiop hptmv \ - iir ips mfi mlx mly pst twa twe tws vinum + iir ips mfi mlx mly mps pst twa twe tws vinum .include diff --git a/sys/dev/raid/mps/Makefile b/sys/dev/raid/mps/Makefile new file mode 100644 index 0000000000..57e8bb69f7 --- /dev/null +++ b/sys/dev/raid/mps/Makefile @@ -0,0 +1,14 @@ +# $FreeBSD: src/sys/modules/mps/Makefile,v 1.4 2012/01/26 18:17:21 ken Exp $ + +.PATH: ${.CURDIR}/../../dev/mps + +KMOD= mps +SRCS= mps_pci.c mps.c mps_sas.c mps_table.c mps_user.c +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_FLAGS += -g + +.include diff --git a/sys/dev/disk/mps/mpi/mpi2.h b/sys/dev/raid/mps/mpi/mpi2.h similarity index 94% rename from sys/dev/disk/mps/mpi/mpi2.h rename to sys/dev/raid/mps/mpi/mpi2.h index e33623438c..4196be76fc 100644 --- a/sys/dev/disk/mps/mpi/mpi2.h +++ b/sys/dev/raid/mps/mpi/mpi2.h @@ -1,6 +1,35 @@ -/* $FreeBSD: src/sys/dev/mps/mpi/mpi2.h,v 1.1 2010/09/10 15:03:56 ken Exp $ */ +/*- + * 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: src/sys/dev/mps/mpi/mpi2.h,v 1.2 2012/01/26 18:17:21 ken Exp $ + */ + /* - * 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/disk/mps/mpi/mpi2_cnfg.h b/sys/dev/raid/mps/mpi/mpi2_cnfg.h similarity index 90% rename from sys/dev/disk/mps/mpi/mpi2_cnfg.h rename to sys/dev/raid/mps/mpi/mpi2_cnfg.h index 7c7ba2f2dc..fc0f6d84e8 100644 --- a/sys/dev/disk/mps/mpi/mpi2_cnfg.h +++ b/sys/dev/raid/mps/mpi/mpi2_cnfg.h @@ -1,13 +1,42 @@ -/* $FreeBSD: src/sys/dev/mps/mpi/mpi2_cnfg.h,v 1.1 2010/09/10 15:03:56 ken Exp $ */ +/*- + * 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: src/sys/dev/mps/mpi/mpi2_cnfg.h,v 1.2 2012/01/26 18:17:21 ken Exp $ + */ + /* - * 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,4 +2863,24 @@ 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/disk/mps/mpi/mpi2_hbd.h b/sys/dev/raid/mps/mpi/mpi2_hbd.h similarity index 72% rename from sys/dev/disk/mps/mpi/mpi2_hbd.h rename to sys/dev/raid/mps/mpi/mpi2_hbd.h index c2d81e76cf..41ac3fb0f3 100644 --- a/sys/dev/disk/mps/mpi/mpi2_hbd.h +++ b/sys/dev/raid/mps/mpi/mpi2_hbd.h @@ -1,13 +1,42 @@ -/* $FreeBSD: src/sys/dev/mps/mpi/mpi2_hbd.h,v 1.1 2010/09/10 15:03:56 ken Exp $ */ +/*- + * 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: src/sys/dev/mps/mpi/mpi2_hbd.h,v 1.2 2012/01/26 18:17:21 ken Exp $ + */ + /* - * 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/disk/mps/mpi/mpi2_history.txt b/sys/dev/raid/mps/mpi/mpi2_history.txt similarity index 75% rename from sys/dev/disk/mps/mpi/mpi2_history.txt rename to sys/dev/raid/mps/mpi/mpi2_history.txt index 6247f920c3..a87375fa4e 100644 --- a/sys/dev/disk/mps/mpi/mpi2_history.txt +++ b/sys/dev/raid/mps/mpi/mpi2_history.txt @@ -1,29 +1,58 @@ -/* $FreeBSD: src/sys/dev/mps/mpi/mpi2_history.txt,v 1.1 2010/09/10 15:03:56 ken Exp $ */ +/*- + * 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: src/sys/dev/mps/mpi/mpi2_history.txt,v 1.2 2012/01/26 18:17:21 ken Exp $ + */ + ============================== 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/disk/mps/mpi/mpi2_init.h b/sys/dev/raid/mps/mpi/mpi2_init.h similarity index 91% rename from sys/dev/disk/mps/mpi/mpi2_init.h rename to sys/dev/raid/mps/mpi/mpi2_init.h index 7d0c43547b..4c6ec98129 100644 --- a/sys/dev/disk/mps/mpi/mpi2_init.h +++ b/sys/dev/raid/mps/mpi/mpi2_init.h @@ -1,13 +1,42 @@ -/* $FreeBSD: src/sys/dev/mps/mpi/mpi2_init.h,v 1.1 2010/09/10 15:03:56 ken Exp $ */ +/*- + * 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: src/sys/dev/mps/mpi/mpi2_init.h,v 1.2 2012/01/26 18:17:21 ken Exp $ + */ + /* - * 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/disk/mps/mpi/mpi2_ioc.h b/sys/dev/raid/mps/mpi/mpi2_ioc.h similarity index 89% rename from sys/dev/disk/mps/mpi/mpi2_ioc.h rename to sys/dev/raid/mps/mpi/mpi2_ioc.h index c25b2f4e87..7dd0b36e2e 100644 --- a/sys/dev/disk/mps/mpi/mpi2_ioc.h +++ b/sys/dev/raid/mps/mpi/mpi2_ioc.h @@ -1,13 +1,42 @@ -/* $FreeBSD: src/sys/dev/mps/mpi/mpi2_ioc.h,v 1.1 2010/09/10 15:03:56 ken Exp $ */ +/*- + * 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: src/sys/dev/mps/mpi/mpi2_ioc.h,v 1.2 2012/01/26 18:17:21 ken Exp $ + */ + /* - * 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,4 +1466,100 @@ typedef struct _MPI2_INIT_IMAGE_FOOTER #define MPI2_INIT_IMAGE_RESETVECTOR_OFFSET (0x14) +/**************************************************************************** +* PowerManagementControl message +****************************************************************************/ + +/* PowerManagementControl Request message */ +typedef struct _MPI2_PWR_MGMT_CONTROL_REQUEST +{ + U8 Feature; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U8 ChainOffset; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 Reserved2; /* 0x04 */ + U8 Reserved3; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved4; /* 0x0A */ + U8 Parameter1; /* 0x0C */ + U8 Parameter2; /* 0x0D */ + U8 Parameter3; /* 0x0E */ + U8 Parameter4; /* 0x0F */ + U32 Reserved5; /* 0x10 */ + U32 Reserved6; /* 0x14 */ +} MPI2_PWR_MGMT_CONTROL_REQUEST, MPI2_POINTER PTR_MPI2_PWR_MGMT_CONTROL_REQUEST, + Mpi2PwrMgmtControlRequest_t, MPI2_POINTER pMpi2PwrMgmtControlRequest_t; + +/* defines for the Feature field */ +#define MPI2_PM_CONTROL_FEATURE_DA_PHY_POWER_COND (0x01) +#define MPI2_PM_CONTROL_FEATURE_PORT_WIDTH_MODULATION (0x02) +#define MPI2_PM_CONTROL_FEATURE_PCIE_LINK (0x03) +#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/disk/mps/mpi/mpi2_ra.h b/sys/dev/raid/mps/mpi/mpi2_ra.h similarity index 71% rename from sys/dev/disk/mps/mpi/mpi2_ra.h rename to sys/dev/raid/mps/mpi/mpi2_ra.h index 859925dcf7..4d9e130257 100644 --- a/sys/dev/disk/mps/mpi/mpi2_ra.h +++ b/sys/dev/raid/mps/mpi/mpi2_ra.h @@ -1,6 +1,35 @@ -/* $FreeBSD: src/sys/dev/mps/mpi/mpi2_ra.h,v 1.1 2010/09/10 15:03:56 ken Exp $ */ +/*- + * 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: src/sys/dev/mps/mpi/mpi2_ra.h,v 1.2 2012/01/26 18:17:21 ken Exp $ + */ + /* - * Copyright (c) 2009 LSI Corporation. + * Copyright (c) 2011 LSI Corporation. * * * Name: mpi2_ra.h diff --git a/sys/dev/disk/mps/mpi/mpi2_raid.h b/sys/dev/raid/mps/mpi/mpi2_raid.h similarity index 89% rename from sys/dev/disk/mps/mpi/mpi2_raid.h rename to sys/dev/raid/mps/mpi/mpi2_raid.h index d10635227b..f1f1988c32 100644 --- a/sys/dev/disk/mps/mpi/mpi2_raid.h +++ b/sys/dev/raid/mps/mpi/mpi2_raid.h @@ -1,13 +1,42 @@ -/* $FreeBSD: src/sys/dev/mps/mpi/mpi2_raid.h,v 1.1 2010/09/10 15:03:56 ken Exp $ */ +/*- + * 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: src/sys/dev/mps/mpi/mpi2_raid.h,v 1.2 2012/01/26 18:17:21 ken Exp $ + */ + /* - * 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/disk/mps/mpi/mpi2_sas.h b/sys/dev/raid/mps/mpi/mpi2_sas.h similarity index 86% rename from sys/dev/disk/mps/mpi/mpi2_sas.h rename to sys/dev/raid/mps/mpi/mpi2_sas.h index 2d2e469bb3..a232d929b7 100644 --- a/sys/dev/disk/mps/mpi/mpi2_sas.h +++ b/sys/dev/raid/mps/mpi/mpi2_sas.h @@ -1,13 +1,42 @@ -/* $FreeBSD: src/sys/dev/mps/mpi/mpi2_sas.h,v 1.1 2010/09/10 15:03:56 ken Exp $ */ +/*- + * 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: src/sys/dev/mps/mpi/mpi2_sas.h,v 1.2 2012/01/26 18:17:21 ken Exp $ + */ + /* - * 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/disk/mps/mpi/mpi2_targ.h b/sys/dev/raid/mps/mpi/mpi2_targ.h similarity index 92% rename from sys/dev/disk/mps/mpi/mpi2_targ.h rename to sys/dev/raid/mps/mpi/mpi2_targ.h index b1d8ab3e29..f52d5787f2 100644 --- a/sys/dev/disk/mps/mpi/mpi2_targ.h +++ b/sys/dev/raid/mps/mpi/mpi2_targ.h @@ -1,13 +1,42 @@ -/* $FreeBSD: src/sys/dev/mps/mpi/mpi2_targ.h,v 1.1 2010/09/10 15:03:56 ken Exp $ */ +/*- + * 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: src/sys/dev/mps/mpi/mpi2_targ.h,v 1.2 2012/01/26 18:17:21 ken Exp $ + */ + /* - * 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/disk/mps/mpi/mpi2_tool.h b/sys/dev/raid/mps/mpi/mpi2_tool.h similarity index 82% rename from sys/dev/disk/mps/mpi/mpi2_tool.h rename to sys/dev/raid/mps/mpi/mpi2_tool.h index c827e3ae48..249bb8745c 100644 --- a/sys/dev/disk/mps/mpi/mpi2_tool.h +++ b/sys/dev/raid/mps/mpi/mpi2_tool.h @@ -1,13 +1,42 @@ -/* $FreeBSD: src/sys/dev/mps/mpi/mpi2_tool.h,v 1.1 2010/09/10 15:03:56 ken Exp $ */ +/*- + * 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: src/sys/dev/mps/mpi/mpi2_tool.h,v 1.2 2012/01/26 18:17:21 ken Exp $ + */ + /* - * 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) @@ -120,6 +153,46 @@ typedef struct _MPI2_TOOLBOX_MEM_MOVE_REQUEST Mpi2ToolboxMemMoveRequest_t, MPI2_POINTER pMpi2ToolboxMemMoveRequest_t; +/**************************************************************************** +* 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/disk/mps/mpi/mpi2_type.h b/sys/dev/raid/mps/mpi/mpi2_type.h similarity index 58% rename from sys/dev/disk/mps/mpi/mpi2_type.h rename to sys/dev/raid/mps/mpi/mpi2_type.h index fca0fa6c90..0ed736f3c4 100644 --- a/sys/dev/disk/mps/mpi/mpi2_type.h +++ b/sys/dev/raid/mps/mpi/mpi2_type.h @@ -1,6 +1,35 @@ -/* $FreeBSD: src/sys/dev/mps/mpi/mpi2_type.h,v 1.1 2010/09/10 15:03:56 ken Exp $ */ +/*- + * 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: src/sys/dev/mps/mpi/mpi2_type.h,v 1.2 2012/01/26 18:17:21 ken Exp $ + */ + /* - * Copyright (c) 2000-2007 LSI Corporation. + * Copyright (c) 2000-2011 LSI Corporation. * * * Name: mpi2_type.h diff --git a/sys/dev/disk/mps/mps.c b/sys/dev/raid/mps/mps.c similarity index 70% rename from sys/dev/disk/mps/mps.c rename to sys/dev/raid/mps/mps.c index 013cca8010..903d51ca2a 100644 --- a/sys/dev/disk/mps/mps.c +++ b/sys/dev/raid/mps/mps.c @@ -23,17 +23,46 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/mps/mps.c,v 1.9 2010/12/10 21:45:10 ken Exp $ + */ +/*- + * 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: src/sys/dev/mps/mps.c,v 1.14 2012/01/26 18:17:21 ken Exp $ */ /* Communications core for LSI MPT2 */ +/* TODO Move headers to mpsvar */ #include #include #include #include #include -#include +#include #include #include #include @@ -41,26 +70,42 @@ #include #include #include +#include +#include #include +#include #include -#include +#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 void mps_dispatch_event(struct mps_softc *sc, uintptr_t data, MPI2_EVENT_NOTIFICATION_REPLY *reply); +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_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"); @@ -73,7 +118,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; @@ -124,7 +169,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__); @@ -155,7 +200,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; } @@ -176,10 +221,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, @@ -215,14 +260,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); + + KKASSERT(lockstatus(&sc->mps_lock, curthread) != 0); + + 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) @@ -377,11 +516,18 @@ 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) + KKASSERT(lockstatus(&sc->mps_lock, curthread) != 0); + + 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); @@ -389,30 +535,6 @@ mps_enqueue_request(struct mps_softc *sc, struct mps_command *cm) 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. */ @@ -455,9 +577,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, (u_int)reply->IOCLogInfo, + reply->PortType); error = ENXIO; + } bcopy(reply, facts, sizeof(MPI2_PORT_FACTS_REPLY)); +done: mps_free_command(sc, cm); return (error); @@ -508,35 +640,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) { @@ -668,7 +771,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 */ @@ -714,15 +817,16 @@ 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 = kmalloc(sizeof(struct mps_chain) * MPS_CHAIN_FRAMES, - M_MPT2, M_WAITOK | M_ZERO); - for (i = 0; i < MPS_CHAIN_FRAMES; i++) { + sc->chains = kmalloc(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); chain->chain_busaddr = sc->chain_busaddr + i * sc->facts->IOCRequestFrameSize * 4; mps_free_chain(sc, chain); + sc->chain_free_lowwater++; } /* XXX Need to pick a more precise value */ @@ -737,7 +841,7 @@ mps_alloc_requests(struct mps_softc *sc) BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */ BUS_DMA_ALLOCNOW, /* flags */ &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); } @@ -762,8 +866,12 @@ mps_alloc_requests(struct mps_softc *sc) /* 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; } @@ -797,28 +905,59 @@ 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; +#if 0 /* XXX swildner */ + sc->disable_msix = 0; +#endif + sc->enable_msi = 1; + sc->max_chains = MPS_CHAIN_FRAMES; /* - * Grab any tunable-set debug level so that tracing works as early - * as possible. + * Grab the global variables. */ - ksnprintf(tmpstr, sizeof(tmpstr), "hw.mps.%d.debug_level", + TUNABLE_INT_FETCH("hw.mps.debug_level", &sc->mps_debug); +#if 0 /* XXX swildner */ + TUNABLE_INT_FETCH("hw.mps.disable_msix", &sc->disable_msix); +#endif + TUNABLE_INT_FETCH("hw.mps.msi.enable", &sc->enable_msi); + TUNABLE_INT_FETCH("hw.mps.max_chains", &sc->max_chains); + + /* Grab the unit-instance variables */ + ksnprintf(tmpstr, sizeof(tmpstr), "dev.mps.%d.debug_level", device_get_unit(sc->mps_dev)); TUNABLE_INT_FETCH(tmpstr, &sc->mps_debug); - ksnprintf(tmpstr, sizeof(tmpstr), "hw.mps.%d.allow_multiple_tm_cmds", + +#if 0 /* XXX swildner */ + ksnprintf(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); +#endif - mps_dprint(sc, MPS_TRACE, "%s\n", __func__); + ksnprintf(tmpstr, sizeof(tmpstr), "dev.mps.%d.enable_msi", + device_get_unit(sc->mps_dev)); + TUNABLE_INT_FETCH(tmpstr, &sc->enable_msi); - lockinit(&sc->mps_lock, "MPT2SAS lock", 0, LK_CANRECURSE); - callout_init(&sc->periodic); - TAILQ_INIT(&sc->event_list); + ksnprintf(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 @@ -830,22 +969,79 @@ mps_attach(struct mps_softc *sc) 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); + SYSCTL_STATIC_CHILDREN(_hw_mps), OID_AUTO, tmpstr2, + CTLFLAG_RD, 0, tmpstr); if (sc->sysctl_tree == NULL) - return (ENOMEM); + return; + sysctl_ctx = &sc->sysctl_ctx; + sysctl_tree = sc->sysctl_tree; - SYSCTL_ADD_INT(&sc->sysctl_ctx, SYSCTL_CHILDREN(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_INT(&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"); +#if 0 /* XXX swildner */ + 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"); +#endif + + SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), + OID_AUTO, "enable_msi", CTLFLAG_RD, &sc->enable_msi, 0, + "Enable 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(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(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(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), + OID_AUTO, "chain_free", CTLFLAG_RD, + &sc->chain_free, 0, "number of free chain elements"); + + 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_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 +} + +int +mps_attach(struct mps_softc *sc) +{ + int i, error; + + mps_get_tunables(sc); - if ((error = mps_transition_ready(sc)) != 0) + mps_dprint(sc, MPS_TRACE, "%s\n", __func__); + + lockinit(&sc->mps_lock, "MPT2SAS lock", 0, LK_CANRECURSE); + callout_init(&sc->periodic); + 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 = kmalloc(sizeof(MPI2_IOC_FACTS_REPLY), M_MPT2, M_ZERO|M_NOWAIT); @@ -854,13 +1050,16 @@ mps_attach(struct mps_softc *sc) mps_print_iocfacts(sc, sc->facts); - mps_printf(sc, "Firmware: %02d.%02d.%02d.%02d\n", + ksnprintf(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, "IOCCapabilities: %b\n", - (u_int)(sc->facts->IOCCapabilities), + + mps_printf(sc, "Firmware: %s, Driver: %s\n", sc->fw_version, + MPS_DRIVER_VERSION); + mps_printf(sc, "IOCCapabilities: %b\n", (int)sc->facts->IOCCapabilities, "\20" "\3ScsiTaskFull" "\4DiagTrace" "\5SnapBuf" "\6ExtBuf" "\7EEDP" "\10BiDirTarg" "\11Multicast" "\14TransRetry" "\15IR" "\16EventReplay" "\17RaidAccel" "\20MSIXIndex" "\21HostDisc"); @@ -873,11 +1072,43 @@ 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. @@ -886,18 +1117,21 @@ mps_attach(struct mps_softc *sc) sc->num_replies = MIN(MPS_REPLY_FRAMES + MPS_EVT_REPLY_FRAMES, sc->facts->MaxReplyDescriptorPostQueueDepth) - 1; TAILQ_INIT(&sc->req_list); + TAILQ_INIT(&sc->high_priority_req_list); TAILQ_INIT(&sc->chain_list); TAILQ_INIT(&sc->tm_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); } @@ -920,6 +1154,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); } @@ -938,10 +1174,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); @@ -957,9 +1200,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) { @@ -969,7 +1227,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); } @@ -981,39 +1241,21 @@ mps_periodic(void *arg) uint32_t db; sc = (struct mps_softc *)arg; - if (sc->mps_flags & MPS_FLAGS_SHUTDOWN) + mps_lock(sc); + if (sc->mps_flags & MPS_FLAGS_SHUTDOWN) { + mps_unlock(sc); return; + } 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); - + mps_unlock(sc); } static void @@ -1082,6 +1324,8 @@ mps_free(struct mps_softc *sc) #if 0 /* XXX swildner */ /* Lock must not be held for this */ callout_drain(&sc->periodic); +#else + callout_stop(&sc->periodic); #endif if (((error = mps_detach_log(sc)) != 0) || @@ -1090,7 +1334,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); } @@ -1153,11 +1397,45 @@ 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); + lockuninit(&sc->mps_lock); return (0); } +static __inline void +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) { + 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 mps_intr(void *data) { @@ -1191,6 +1469,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); @@ -1208,20 +1487,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]; @@ -1263,8 +1557,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; @@ -1284,27 +1602,18 @@ mps_intr_locked(void *data) break; } - if (cm != NULL) { - if (cm->cm_flags & MPS_CM_FLAGS_POLLED) - cm->cm_flags |= MPS_CM_FLAGS_COMPLETE; - - if (cm->cm_complete != NULL) - cm->cm_complete(sc, cm); - - if (cm->cm_flags & MPS_CM_FLAGS_WAKEUP) - wakeup(cm); - } + if (cm != NULL) + mps_complete_command(cm); 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; @@ -1327,6 +1636,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); } /* @@ -1393,14 +1724,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) { @@ -1459,6 +1836,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); @@ -1485,8 +1863,7 @@ mps_push_sge(struct mps_command *cm, void *sgep, size_t len, int segsleft) if (((sge->FlagsLength >> MPI2_SGE_FLAGS_SHIFT) & MPI2_SGE_FLAGS_ADDRESS_SIZE) == 0) panic("SGE simple %p flags %02x not marked 64-bit?", - sge, - (u_int)(sge->FlagsLength >> MPI2_SGE_FLAGS_SHIFT)); + sge, (u_int)(sge->FlagsLength >> MPI2_SGE_FLAGS_SHIFT)); break; default: @@ -1558,12 +1935,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; @@ -1582,10 +1995,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); @@ -1613,8 +2026,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) { @@ -1640,21 +2053,22 @@ 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, sflags, nsegs - i); if (error != 0) { /* Resource shortage, roll back! */ - mps_printf(sc, "out of chain frames\n"); + mps_dprint(sc, MPS_INFO, "out of chain frames\n"); + cm->cm_flags |= MPS_CM_FLAGS_CHAIN_FAILED; + mps_complete_command(cm); return; } } @@ -1673,8 +2087,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) @@ -1705,6 +2122,57 @@ mps_map_command(struct mps_softc *sc, struct mps_command *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; + + KKASSERT(lockstatus(&sc->mps_lock, curthread) != 0); + + 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 = lksleep(cm, &sc->mps_lock, 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); +} + /* * The MPT driver had a verbose interface for config pages. In this driver, * reduce it to much simplier terms, similar to the Linux driver. @@ -1763,11 +2231,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); - lksleep(cm, &sc->mps_lock, 0, "mpswait", 0); + } mps_config_complete(sc, cm); } @@ -1794,7 +2264,20 @@ mps_config_complete(struct mps_softc *sc, struct mps_command *cm) bus_dmamap_unload(sc->buffer_dmat, cm->cm_dmamap); } + /* + * XXX KDM need to do more error recovery? This results in the + * device in question not getting probed. + */ + if ((cm->cm_flags & MPS_CM_FLAGS_ERROR_MASK) != 0) { + params->status = MPI2_IOCSTATUS_BUSY; + 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; @@ -1806,6 +2289,7 @@ mps_config_complete(struct mps_softc *sc, struct mps_command *cm) params->hdr.Struct.PageVersion = reply->Header.PageVersion; } +done: mps_free_command(sc, cm); if (params->callback != NULL) params->callback(sc, params); diff --git a/sys/dev/raid/mps/mps_config.c b/sys/dev/raid/mps/mps_config.c new file mode 100644 index 0000000000..44b70eb55b --- /dev/null +++ b/sys/dev/raid/mps/mps_config.c @@ -0,0 +1,1390 @@ +/*- + * 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: src/sys/dev/mps/mps_config.c,v 1.1 2012/01/26 18:17:21 ken Exp $ + */ + +/* TODO Move headers to mpsvar */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * 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) { + kprintf("%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 */ + kprintf("%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 */ + kprintf("%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) { + kprintf("%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 = kmalloc((cm->cm_length), M_MPT2, M_ZERO | M_NOWAIT); + if (!page) { + kprintf("%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 */ + kprintf("%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 */ + kprintf("%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: + kfree(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) { + kprintf("%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 */ + kprintf("%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 */ + kprintf("%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) { + kprintf("%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 = kmalloc(MPS_MAN_PAGE10_SIZE, M_MPT2, M_ZERO | M_NOWAIT); + if (!page) { + kprintf("%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 */ + kprintf("%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 */ + kprintf("%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 = (uint32_t *)&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: + kfree(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 = kmalloc(sizeof(Mpi2RaidVolPage0_t) + + (sizeof(Mpi2RaidVol0PhysDisk_t) * MPS_MAX_DISKS_IN_VOL), + M_MPT2, M_ZERO | M_NOWAIT); + if (!raid_vol_pg0) { + kprintf("%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) + kfree(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) { + kprintf("%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 */ + kprintf("%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 */ + kprintf("%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) { + kprintf("%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 = kmalloc(cm->cm_length, M_MPT2, M_ZERO|M_NOWAIT); + if (!page) { + kprintf("%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 */ + kprintf("%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 */ + kprintf("%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: + kfree(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) { + kprintf("%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 */ + kprintf("%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 */ + kprintf("%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) { + kprintf("%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 = kmalloc(cm->cm_length, M_MPT2, M_ZERO | M_NOWAIT); + if (!page) { + kprintf("%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 */ + kprintf("%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 */ + kprintf("%s: page written with error; iocstatus = 0x%x\n", + __func__, ioc_status); + error = ENXIO; + goto out; + } +out: + kfree(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) { + kprintf("%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 */ + kprintf("%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 */ + kprintf("%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) { + kprintf("%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 = kmalloc(cm->cm_length, M_MPT2, M_ZERO | M_NOWAIT); + if (!page) { + kprintf("%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 */ + kprintf("%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 */ + kprintf("%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: + kfree(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) { + kprintf("%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 */ + kprintf("%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 */ + kprintf("%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) { + kprintf("%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 = kmalloc(cm->cm_length, M_MPT2, M_ZERO | M_NOWAIT); + if (!page) { + kprintf("%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 */ + kprintf("%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 */ + kprintf("%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: + kfree(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) { + kprintf("%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 */ + kprintf("%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 */ + kprintf("%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) { + kprintf("%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 = kmalloc(cm->cm_length, M_MPT2, M_ZERO | M_NOWAIT); + if (!page) { + kprintf("%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 */ + kprintf("%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 */ + kprintf("%s: page read with error; iocstatus = 0x%x\n", + __func__, ioc_status); + error = ENXIO; + goto out; + } + bcopy(page, config_page, cm->cm_length); +out: + kfree(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) { + kprintf("%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 */ + kprintf("%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 */ + kprintf("%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) { + kprintf("%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 = kmalloc(cm->cm_length, M_MPT2, M_ZERO | M_NOWAIT); + if (!page) { + kprintf("%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 */ + kprintf("%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 */ + kprintf("%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: + kfree(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) { + kprintf("%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 */ + kprintf("%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 */ + kprintf("%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) { + kprintf("%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 = kmalloc(cm->cm_length, M_MPT2, M_ZERO | M_NOWAIT); + if (!page) { + kprintf("%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 */ + kprintf("%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 */ + kprintf("%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: + kfree(page, M_MPT2); + if (cm) + mps_free_command(sc, cm); + return (error); +} diff --git a/sys/dev/raid/mps/mps_ioctl.h b/sys/dev/raid/mps/mps_ioctl.h new file mode 100644 index 0000000000..e56299278d --- /dev/null +++ b/sys/dev/raid/mps/mps_ioctl.h @@ -0,0 +1,387 @@ +/*- + * Copyright (c) 2008 Yahoo!, Inc. + * All rights reserved. + * Written by: John Baldwin + * + * 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. + * 3. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 userland interface + * + * $FreeBSD: src/sys/dev/mps/mps_ioctl.h,v 1.3 2012/01/26 18:17:21 ken Exp $ + */ +/*- + * 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: src/sys/dev/mps/mps_ioctl.h,v 1.3 2012/01/26 18:17:21 ken Exp $ + */ + +#ifndef _MPS_IOCTL_H_ +#define _MPS_IOCTL_H_ + +#include +#include +#include +#include + +/* + * For the read header requests, the header should include the page + * type or extended page type, page number, and page version. The + * buffer and length are unused. The completed header is returned in + * the 'header' member. + * + * For the read page and write page requests, 'buf' should point to a + * buffer of 'len' bytes which holds the entire page (including the + * header). + * + * All requests specify the page address in 'page_address'. + */ +struct mps_cfg_page_req { + MPI2_CONFIG_PAGE_HEADER header; + uint32_t page_address; + void *buf; + int len; + uint16_t ioc_status; +}; + +struct mps_ext_cfg_page_req { + MPI2_CONFIG_EXTENDED_PAGE_HEADER header; + uint32_t page_address; + void *buf; + int len; + uint16_t ioc_status; +}; + +struct mps_raid_action { + uint8_t action; + uint8_t volume_bus; + uint8_t volume_id; + uint8_t phys_disk_num; + uint32_t action_data_word; + void *buf; + int len; + uint32_t volume_status; + uint32_t action_data[4]; + uint16_t action_status; + uint16_t ioc_status; + uint8_t write; +}; + +struct mps_usr_command { + void *req; + uint32_t req_len; + void *rpl; + uint32_t rpl_len; + void *buf; + int len; + uint32_t flags; +}; + +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) +#define MPSIO_READ_EXT_CFG_PAGE _IOWR('M', 203, struct mps_ext_cfg_page_req) +#define MPSIO_WRITE_CFG_PAGE _IOWR('M', 204, struct mps_cfg_page_req) +#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/raid/mps/mps_mapping.c b/sys/dev/raid/mps/mps_mapping.c new file mode 100644 index 0000000000..cb0215469a --- /dev/null +++ b/sys/dev/raid/mps/mps_mapping.c @@ -0,0 +1,2265 @@ +/*- + * 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: src/sys/dev/mps/mps_mapping.c,v 1.1 2012/01/26 18:17:21 ken Exp $ + */ + +/* TODO Move headers to mpsvar */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * _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)) { + kprintf("%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)) { + kprintf("%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) { + kprintf("%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) { + kprintf("%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) { + kprintf("%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; + kprintf("%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; + kprintf("%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; + } + kprintf("%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; + kprintf("%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; + } + kprintf("%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; + kprintf("%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)) { + kprintf("%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 = kmalloc((sizeof(struct dev_mapping_table) * + sc->max_devices), M_MPT2, M_ZERO|M_NOWAIT); + if (!sc->mapping_table) + goto free_resources; + + sc->removal_table = kmalloc((sizeof(struct map_removal_table) * + sc->max_devices), M_MPT2, M_ZERO|M_NOWAIT); + if (!sc->removal_table) + goto free_resources; + + sc->enclosure_table = kmalloc((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 = kmalloc((sizeof(u8) * sc->max_dpm_entries), + M_MPT2, M_ZERO|M_NOWAIT); + if (!sc->dpm_entry_used) + goto free_resources; + + sc->dpm_flush_entry = kmalloc((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 = kmalloc(dpm_pg0_sz, M_MPT2, M_ZERO|M_NOWAIT); + if (!sc->dpm_pg0) { + kprintf("%s: memory alloc failed for dpm page; disabling dpm\n", + __func__); + sc->is_dpm_enable = 0; + } + + return 0; + +free_resources: + kfree(sc->mapping_table, M_MPT2); + kfree(sc->removal_table, M_MPT2); + kfree(sc->enclosure_table, M_MPT2); + kfree(sc->dpm_entry_used, M_MPT2); + kfree(sc->dpm_flush_entry, M_MPT2); + kfree(sc->dpm_pg0, M_MPT2); + kprintf("%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) +{ + kfree(sc->mapping_table, M_MPT2); + kfree(sc->removal_table, M_MPT2); + kfree(sc->enclosure_table, M_MPT2); + kfree(sc->dpm_entry_used, M_MPT2); + kfree(sc->dpm_flush_entry, M_MPT2); + kfree(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) { + kprintf("%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) { + kprintf("%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) { + kprintf("%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) + kprintf("%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)) { + kprintf("%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) { + kprintf("%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) { + kprintf("%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) { + kprintf("%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) { + kprintf("%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 = kmalloc(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: + kfree(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 = kmalloc(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) { + kprintf("%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) { + kprintf("%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) { + kprintf("%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); + kfree(wwid_table, M_MPT2); + if (sc->pending_map_events) + sc->pending_map_events--; +} diff --git a/sys/dev/disk/mps/mps_table.h b/sys/dev/raid/mps/mps_mapping.h similarity index 51% copy from sys/dev/disk/mps/mps_table.h copy to sys/dev/raid/mps/mps_mapping.h index 9c9f78c93e..07a48a4cbe 100644 --- a/sys/dev/disk/mps/mps_table.h +++ b/sys/dev/raid/mps/mps_mapping.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2009 Yahoo! Inc. + * Copyright (c) 2011 LSI Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -23,31 +23,49 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/mps/mps_table.h,v 1.1 2010/09/10 15:03:56 ken Exp $ + * LSI MPT-Fusion Host Adapter FreeBSD + * + * $FreeBSD: src/sys/dev/mps/mps_mapping.h,v 1.1 2012/01/26 18:17:21 ken Exp $ */ -#ifndef _MPS_TABLE_H -#define _MPS_TABLE_H +#ifndef _MPS_MAPPING_H +#define _MPS_MAPPING_H -struct mps_table_lookup { - char *string; - u_int code; +/** + * 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; }; -char * mps_describe_table(struct mps_table_lookup *table, u_int code); -void mps_describe_devinfo(uint32_t devinfo, char *string, int len); -extern struct mps_table_lookup mps_event_names[]; -extern struct mps_table_lookup mps_phystatus_names[]; -extern struct mps_table_lookup mps_linkrate_names[]; +extern int +mpssas_get_sas_address_for_sata_disk(struct mps_softc *ioc, + u64 *sas_address, u16 handle, u32 device_info); -void mps_print_iocfacts(struct mps_softc *, MPI2_IOC_FACTS_REPLY *); -void mps_print_portfacts(struct mps_softc *, MPI2_PORT_FACTS_REPLY *); -void mps_print_event(struct mps_softc *, MPI2_EVENT_NOTIFICATION_REPLY *); -void mps_print_sasdev0(struct mps_softc *, MPI2_CONFIG_PAGE_SAS_DEV_0 *); -void mps_print_evt_sas(struct mps_softc *, MPI2_EVENT_NOTIFICATION_REPLY *); -void mps_print_expander1(struct mps_softc *, MPI2_CONFIG_PAGE_EXPANDER_1 *); -void mps_print_sasphy0(struct mps_softc *, MPI2_CONFIG_PAGE_SAS_PHY_0 *); -void mps_print_sgl(struct mps_softc *, struct mps_command *, int); -void mps_print_scsiio_cmd(struct mps_softc *, struct mps_command *); #endif diff --git a/sys/dev/disk/mps/mps_pci.c b/sys/dev/raid/mps/mps_pci.c similarity index 69% rename from sys/dev/disk/mps/mps_pci.c rename to sys/dev/raid/mps/mps_pci.c index 3b4642df76..961a8c3b28 100644 --- a/sys/dev/disk/mps/mps_pci.c +++ b/sys/dev/raid/mps/mps_pci.c @@ -23,11 +23,12 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/mps/mps_pci.c,v 1.2 2010/11/30 22:39:46 ken Exp $ + * $FreeBSD: src/sys/dev/mps/mps_pci.c,v 1.4 2012/01/26 18:17:21 ken Exp $ */ /* PCI/PCI-X/PCIe bus interface for the LSI MPT2 controllers */ +/* TODO Move headers to mpsvar */ #include #include #include @@ -35,22 +36,27 @@ #include #include #include +#include #include #include #include #include -#include #include #include +#include -#include -#include -#include -#include +#include +#include +#include +#include +#include -#include +#include +#include +#include +#include static int mps_pci_probe(device_t); static int mps_pci_attach(device_t); @@ -58,28 +64,17 @@ static int mps_pci_detach(device_t); static int mps_pci_suspend(device_t); static int mps_pci_resume(device_t); static void mps_pci_free(struct mps_softc *); -#ifdef OLD_MSI +#if 0 /* XXX swildner */ static int mps_alloc_msix(struct mps_softc *sc, int msgs); -static int mps_alloc_msi(struct mps_softc *sc, int msgs); #endif -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), DEVMETHOD(device_detach, mps_pci_detach), DEVMETHOD(device_suspend, mps_pci_suspend), DEVMETHOD(device_resume, mps_pci_resume), - DEVMETHOD(bus_print_child, bus_generic_print_child), - DEVMETHOD(bus_driver_added, bus_generic_driver_added), + { 0, 0 } }; @@ -90,7 +85,7 @@ static driver_t mps_pci_driver = { }; static devclass_t mps_devclass; -DRIVER_MODULE(mps, pci, mps_pci_driver, mps_devclass, NULL, NULL); +DRIVER_MODULE(mps, pci, mps_pci_driver, mps_devclass, 0, 0); struct mps_ident { uint16_t vendor; @@ -126,10 +121,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 } }; @@ -162,7 +171,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); } @@ -206,7 +215,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(NULL, /* parent */ 1, 0, /* algnmnt, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ @@ -231,57 +240,29 @@ int mps_pci_setup_interrupts(struct mps_softc *sc) { device_t dev; - int i, error; -#ifdef OLD_MSI - int msgs; -#endif + int error; + u_int irq_flags; dev = sc->mps_dev; - error = ENXIO; -#ifdef OLD_MSI - if ((mps_disable_msix == 0) && +#if 0 /* XXX swildner */ + 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) && - ((msgs = pci_msi_count(dev)) >= MPS_MSI_COUNT)) - error = mps_alloc_msi(sc, MPS_MSI_COUNT); #endif - if (error != 0) { - sc->mps_flags |= MPS_FLAGS_INTX; - sc->mps_irq_rid[0] = 0; - sc->mps_irq[0] = bus_alloc_resource_any(dev, SYS_RES_IRQ, - &sc->mps_irq_rid[0], RF_SHAREABLE | RF_ACTIVE); - if (sc->mps_irq[0] == NULL) { - device_printf(dev, "Cannot allocate INTx interrupt\n"); - return (ENXIO); - } - error = bus_setup_intr(dev, sc->mps_irq[0], - INTR_MPSAFE, mps_intr, sc, - &sc->mps_intrhand[0], NULL); - if (error) - device_printf(dev, "Cannot setup INTx interrupt\n"); - } else { - sc->mps_flags |= MPS_FLAGS_MSI; - for (i = 0; i < MPS_MSI_COUNT; i++) { - sc->mps_irq_rid[i] = i + 1; - sc->mps_irq[i] = bus_alloc_resource_any(dev, - SYS_RES_IRQ, &sc->mps_irq_rid[i], RF_ACTIVE); - if (sc->mps_irq[i] == NULL) { - device_printf(dev, - "Cannot allocate MSI interrupt\n"); - return (ENXIO); - } - error = bus_setup_intr(dev, sc->mps_irq[i], - INTR_MPSAFE, mps_intr_msi, - sc, &sc->mps_intrhand[i], NULL); - if (error) { - device_printf(dev, - "Cannot setup MSI interrupt %d\n", i); - break; - } - } + sc->mps_irq_rid[0] = 0; + sc->mps_irq_type[0] = pci_alloc_1intr(dev, sc->enable_msi, + &sc->mps_irq_rid[0], &irq_flags); + sc->mps_irq[0] = bus_alloc_resource_any(dev, SYS_RES_IRQ, + &sc->mps_irq_rid[0], irq_flags); + if (sc->mps_irq[0] == NULL) { + device_printf(dev, "Cannot allocate interrupt\n"); + return (ENXIO); } + error = bus_setup_intr(dev, sc->mps_irq[0], INTR_MPSAFE, mps_intr, sc, + &sc->mps_intrhand[0], NULL); + if (error) + device_printf(dev, "Cannot setup interrupt\n"); return (error); } @@ -304,30 +285,15 @@ mps_pci_detach(device_t dev) static void mps_pci_free(struct mps_softc *sc) { - int i; - if (sc->mps_parent_dmat != NULL) { bus_dma_tag_destroy(sc->mps_parent_dmat); } - if (sc->mps_flags & MPS_FLAGS_MSI) { - for (i = 0; i < MPS_MSI_COUNT; i++) { - if (sc->mps_irq[i] != NULL) { - bus_teardown_intr(sc->mps_dev, sc->mps_irq[i], - sc->mps_intrhand[i]); - bus_release_resource(sc->mps_dev, SYS_RES_IRQ, - sc->mps_irq_rid[i], sc->mps_irq[i]); - } - } + bus_teardown_intr(sc->mps_dev, sc->mps_irq[0], sc->mps_intrhand[0]); + bus_release_resource(sc->mps_dev, SYS_RES_IRQ, sc->mps_irq_rid[0], + sc->mps_irq[0]); + if (sc->mps_irq_type[0] == PCI_INTR_TYPE_MSI) pci_release_msi(sc->mps_dev); - } - - if (sc->mps_flags & MPS_FLAGS_INTX) { - bus_teardown_intr(sc->mps_dev, sc->mps_irq[0], - sc->mps_intrhand[0]); - bus_release_resource(sc->mps_dev, SYS_RES_IRQ, - sc->mps_irq_rid[0], sc->mps_irq[0]); - } if (sc->mps_regs_resource != NULL) { bus_release_resource(sc->mps_dev, SYS_RES_MEMORY, @@ -349,7 +315,7 @@ mps_pci_resume(device_t dev) return (EINVAL); } -#ifdef OLD_MSI +#if 0 /* XXX swildner */ static int mps_alloc_msix(struct mps_softc *sc, int msgs) { @@ -358,13 +324,21 @@ mps_alloc_msix(struct mps_softc *sc, int msgs) error = pci_alloc_msix(sc->mps_dev, &msgs); return (error); } +#endif -static int -mps_alloc_msi(struct mps_softc *sc, int msgs) +int +mps_pci_restore(struct mps_softc *sc) { - int error; + struct pci_devinfo *dinfo; - error = pci_alloc_msi(sc->mps_dev, &msgs); - return (error); + 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); } -#endif diff --git a/sys/dev/raid/mps/mps_sas.c b/sys/dev/raid/mps/mps_sas.c new file mode 100644 index 0000000000..58b6dc4ba3 --- /dev/null +++ b/sys/dev/raid/mps/mps_sas.c @@ -0,0 +1,3234 @@ +/*- + * Copyright (c) 2009 Yahoo! Inc. + * 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. + */ +/*- + * 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: src/sys/dev/mps/mps_sas.c,v 1.16 2012/01/26 18:17:21 ken Exp $ + */ + +/* Communications core for LSI MPT2 */ + +/* TODO Move headers to mpsvar */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if 0 /* XXX __FreeBSD_version >= 900026 */ +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MPSSAS_DISCOVERY_TIMEOUT 20 +#define MPSSAS_MAX_DISCOVERY_TIMEOUTS 10 /* 200 seconds */ + +/* + * 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 +}; + +MALLOC_DEFINE(M_MPSSAS, "MPSSAS", "MPS SAS memory"); + +static struct mpssas_target * mpssas_find_target_by_handle(struct mpssas_softc *, int, uint16_t); +#if 0 /* XXX unused */ +static void mpssas_discovery_timeout(void *data); +#endif +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_scsiio_timeout(void *data); +static void mpssas_abort_complete(struct mps_softc *sc, struct mps_command *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_complete(struct mps_softc *, struct mps_command *); +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 struct mpssas_target * +mpssas_find_target_by_handle(struct mpssas_softc *sassc, int start, uint16_t handle) +{ + 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 (NULL); +} + +/* 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) +{ + 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); + } +} + +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); + } +} + +/* 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; + + 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; +} + +void +mpssas_free_tm(struct mps_softc *sc, struct mps_command *tm) +{ + if (tm == NULL) + return; + + /* 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); + + mps_free_high_priority_command(sc, tm); +} + + +void +mpssas_rescan_target(struct mps_softc *sc, struct mpssas_target *targ) +{ + struct mpssas_softc *sassc = sc->sassc; + path_id_t pathid; + target_id_t targetid; + union ccb *ccb; + + pathid = cam_sim_path(sassc->sim); + if (targ == NULL) + targetid = CAM_TARGET_WILDCARD; + else + targetid = targ - sassc->targets; + + /* + * Allocate a CCB and schedule a rescan. + */ + ccb = kmalloc(sizeof(union ccb), M_TEMP, M_WAITOK | M_ZERO); + + 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_log_command(struct mps_command *cm, const char *fmt, ...) +{ + struct sbuf sb; + __va_list ap; + char str[192]; + char path_str[64]; + + if (cm == NULL) + return; + + 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); + } + } + 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); + } + + sbuf_printf(&sb, "SMID %u ", cm->cm_desc.Default.SMID); + sbuf_vprintf(&sb, fmt, ap); + sbuf_finish(&sb); + kprintf("%s", sbuf_data(&sb)); + + __va_end(ap); +} + +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. + */ +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; + + mps_dprint(sassc->sc, MPS_TRACE, "%s\n", __func__); + + /* + * 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? */ + kprintf("%s: invalid handle 0x%x \n", __func__, handle); + return; + } + + targ->flags |= MPSSAS_TARGET_INREMOVAL; + + cm = mpssas_alloc_tm(sc); + if (cm == NULL) { + mps_printf(sc, "%s: command alloc failure\n", __func__); + return; + } + + mpssas_lost_target(sc, targ); + + req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)cm->cm_req; + memset(req, 0, sizeof(*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_targ = targ; + cm->cm_data = NULL; + cm->cm_desc.HighPriority.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY; + cm->cm_complete = mpssas_remove_device; + 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 *tm) +{ + MPI2_SCSI_TASK_MANAGE_REPLY *reply; + MPI2_SAS_IOUNIT_CONTROL_REQUEST *req; + struct mpssas_target *targ; + struct mps_command *next_cm; + uint16_t handle; + + mps_dprint(sc, MPS_TRACE, "%s\n", __func__); + + 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 ((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 (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, "IOCStatus = 0x%x while resetting device 0x%x\n", + reply->IOCStatus, handle); + mpssas_free_tm(sc, tm); + return; + } + + mps_dprint(sc, MPS_INFO, "Reset aborted %u commands\n", + (u_int)reply->TerminationCount); + 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 *)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; + 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, tm); + + mps_dprint(sc, MPS_INFO, "clearing target %u handle 0x%04x\n", + targ->tid, handle); + TAILQ_FOREACH_MUTABLE(tm, &targ->commands, cm_link, next_cm) { + union ccb *ccb; + + 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, tm); + } +} + +static void +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 *)tm->cm_reply; + handle = (uint16_t)(uintptr_t)tm->cm_complete_data; + + /* + * 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 (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; + } + + mps_printf(sc, "%s on handle 0x%04x, IOCStatus= 0x%x\n", __func__, + handle, reply->IOCStatus); + + /* + * 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; + } + + mpssas_free_tm(sc, tm); +} + +static int +mpssas_register_events(struct mps_softc *sc) +{ + uint8_t events[16]; + + bzero(events, 16); + setbit(events, MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE); + setbit(events, MPI2_EVENT_SAS_DISCOVERY); + setbit(events, MPI2_EVENT_SAS_BROADCAST_PRIMITIVE); + setbit(events, MPI2_EVENT_SAS_INIT_DEVICE_STATUS_CHANGE); + 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); + + return (0); +} + +int +mps_attach_sas(struct mps_softc *sc) +{ + struct mpssas_softc *sassc; +#if __FreeBSD_version >= 1000006 + cam_status status; +#endif + int unit, error = 0; + + mps_dprint(sc, MPS_TRACE, "%s\n", __func__); + + sassc = kmalloc(sizeof(struct mpssas_softc), M_MPT2, M_WAITOK|M_ZERO); + sassc->targets = kmalloc(sizeof(struct mpssas_target) * + sc->facts->MaxTargets, M_MPT2, M_WAITOK|M_ZERO); + sc->sassc = sassc; + sassc->sc = sc; + + 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, + unit, &sc->mps_lock, 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, -1, "%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. + */ + if ((error = xpt_bus_register(sassc->sim, 0)) != 0) { + mps_dprint(sc, MPS_FAULT, "Error %d registering SCSI bus\n", + error); + mps_unlock(sc); + goto out; + } + + /* + * Assume that discovery events will start right away. Freezing + * the simq will prevent the CAM boottime scanner from running + * before discovery is complete. + */ + sassc->flags |= MPSSAS_IN_STARTUP | MPSSAS_IN_DISCOVERY; + xpt_freeze_simq(sassc->sim, 1); + sc->sassc->startup_refcount = 0; + + callout_init_mp(&sassc->discovery_callout); + 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) + mps_detach_sas(sc); + return (error); +} + +int +mps_detach_sas(struct mps_softc *sc) +{ + struct mpssas_softc *sassc; + + mps_dprint(sc, MPS_TRACE, "%s\n", __func__); + + if (sc->sassc == NULL) + 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); + + /* Deregister our async handler */ +#if __FreeBSD_version >= 1000006 + xpt_register_async(0, mpssas_async, sc, NULL); +#endif + + 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); + } + + if (sassc->flags & MPSSAS_SCANTHREAD) { + sassc->flags |= MPSSAS_SHUTDOWN; + wakeup(&sassc->ccb_scanq); + + if (sassc->flags & MPSSAS_SCANTHREAD) { + lksleep(&sassc->flags, &sc->mps_lock, 0, + "mps_shutdown", 30 * hz); + } + } + mps_unlock(sc); + + if (sassc->devq != NULL) + cam_simq_release(sassc->devq); + + kfree(sassc->targets, M_MPT2); + kfree(sassc, M_MPT2); + sc->sassc = NULL; + + return (0); +} + +void +mpssas_discovery_end(struct mpssas_softc *sassc) +{ + struct mps_softc *sc = sassc->sc; + + mps_dprint(sc, MPS_TRACE, "%s\n", __func__); + + if (sassc->flags & MPSSAS_DISCOVERY_TIMEOUT_PENDING) + callout_stop(&sassc->discovery_callout); + +} + +#if 0 /* XXX unused */ +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_printf(sassc->sc, + "Finished polling after discovery timeout at %d\n", ticks); + + if ((sassc->flags & MPSSAS_IN_DISCOVERY) == 0) { + mpssas_discovery_end(sassc); + } else { + if (sassc->discovery_timeouts < MPSSAS_MAX_DISCOVERY_TIMEOUTS) { + sassc->flags |= MPSSAS_DISCOVERY_TIMEOUT_PENDING; + callout_reset(&sassc->discovery_callout, + MPSSAS_DISCOVERY_TIMEOUT * hz, + mpssas_discovery_timeout, sassc); + sassc->discovery_timeouts++; + } else { + mps_dprint(sassc->sc, MPS_FAULT, + "Discovery timed out, continuing.\n"); + sassc->flags &= ~MPSSAS_IN_DISCOVERY; + mpssas_discovery_end(sassc); + } + } + + mps_unlock(sc); +} +#endif + +static void +mpssas_action(struct cam_sim *sim, union ccb *ccb) +{ + struct mpssas_softc *sassc; + + sassc = cam_sim_softc(sim); + + mps_dprint(sassc->sc, MPS_TRACE, "%s func 0x%x\n", __func__, + ccb->ccb_h.func_code); + KKASSERT(lockstatus(&sassc->sc->mps_lock, curthread) != 0); + + switch (ccb->ccb_h.func_code) { + case XPT_PATH_INQ: + { + struct ccb_pathinq *cpi = &ccb->cpi; + + cpi->version_num = 1; + cpi->hba_inquiry = PI_SDTR_ABLE|PI_TAG_ABLE|PI_WIDE_16; + cpi->target_sprt = 0; + cpi->hba_misc = PIM_NOBUSRESET; + cpi->hba_eng_cnt = 0; + cpi->max_target = sassc->sc->facts->MaxTargets - 1; + cpi->max_lun = 0; + cpi->initiator_id = 255; + strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); + strncpy(cpi->hba_vid, "LSILogic", HBA_IDLEN); + strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); + cpi->unit_number = cam_sim_unit(sim); + cpi->bus_id = cam_sim_bus(sim); + cpi->base_transfer_speed = 150000; + cpi->transport = XPORT_SAS; + cpi->transport_version = 0; + cpi->protocol = PROTO_SCSI; + cpi->protocol_version = SCSI_REV_SPC; +#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; + } + case XPT_GET_TRAN_SETTINGS: + { + struct ccb_trans_settings *cts; + struct ccb_trans_settings_sas *sas; + struct ccb_trans_settings_scsi *scsi; + struct mpssas_target *targ; + + cts = &ccb->cts; + sas = &cts->xport_specific.sas; + scsi = &cts->proto_specific.scsi; + + targ = &sassc->targets[cts->ccb_h.target_id]; + if (targ->handle == 0x0) { + cts->ccb_h.status = CAM_TID_INVALID; + break; + } + + cts->protocol_version = SCSI_REV_SPC2; + cts->transport = XPORT_SAS; + cts->transport_version = 0; + + sas->valid = CTS_SAS_VALID_SPEED; + switch (targ->linkrate) { + case 0x08: + sas->bitrate = 150000; + break; + case 0x09: + sas->bitrate = 300000; + break; + case 0x0a: + sas->bitrate = 600000; + break; + default: + sas->valid = 0; + } + + cts->protocol = PROTO_SCSI; + scsi->valid = CTS_SCSI_VALID_TQ; + scsi->flags = CTS_SCSI_FLAGS_TAG_ENB; + + cts->ccb_h.status = CAM_REQ_CMP; + break; + } + case XPT_CALC_GEOMETRY: + cam_calc_geometry(&ccb->ccg, /*extended*/1); + 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: + mpssas_action_scsiio(sassc, ccb); + return; +#if __FreeBSD_version >= 900026 + case XPT_SMP_IO: + mpssas_action_smpio(sassc, ccb); + return; +#endif + default: + ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; + break; + } + xpt_done(ccb); + +} + +static void +mpssas_announce_reset(struct mps_softc *sc, uint32_t ac_code, + target_id_t target_id, lun_id_t lun_id) +{ + path_id_t path_id = cam_sim_path(sc->sassc->sim); + struct cam_path *path; + + mps_printf(sc, "%s code %x target %d lun %d\n", __func__, + ac_code, target_id, lun_id); + + 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; + } + + xpt_async(ac_code, path, NULL); + xpt_free_path(path); +} + +static void +mpssas_complete_all_commands(struct mps_softc *sc) +{ + struct mps_command *cm; + int i; + int completed; + + mps_printf(sc, "%s\n", __func__); + KKASSERT(lockstatus(&sc->mps_lock, curthread) != 0); + + /* 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); + } + } +} + +void +mpssas_handle_reinit(struct mps_softc *sc) +{ + int i; + + /* 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. + */ + 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); + + /* 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); + + /* + * The simq was explicitly frozen above, so set the refcount to 0. + * The simq will be explicitly released after port enable completes. + */ + 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; + + mps_lock(sc); + mpssas_log_command(tm, "task mgmt %p timed out\n", tm); + mps_reinit(sc); + mps_unlock(sc); +} + +static void +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; + + 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 LUN reset! " + "This should not happen!\n", __func__, tm->cm_flags); + mpssas_free_tm(sc, tm); + return; + } + + 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_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_target_reset_complete(struct mps_softc *sc, struct mps_command *tm) +{ + MPI2_SCSI_TASK_MANAGE_REPLY *reply; + MPI2_SCSI_TASK_MANAGE_REQUEST *req; + struct mpssas_target *targ; + + 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; + } + + 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_log_command(tm, + "target reset status 0x%x code 0x%x count %u\n", + reply->IOCStatus, reply->ResponseCode, + reply->TerminationCount); + + 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_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); + } +} + +#define MPS_RESET_TIMEOUT 30 + +static int +mpssas_send_reset(struct mps_softc *sc, struct mps_command *tm, uint8_t type) +{ + MPI2_SCSI_TASK_MANAGE_REQUEST *req; + struct mpssas_target *target; + int err; + + target = tm->cm_targ; + if (target->handle == 0) { + mps_printf(sc, "%s null devhandle for target_id %d\n", + __func__, target->tid); + return -1; + } + + 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; + } + + 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_abort_complete(struct mps_softc *sc, struct mps_command *tm) +{ + struct mps_command *cm; + MPI2_SCSI_TASK_MANAGE_REPLY *reply; + MPI2_SCSI_TASK_MANAGE_REQUEST *req; + struct mpssas_target *targ; + + 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) { + 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; + } + + 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; + } + + 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. + */ + 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); + } +} + +#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_REQUEST *req; + struct mpssas_target *targ; + int err; + + 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; + } + + 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; + + /* XXX Need to handle invalid LUNs */ + MPS_SET_LUN(req->LUN, cm->cm_ccb->ccb_h.target_lun); + + req->TaskMID = cm->cm_desc.Default.SMID; + + 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; + + callout_reset(&tm->cm_callout, MPS_ABORT_TIMEOUT * hz, + mpssas_tm_timeout, tm); + + targ->aborts++; + + 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; +} + + +static void +mpssas_scsiio_timeout(void *data) +{ + struct mps_softc *sc; + struct mps_command *cm; + struct mpssas_target *targ; + + cm = (struct mps_command *)data; + sc = cm->cm_sc; + + KKASSERT(lockstatus(&sc->mps_lock, curthread) != 0); + + 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; + } + + if (cm->cm_ccb == NULL) { + mps_printf(sc, "command timeout with NULL ccb\n"); + return; + } + + mpssas_log_command(cm, "command timeout cm %p ccb %p\n", + cm, cm->cm_ccb); + + targ = cm->cm_targ; + targ->timeouts++; + + /* XXX first, check the firmware state, to see if it's still + * operational. if not, do a diag reset. + */ + + 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 (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); + + /* 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); + } + +} + +static void +mpssas_action_scsiio(struct mpssas_softc *sassc, union ccb *ccb) +{ + MPI2_SCSI_IO_REQUEST *req; + struct ccb_scsiio *csio; + struct mps_softc *sc; + struct mpssas_target *targ; + struct mpssas_lun *lun; + struct mps_command *cm; + 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); + KKASSERT(lockstatus(&sc->mps_lock, curthread) != 0); + + csio = &ccb->csio; + targ = &sassc->targets[csio->ccb_h.target_id]; + if (targ->handle == 0x0) { + 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; + } + + cm = mps_alloc_command(sc); + if (cm == NULL) { + if ((sassc->flags & MPSSAS_QUEUE_FROZEN) == 0) { + xpt_freeze_simq(sassc->sim, 1); + sassc->flags |= MPSSAS_QUEUE_FROZEN; + } + ccb->ccb_h.status &= ~CAM_SIM_QUEUED; + ccb->ccb_h.status |= CAM_REQUEUE_REQ; + xpt_done(ccb); + return; + } + + req = (MPI2_SCSI_IO_REQUEST *)cm->cm_req; + bzero(req, sizeof(*req)); + req->DevHandle = targ->handle; + req->Function = MPI2_FUNCTION_SCSI_IO_REQUEST; + req->MsgFlags = 0; + req->SenseBufferLowAddress = cm->cm_sense_busaddr; + req->SenseBufferLength = MPS_SENSE_LEN; + req->SGLFlags = 0; + req->ChainOffset = 0; + req->SGLOffset0 = 24; /* 32bit word offset to the SGL */ + req->SGLOffset1= 0; + req->SGLOffset2= 0; + req->SGLOffset3= 0; + req->SkipCount = 0; + req->DataLength = csio->dxfer_len; + req->BidirectionalDataLength = 0; + req->IoFlags = csio->cdb_len; + req->EEDPFlags = 0; + + /* Note: BiDirectional transfers are not supported */ + switch (csio->ccb_h.flags & CAM_DIR_MASK) { + case CAM_DIR_IN: + req->Control = MPI2_SCSIIO_CONTROL_READ; + cm->cm_flags |= MPS_CM_FLAGS_DATAIN; + break; + case CAM_DIR_OUT: + req->Control = MPI2_SCSIIO_CONTROL_WRITE; + cm->cm_flags |= MPS_CM_FLAGS_DATAOUT; + break; + case CAM_DIR_NONE: + default: + req->Control = MPI2_SCSIIO_CONTROL_NODATATRANSFER; + break; + } + + /* + * It looks like the hardware doesn't require an explicit tag + * number for each transaction. SAM Task Management not supported + * at the moment. + */ + switch (csio->tag_action) { + case MSG_HEAD_OF_Q_TAG: + req->Control |= MPI2_SCSIIO_CONTROL_HEADOFQ; + break; + case MSG_ORDERED_Q_TAG: + req->Control |= MPI2_SCSIIO_CONTROL_ORDEREDQ; + break; + case MSG_ACA_TASK: + req->Control |= MPI2_SCSIIO_CONTROL_ACAQ; + break; + case CAM_TAG_ACTION_NONE: + case MSG_SIMPLE_Q_TAG: + default: + 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); + ccb->ccb_h.status = CAM_LUN_INVALID; + xpt_done(ccb); + return; + } + + if (csio->ccb_h.flags & CAM_CDB_POINTER) + bcopy(csio->cdb_io.cdb_ptr, &req->CDB.CDB32[0], csio->cdb_len); + else + bcopy(csio->cdb_io.cdb_bytes, &req->CDB.CDB32[0],csio->cdb_len); + req->IoFlags = csio->cdb_len; + + /* + * 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; + cm->cm_sglsize = (32 - 24) * 4; + cm->cm_desc.SCSIIO.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO; + cm->cm_desc.SCSIIO.DevHandle = targ->handle; + 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; + + /* + * 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; + } + } + + 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; +} + +static void +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; + 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 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); + KKASSERT(lockstatus(&sc->mps_lock, curthread) != 0); + + 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, + * assuming it doesn't cause problems. + */ + 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); + } + + 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, + * so we're getting a callback without queueing the command + * to the hardware. So we set the status here, and it will + * be retained below. We'll go through the "fast path", + * because there can be no reply when we haven't actually + * gone out to the hardware. + */ + ccb->ccb_h.status |= CAM_REQUEUE_REQ; + + /* + * Currently the only error included in the mask is + * MPS_CM_FLAGS_CHAIN_FAILED, which means we're out of + * chain frames. We need to freeze the queue until we get + * a command that completed without this error, which will + * hopefully have some chain frames attached that we can + * use. If we wanted to get smarter about it, we would + * only unfreeze the queue in this condition when we're + * sure that we're getting some chain frames back. That's + * probably unnecessary. + */ + if ((sassc->flags & MPSSAS_QUEUE_FROZEN) == 0) { + xpt_freeze_simq(sassc->sim, 1); + sassc->flags |= MPSSAS_QUEUE_FROZEN; + mps_dprint(sc, MPS_INFO, "Error sending command, " + "freezing SIM queue\n"); + } + } + + /* Take the fast path to completion */ + if (cm->cm_reply == NULL) { + if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INPROG) { + 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"); + } + } + + /* + * 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); + } + mps_free_command(sc, cm); + xpt_done(ccb); + return; + } + + 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_SCSI_DATA_UNDERRUN: + csio->resid = cm->cm_length - rep->TransferCount; + /* FALLTHROUGH */ + case MPI2_IOCSTATUS_SUCCESS: + case MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR: + + 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: + /* + * 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: + /* + * This is one of the responses that comes back when an I/O + * has been aborted. If it is because of a timeout that we + * initiated, just set the status to CAM_CMD_TIMEOUT. + * Otherwise set it to CAM_REQ_ABORTED. The effect on the + * command is the same (it gets retried, subject to the + * retry counter), the only difference is what gets printed + * on the console. + */ + if (cm->cm_state == MPS_CM_STATE_TIMEDOUT) + ccb->ccb_h.status = CAM_CMD_TIMEOUT; + 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: + /* + * 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; + 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: + case MPI2_IOCSTATUS_INVALID_VPID: + case MPI2_IOCSTATUS_INVALID_FIELD: + case MPI2_IOCSTATUS_INVALID_STATE: + case MPI2_IOCSTATUS_OP_STATE_NOT_SUPPORTED: + case MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR: + case MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR: + 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 (sassc->flags & MPSSAS_QUEUE_FROZEN) { + ccb->ccb_h.status |= CAM_RELEASE_SIMQ; + sassc->flags &= ~MPSSAS_QUEUE_FROZEN; + 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) +{ + MPI2_SMP_PASSTHROUGH_REPLY *rpl; + MPI2_SMP_PASSTHROUGH_REQUEST *req; + uint64_t sasaddr; + union ccb *ccb; + + ccb = cm->cm_complete_data; + + /* + * Currently there should be no way we can hit this case. It only + * happens when we have a failure to allocate chain frames, and SMP + * commands require two S/G elements only. That should be handled + * in the standard request size. + */ + if ((cm->cm_flags & MPS_CM_FLAGS_ERROR_MASK) != 0) { + mps_printf(sc, "%s: cm_flags = %#x on SMP request!\n", + __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) { + mps_dprint(sc, MPS_INFO, "%s: NULL cm_reply!\n", __func__); + ccb->ccb_h.status = CAM_REQ_CMP_ERR; + goto bailout; + } + + req = (MPI2_SMP_PASSTHROUGH_REQUEST *)cm->cm_req; + sasaddr = le32toh(req->SASAddress.Low); + sasaddr |= ((uint64_t)(le32toh(req->SASAddress.High))) << 32; + + if ((rpl->IOCStatus & MPI2_IOCSTATUS_MASK) != MPI2_IOCSTATUS_SUCCESS || + rpl->SASStatus != MPI2_SASSTATUS_SUCCESS) { + mps_dprint(sc, MPS_INFO, "%s: IOCStatus %04x SASStatus %02x\n", + __func__, rpl->IOCStatus, rpl->SASStatus); + ccb->ccb_h.status = CAM_REQ_CMP_ERR; + goto bailout; + } + + mps_dprint(sc, MPS_INFO, "%s: SMP request to SAS address " + "%#jx completed successfully\n", __func__, + (uintmax_t)sasaddr); + + if (ccb->smpio.smp_response[2] == SMP_FR_ACCEPTED) + ccb->ccb_h.status = CAM_REQ_CMP; + else + ccb->ccb_h.status = CAM_SMP_STATUS_ERROR; + +bailout: + /* + * We sync in both directions because we had DMAs in the S/G list + * in both directions. + */ + bus_dmamap_sync(sc->buffer_dmat, cm->cm_dmamap, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->buffer_dmat, cm->cm_dmamap); + mps_free_command(sc, cm); + xpt_done(ccb); +} + +static void +mpssas_send_smpcmd(struct mpssas_softc *sassc, union ccb *ccb, uint64_t sasaddr) +{ + struct mps_command *cm; + uint8_t *request, *response; + MPI2_SMP_PASSTHROUGH_REQUEST *req; + struct mps_softc *sc; + struct sglist *sg; + int error; + + sc = sassc->sc; + sg = NULL; + error = 0; + + /* + * XXX We don't yet support physical addresses here. + */ + if (ccb->ccb_h.flags & (CAM_DATA_PHYS|CAM_SG_LIST_PHYS)) { + mps_printf(sc, "%s: physical addresses not supported\n", + __func__); + ccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(ccb); + return; + } + + /* + * If the user wants to send an S/G list, check to make sure they + * have single buffers. + */ + if (ccb->ccb_h.flags & CAM_SCATTER_VALID) { + /* + * The chip does not support more than one buffer for the + * request or response. + */ + if ((ccb->smpio.smp_request_sglist_cnt > 1) + || (ccb->smpio.smp_response_sglist_cnt > 1)) { + mps_printf(sc, "%s: multiple request or response " + "buffer segments not supported for SMP\n", + __func__); + ccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(ccb); + return; + } + + /* + * The CAM_SCATTER_VALID flag was originally implemented + * for the XPT_SCSI_IO CCB, which only has one data pointer. + * We have two. So, just take that flag to mean that we + * might have S/G lists, and look at the S/G segment count + * to figure out whether that is the case for each individual + * buffer. + */ + if (ccb->smpio.smp_request_sglist_cnt != 0) { + bus_dma_segment_t *req_sg; + + req_sg = (bus_dma_segment_t *)ccb->smpio.smp_request; + request = (uint8_t *)req_sg[0].ds_addr; + } else + request = ccb->smpio.smp_request; + + if (ccb->smpio.smp_response_sglist_cnt != 0) { + bus_dma_segment_t *rsp_sg; + + rsp_sg = (bus_dma_segment_t *)ccb->smpio.smp_response; + response = (uint8_t *)rsp_sg[0].ds_addr; + } else + response = ccb->smpio.smp_response; + } else { + request = ccb->smpio.smp_request; + response = ccb->smpio.smp_response; + } + + cm = mps_alloc_command(sc); + if (cm == NULL) { + mps_printf(sc, "%s: cannot allocate command\n", __func__); + ccb->ccb_h.status = CAM_RESRC_UNAVAIL; + xpt_done(ccb); + return; + } + + req = (MPI2_SMP_PASSTHROUGH_REQUEST *)cm->cm_req; + bzero(req, sizeof(*req)); + req->Function = MPI2_FUNCTION_SMP_PASSTHROUGH; + + /* Allow the chip to use any route to this SAS address. */ + req->PhysicalPort = 0xff; + + req->RequestDataLength = ccb->smpio.smp_request_len; + req->SGLFlags = + MPI2_SGLFLAGS_SYSTEM_ADDRESS_SPACE | MPI2_SGLFLAGS_SGL_TYPE_MPI; + + mps_dprint(sc, MPS_INFO, "%s: sending SMP request to SAS " + "address %#jx\n", __func__, (uintmax_t)sasaddr); + + mpi_init_sge(cm, req, &req->SGL); + + /* + * Set up a uio to pass into mps_map_command(). This allows us to + * do one map command, and one busdma call in there. + */ + cm->cm_uio.uio_iov = cm->cm_iovec; + cm->cm_uio.uio_iovcnt = 2; + cm->cm_uio.uio_segflg = UIO_SYSSPACE; + + /* + * The read/write flag isn't used by busdma, but set it just in + * case. This isn't exactly accurate, either, since we're going in + * both directions. + */ + cm->cm_uio.uio_rw = UIO_WRITE; + + cm->cm_iovec[0].iov_base = request; + cm->cm_iovec[0].iov_len = req->RequestDataLength; + cm->cm_iovec[1].iov_base = response; + cm->cm_iovec[1].iov_len = ccb->smpio.smp_response_len; + + cm->cm_uio.uio_resid = cm->cm_iovec[0].iov_len + + cm->cm_iovec[1].iov_len; + + /* + * Trigger a warning message in mps_data_cb() for the user if we + * wind up exceeding two S/G segments. The chip expects one + * segment for the request and another for the response. + */ + cm->cm_max_segs = 2; + + cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; + cm->cm_complete = mpssas_smpio_complete; + cm->cm_complete_data = ccb; + + /* + * Tell the mapping code that we're using a uio, and that this is + * an SMP passthrough request. There is a little special-case + * logic there (in mps_data_cb()) to handle the bidirectional + * transfer. + */ + cm->cm_flags |= MPS_CM_FLAGS_USE_UIO | MPS_CM_FLAGS_SMP_PASS | + MPS_CM_FLAGS_DATAIN | MPS_CM_FLAGS_DATAOUT; + + /* The chip data format is little endian. */ + req->SASAddress.High = htole32(sasaddr >> 32); + req->SASAddress.Low = htole32(sasaddr); + + /* + * XXX Note that we don't have a timeout/abort mechanism here. + * From the manual, it looks like task management requests only + * work for SCSI IO and SATA passthrough requests. We may need to + * have a mechanism to retry requests in the event of a chip reset + * at least. Hopefully the chip will insure that any errors short + * of that are relayed back to the driver. + */ + error = mps_map_command(sc, cm); + if ((error != 0) && (error != EINPROGRESS)) { + mps_printf(sc, "%s: error %d returned from mps_map_command()\n", + __func__, error); + goto bailout_error; + } + + return; + +bailout_error: + mps_free_command(sc, cm); + ccb->ccb_h.status = CAM_RESRC_UNAVAIL; + xpt_done(ccb); + return; + +} + +static void +mpssas_action_smpio(struct mpssas_softc *sassc, union ccb *ccb) +{ + struct mps_softc *sc; + struct mpssas_target *targ; + uint64_t sasaddr = 0; + + sc = sassc->sc; + + /* + * Make sure the target exists. + */ + targ = &sassc->targets[ccb->ccb_h.target_id]; + if (targ->handle == 0x0) { + mps_printf(sc, "%s: target %d does not exist!\n", __func__, + ccb->ccb_h.target_id); + ccb->ccb_h.status = CAM_SEL_TIMEOUT; + xpt_done(ccb); + return; + } + + /* + * If this device has an embedded SMP target, we'll talk to it + * directly. + * figure out what the expander's address is. + */ + if ((targ->devinfo & MPI2_SAS_DEVICE_INFO_SMP_TARGET) != 0) + sasaddr = targ->sasaddr; + + /* + * If we don't have a SAS address for the expander yet, try + * grabbing it from the page 0x83 information cached in the + * transport layer for this target. LSI expanders report the + * expander SAS address as the port-associated SAS address in + * Inquiry VPD page 0x83. Maxim expanders don't report it in page + * 0x83. + * + * XXX KDM disable this for now, but leave it commented out so that + * it is obvious that this is another possible way to get the SAS + * address. + * + * The parent handle method below is a little more reliable, and + * the other benefit is that it works for devices other than SES + * devices. So you can send a SMP request to a da(4) device and it + * will get routed to the expander that device is attached to. + * (Assuming the da(4) device doesn't contain an SMP target...) + */ +#if 0 + if (sasaddr == 0) + sasaddr = xpt_path_sas_addr(ccb->ccb_h.path); +#endif + + /* + * If we still don't have a SAS address for the expander, look for + * 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 " + "parent handle!\n", __func__, targ->handle); + ccb->ccb_h.status = CAM_REQ_INVALID; + goto bailout; + } +#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 " + "parent target!\n", __func__, targ->handle); + ccb->ccb_h.status = CAM_REQ_INVALID; + goto bailout; + } + + if ((parent_target->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, parent_target->handle); + ccb->ccb_h.status = CAM_REQ_INVALID; + goto bailout; + + } + + 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) { + mps_printf(sc, "%s: unable to find SAS address for handle %d\n", + __func__, targ->handle); + ccb->ccb_h.status = CAM_REQ_INVALID; + goto bailout; + } + mpssas_send_smpcmd(sassc, ccb, sasaddr); + + return; + +bailout: + xpt_done(ccb); + +} +#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 *tm; + struct mpssas_target *targ; + + mps_dprint(sassc->sc, MPS_TRACE, __func__); + KKASSERT(lockstatus(&sassc->sc->mps_lock, curthread) != 0); + + 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; + } + + 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; + + 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 *tm) +{ + MPI2_SCSI_TASK_MANAGE_REPLY *resp; + union ccb *ccb; + + mps_dprint(sc, MPS_TRACE, __func__); + KKASSERT(lockstatus(&sc->mps_lock, curthread) != 0); + + resp = (MPI2_SCSI_TASK_MANAGE_REPLY *)tm->cm_reply; + ccb = tm->cm_complete_data; + + /* + * 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 *)tm->cm_req; + + mps_printf(sc, "%s: cm_flags = %#x for reset of handle %#04x! " + "This should not happen!\n", __func__, tm->cm_flags, + req->DevHandle); + ccb->ccb_h.status = CAM_REQ_CMP_ERR; + goto bailout; + } + + kprintf("%s: IOCStatus = 0x%x ResponseCode = 0x%x\n", __func__, + resp->IOCStatus, resp->ResponseCode); + + 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_free_tm(sc, tm); + xpt_done(ccb); +} + +static void +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_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; + + KKASSERT(lockstatus(&sassc->sc->mps_lock, curthread) != 0); + + 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 (;;) { + lksleep(&sassc->ccb_scanq, &sc->mps_lock, 0, "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__); + + KKASSERT(lockstatus(&sassc->sc->mps_lock, curthread) != 0); + + 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 = kmalloc(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 = + kmalloc(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 = kmalloc(sizeof(union ccb), M_TEMP, + M_WAITOK | M_ZERO); + + 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"); + kfree(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 = kmalloc(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"); + kfree(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 { + kfree(rcap_buf, M_MPT2); + xpt_free_path(ccb->ccb_h.path); + xpt_free_ccb(ccb); + } + } while (found_periph); + } +} + + +static void +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. + kfree(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/raid/mps/mps_sas.h b/sys/dev/raid/mps/mps_sas.h new file mode 100644 index 0000000000..f926dec292 --- /dev/null +++ b/sys/dev/raid/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: src/sys/dev/mps/mps_sas.h,v 1.1 2012/01/26 18:17:21 ken Exp $ + */ + +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 thread *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/raid/mps/mps_sas_lsi.c b/sys/dev/raid/mps/mps_sas_lsi.c new file mode 100644 index 0000000000..51b3916686 --- /dev/null +++ b/sys/dev/raid/mps/mps_sas_lsi.c @@ -0,0 +1,862 @@ +/*- + * 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: src/sys/dev/mps/mps_sas_lsi.c,v 1.1 2012/01/26 18:17:21 ken Exp $ + */ + +/* Communications core for LSI MPT2 */ + +/* TODO Move headers to mpsvar */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 = kmalloc(sizeof(struct mps_fw_event_work), M_MPT2, + M_ZERO|M_NOWAIT); + if (!fw_event) { + kprintf("%s: allocate failed for fw_event\n", __func__); + return; + } + sz = le16toh(event->EventDataLength) * 4; + fw_event->event_data = kmalloc(sz, M_MPT2, M_ZERO|M_NOWAIT); + if (!fw_event->event_data) { + kprintf("%s: allocate failed for event_data\n", __func__); + kfree(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) +{ + + kfree(fw_event->event_data, M_MPT2); + kfree(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)){ + kprintf("%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)) { + kprintf("%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) { + kprintf("%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); + kprintf("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)){ + kprintf("%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", (u_int)event_data->PreviousValue, + (u_int)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", (u_int)event_data->PreviousValue, + (u_int)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", (u_int)event_data->PreviousValue, + (u_int)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", (u_int)event_data->PreviousValue, + (u_int)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", + (u_int)event_data->PreviousValue, (u_int)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", + (u_int)event_data->PreviousValue, (u_int)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: + kprintf("WarpDrive Warning: IO Throttling has " + "occurred in the WarpDrive subsystem. " + "Check WarpDrive documentation for " + "additional details\n"); + break; + case MPI2_WD_DRIVE_LIFE_WARN: + kprintf("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: + kprintf("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: + kprintf("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))) { + kprintf("%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)))) { + kprintf("%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) { + kprintf("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 = kmalloc( 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 */ + kprintf("%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) { + kprintf("%s: error reading SATA PASSTHRU; iocstatus = 0x%x\n", + __func__, reply->IOCStatus); + error = ENXIO; + goto out; + } +out: + mps_free_command(sc, cm); + kfree(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) { + kprintf("%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) { + kprintf("%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) { + kprintf("%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/disk/mps/mps_table.c b/sys/dev/raid/mps/mps_table.c similarity index 94% rename from sys/dev/disk/mps/mps_table.c rename to sys/dev/raid/mps/mps_table.c index adffe509f0..c2344d7763 100644 --- a/sys/dev/disk/mps/mps_table.c +++ b/sys/dev/raid/mps/mps_table.c @@ -23,11 +23,12 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/mps/mps_table.c,v 1.1 2010/09/10 15:03:56 ken Exp $ + * $FreeBSD: src/sys/dev/mps/mps_table.c,v 1.2 2012/01/26 18:17:21 ken Exp $ */ /* Debugging tables for MPT2 */ +/* TODO Move headers to mpsvar */ #include #include #include @@ -35,23 +36,30 @@ #include #include #include +#include #include #include #include #include +#include +#include +#include #include -#include + +#include #include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include char * mps_describe_table(struct mps_table_lookup *table, u_int code) @@ -263,8 +271,7 @@ mps_print_sasdev0(struct mps_softc *sc, MPI2_CONFIG_PAGE_SAS_DEV_0 *buf) MPS_PRINTFIELD(sc, buf, DevHandle, 0x%x); MPS_PRINTFIELD(sc, buf, AttachedPhyIdentifier, 0x%x); MPS_PRINTFIELD(sc, buf, ZoneGroup, %d); - mps_dprint_field(sc, MPS_INFO, "DeviceInfo: %b,%s\n", - (u_int)buf->DeviceInfo, + mps_dprint_field(sc, MPS_INFO, "DeviceInfo: %b,%s\n", (int)buf->DeviceInfo, "\20" "\4SataHost" "\5SmpInit" "\6StpInit" "\7SspInit" "\10SataDev" "\11SmpTarg" "\12StpTarg" "\13SspTarg" "\14Direct" "\15LsiDev" "\16AtapiDev" "\17SepDev", @@ -297,7 +304,7 @@ mps_print_evt_sas(struct mps_softc *sc, MPI2_EVENT_NOTIFICATION_REPLY *event) mps_describe_table(mps_sasdisc_reason, data->ReasonCode)); MPS_EVENTFIELD(sc, data, PhysicalPort, %d); mps_dprint_field(sc, MPS_EVENT, "DiscoveryStatus: %b\n", - (u_int)data->DiscoveryStatus, "\20" + (int)data->DiscoveryStatus, "\20" "\1Loop" "\2UnaddressableDev" "\3DupSasAddr" "\5SmpTimeout" "\6ExpRouteFull" "\7RouteIndexError" "\10SmpFailed" "\11SmpCrcError" "\12SubSubLink" "\13TableTableLink" @@ -395,8 +402,7 @@ mps_print_expander1(struct mps_softc *sc, MPI2_CONFIG_PAGE_EXPANDER_1 *buf) mps_describe_table(mps_phyinfo_reason_names, (buf->PhyInfo >> 16) & 0xf), (u_int)buf->PhyInfo); mps_dprint_field(sc, MPS_INFO, "AttachedDeviceInfo: %b,%s\n", - (u_int)buf->AttachedDeviceInfo, - "\20" "\4SATAhost" "\5SMPinit" "\6STPinit" + (int)buf->AttachedDeviceInfo, "\20" "\4SATAhost" "\5SMPinit" "\6STPinit" "\7SSPinit" "\10SATAdev" "\11SMPtarg" "\12STPtarg" "\13SSPtarg" "\14Direct" "\15LSIdev" "\16ATAPIdev" "\17SEPdev", mps_describe_table(mps_sasdev0_devtype, @@ -408,8 +414,7 @@ mps_print_expander1(struct mps_softc *sc, MPI2_CONFIG_PAGE_EXPANDER_1 *buf) buf->NegotiatedLinkRate & 0xf), buf->NegotiatedLinkRate); MPS_PRINTFIELD(sc, buf, PhyIdentifier, %d); MPS_PRINTFIELD(sc, buf, AttachedPhyIdentifier, %d); - mps_dprint_field(sc, MPS_INFO, "DiscoveryInfo: 0x%x\n", - (u_int)buf->DiscoveryInfo); + MPS_PRINTFIELD(sc, buf, DiscoveryInfo, 0x%x); mps_dprint_field(sc, MPS_INFO, "AttachedPhyInfo: 0x%x\n", (u_int)buf->AttachedPhyInfo); mps_dprint_field(sc, MPS_INFO, "AttachedPhyInfo Reason: %s (0x%x)\n", @@ -462,8 +467,8 @@ mps_print_sgl(struct mps_softc *sc, struct mps_command *cm, int offset) while (frame != NULL) { flags = sge->FlagsLength >> MPI2_SGE_FLAGS_SHIFT; - kprintf("seg%d flags=0x%x len=0x%lx addr=0x%jx\n", i, flags, - sge->FlagsLength & 0xffffff, mps_to_u64(&sge->Address)); + kprintf("seg%d flags=0x%x len=0x%x addr=0x%jx\n", i, flags, + (u_int)(sge->FlagsLength & 0xffffff), mps_to_u64(&sge->Address)); if (flags & (MPI2_SGE_FLAGS_END_OF_LIST | MPI2_SGE_FLAGS_END_OF_BUFFER)) break; diff --git a/sys/dev/disk/mps/mps_table.h b/sys/dev/raid/mps/mps_table.h similarity index 100% rename from sys/dev/disk/mps/mps_table.h rename to sys/dev/raid/mps/mps_table.h diff --git a/sys/dev/raid/mps/mps_user.c b/sys/dev/raid/mps/mps_user.c new file mode 100644 index 0000000000..76ba6d9e3f --- /dev/null +++ b/sys/dev/raid/mps/mps_user.c @@ -0,0 +1,2399 @@ +/*- + * Copyright (c) 2008 Yahoo!, Inc. + * All rights reserved. + * Written by: John Baldwin + * + * 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. + * 3. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 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: src/sys/dev/mps/mps_user.c,v 1.10 2012/01/26 18:17:21 ken Exp $ + */ + +#include "opt_compat.h" + +/* TODO Move headers to mpsvar */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static d_open_t mps_open; +static d_close_t mps_close; +static d_ioctl_t mps_ioctl_devsw; + +static struct dev_ops mps_ops = { + { "mps", 0, 0 }, + .d_open = mps_open, + .d_close = mps_close, + .d_ioctl = mps_ioctl_devsw, +}; + +typedef int (mps_user_f)(struct mps_command *, struct mps_usr_command *); +static mps_user_f mpi_pre_ioc_facts; +static mps_user_f mpi_pre_port_facts; +static mps_user_f mpi_pre_fw_download; +static mps_user_f mpi_pre_fw_upload; +static mps_user_f mpi_pre_sata_passthrough; +static mps_user_f mpi_pre_smp_passthrough; +static mps_user_f mpi_pre_config; +static mps_user_f mpi_pre_sas_io_unit_control; + +static int mps_user_read_cfg_header(struct mps_softc *, + struct mps_cfg_page_req *); +static int mps_user_read_cfg_page(struct mps_softc *, + struct mps_cfg_page_req *, void *); +static int mps_user_read_extcfg_header(struct mps_softc *, + struct mps_ext_cfg_page_req *); +static int mps_user_read_extcfg_page(struct mps_softc *, + struct mps_ext_cfg_page_req *, void *); +static int mps_user_write_cfg_page(struct mps_softc *, + struct mps_cfg_page_req *, void *); +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) +{ + int unit; + + unit = device_get_unit(sc->mps_dev); + sc->mps_cdev = make_dev(&mps_ops, unit, UID_ROOT, GID_OPERATOR, 0640, + "mps%d", unit); + if (sc->mps_cdev == NULL) { + return (ENOMEM); + } + sc->mps_cdev->si_drv1 = sc; + return (0); +} + +void +mps_detach_user(struct mps_softc *sc) +{ + + /* XXX: do a purge of pending requests? */ + destroy_dev(sc->mps_cdev); + +} + +static int +mps_open(struct dev_open_args *ap) +{ + + return (0); +} + +static int +mps_close(struct dev_close_args *ap) +{ + + return (0); +} + +static int +mps_user_read_cfg_header(struct mps_softc *sc, + struct mps_cfg_page_req *page_req) +{ + MPI2_CONFIG_PAGE_HEADER *hdr; + struct mps_config_params params; + int error; + + hdr = ¶ms.hdr.Struct; + params.action = MPI2_CONFIG_ACTION_PAGE_HEADER; + params.page_address = le32toh(page_req->page_address); + hdr->PageVersion = 0; + hdr->PageLength = 0; + hdr->PageNumber = page_req->header.PageNumber; + hdr->PageType = page_req->header.PageType; + params.buffer = NULL; + params.length = 0; + params.callback = NULL; + + if ((error = mps_read_config_page(sc, ¶ms)) != 0) { + /* + * Leave the request. Without resetting the chip, it's + * still owned by it and we'll just get into trouble + * freeing it now. Mark it as abandoned so that if it + * shows up later it can be freed. + */ + mps_printf(sc, "read_cfg_header timed out\n"); + return (ETIMEDOUT); + } + + page_req->ioc_status = htole16(params.status); + if ((page_req->ioc_status & MPI2_IOCSTATUS_MASK) == + MPI2_IOCSTATUS_SUCCESS) { + bcopy(hdr, &page_req->header, sizeof(page_req->header)); + } + + return (0); +} + +static int +mps_user_read_cfg_page(struct mps_softc *sc, struct mps_cfg_page_req *page_req, + void *buf) +{ + MPI2_CONFIG_PAGE_HEADER *reqhdr, *hdr; + struct mps_config_params params; + int error; + + reqhdr = buf; + hdr = ¶ms.hdr.Struct; + hdr->PageVersion = reqhdr->PageVersion; + hdr->PageLength = reqhdr->PageLength; + hdr->PageNumber = reqhdr->PageNumber; + hdr->PageType = reqhdr->PageType & MPI2_CONFIG_PAGETYPE_MASK; + params.action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; + params.page_address = le32toh(page_req->page_address); + params.buffer = buf; + params.length = le32toh(page_req->len); + params.callback = NULL; + + if ((error = mps_read_config_page(sc, ¶ms)) != 0) { + mps_printf(sc, "mps_user_read_cfg_page timed out\n"); + return (ETIMEDOUT); + } + + page_req->ioc_status = htole16(params.status); + return (0); +} + +static int +mps_user_read_extcfg_header(struct mps_softc *sc, + struct mps_ext_cfg_page_req *ext_page_req) +{ + MPI2_CONFIG_EXTENDED_PAGE_HEADER *hdr; + struct mps_config_params params; + int error; + + hdr = ¶ms.hdr.Ext; + params.action = MPI2_CONFIG_ACTION_PAGE_HEADER; + hdr->PageVersion = ext_page_req->header.PageVersion; + hdr->ExtPageLength = 0; + hdr->PageNumber = ext_page_req->header.PageNumber; + hdr->ExtPageType = ext_page_req->header.ExtPageType; + params.page_address = le32toh(ext_page_req->page_address); + if ((error = mps_read_config_page(sc, ¶ms)) != 0) { + /* + * Leave the request. Without resetting the chip, it's + * still owned by it and we'll just get into trouble + * freeing it now. Mark it as abandoned so that if it + * shows up later it can be freed. + */ + mps_printf(sc, "mps_user_read_extcfg_header timed out\n"); + return (ETIMEDOUT); + } + + ext_page_req->ioc_status = htole16(params.status); + if ((ext_page_req->ioc_status & MPI2_IOCSTATUS_MASK) == + MPI2_IOCSTATUS_SUCCESS) { + ext_page_req->header.PageVersion = hdr->PageVersion; + ext_page_req->header.PageNumber = hdr->PageNumber; + ext_page_req->header.PageType = hdr->PageType; + ext_page_req->header.ExtPageLength = hdr->ExtPageLength; + ext_page_req->header.ExtPageType = hdr->ExtPageType; + } + + return (0); +} + +static int +mps_user_read_extcfg_page(struct mps_softc *sc, + struct mps_ext_cfg_page_req *ext_page_req, void *buf) +{ + MPI2_CONFIG_EXTENDED_PAGE_HEADER *reqhdr, *hdr; + struct mps_config_params params; + int error; + + reqhdr = buf; + hdr = ¶ms.hdr.Ext; + params.action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; + params.page_address = le32toh(ext_page_req->page_address); + hdr->PageVersion = reqhdr->PageVersion; + hdr->PageNumber = reqhdr->PageNumber; + hdr->ExtPageType = reqhdr->ExtPageType; + hdr->ExtPageLength = reqhdr->ExtPageLength; + params.buffer = buf; + params.length = le32toh(ext_page_req->len); + params.callback = NULL; + + if ((error = mps_read_config_page(sc, ¶ms)) != 0) { + mps_printf(sc, "mps_user_read_extcfg_page timed out\n"); + return (ETIMEDOUT); + } + + ext_page_req->ioc_status = htole16(params.status); + return (0); +} + +static int +mps_user_write_cfg_page(struct mps_softc *sc, + struct mps_cfg_page_req *page_req, void *buf) +{ + MPI2_CONFIG_PAGE_HEADER *reqhdr, *hdr; + struct mps_config_params params; + u_int hdr_attr; + int error; + + reqhdr = buf; + hdr = ¶ms.hdr.Struct; + hdr_attr = reqhdr->PageType & MPI2_CONFIG_PAGEATTR_MASK; + if (hdr_attr != MPI2_CONFIG_PAGEATTR_CHANGEABLE && + hdr_attr != MPI2_CONFIG_PAGEATTR_PERSISTENT) { + mps_printf(sc, "page type 0x%x not changeable\n", + reqhdr->PageType & MPI2_CONFIG_PAGETYPE_MASK); + return (EINVAL); + } + + /* + * There isn't any point in restoring stripped out attributes + * if you then mask them going down to issue the request. + */ + + hdr->PageVersion = reqhdr->PageVersion; + hdr->PageLength = reqhdr->PageLength; + hdr->PageNumber = reqhdr->PageNumber; + hdr->PageType = reqhdr->PageType; + params.action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT; + params.page_address = le32toh(page_req->page_address); + params.buffer = buf; + params.length = le32toh(page_req->len); + params.callback = NULL; + + if ((error = mps_write_config_page(sc, ¶ms)) != 0) { + mps_printf(sc, "mps_write_cfg_page timed out\n"); + return (ETIMEDOUT); + } + + page_req->ioc_status = htole16(params.status); + return (0); +} + +void +mpi_init_sge(struct mps_command *cm, void *req, void *sge) +{ + int off, space; + + space = (int)cm->cm_sc->facts->IOCRequestFrameSize * 4; + off = (uintptr_t)sge - (uintptr_t)req; + + KASSERT(off < space, ("bad pointers %p %p, off %d, space %d", + req, sge, off, space)); + + cm->cm_sge = sge; + cm->cm_sglsize = space - off; +} + +/* + * Prepare the mps_command for an IOC_FACTS request. + */ +static int +mpi_pre_ioc_facts(struct mps_command *cm, struct mps_usr_command *cmd) +{ + MPI2_IOC_FACTS_REQUEST *req = (void *)cm->cm_req; + MPI2_IOC_FACTS_REPLY *rpl; + + if (cmd->req_len != sizeof *req) + return (EINVAL); + if (cmd->rpl_len != sizeof *rpl) + return (EINVAL); + + cm->cm_sge = NULL; + cm->cm_sglsize = 0; + return (0); +} + +/* + * Prepare the mps_command for a PORT_FACTS request. + */ +static int +mpi_pre_port_facts(struct mps_command *cm, struct mps_usr_command *cmd) +{ + MPI2_PORT_FACTS_REQUEST *req = (void *)cm->cm_req; + MPI2_PORT_FACTS_REPLY *rpl; + + if (cmd->req_len != sizeof *req) + return (EINVAL); + if (cmd->rpl_len != sizeof *rpl) + return (EINVAL); + + cm->cm_sge = NULL; + cm->cm_sglsize = 0; + return (0); +} + +/* + * Prepare the mps_command for a FW_DOWNLOAD request. + */ +static int +mpi_pre_fw_download(struct mps_command *cm, struct mps_usr_command *cmd) +{ + MPI2_FW_DOWNLOAD_REQUEST *req = (void *)cm->cm_req; + MPI2_FW_DOWNLOAD_REPLY *rpl; + MPI2_FW_DOWNLOAD_TCSGE tc; + int error; + + /* + * This code assumes there is room in the request's SGL for + * the TransactionContext plus at least a SGL chain element. + */ + CTASSERT(sizeof req->SGL >= sizeof tc + MPS_SGC_SIZE); + + if (cmd->req_len != sizeof *req) + return (EINVAL); + if (cmd->rpl_len != sizeof *rpl) + return (EINVAL); + + if (cmd->len == 0) + return (EINVAL); + + error = copyin(cmd->buf, cm->cm_data, cmd->len); + if (error != 0) + return (error); + + mpi_init_sge(cm, req, &req->SGL); + bzero(&tc, sizeof tc); + + /* + * For now, the F/W image must be provided in a single request. + */ + if ((req->MsgFlags & MPI2_FW_DOWNLOAD_MSGFLGS_LAST_SEGMENT) == 0) + return (EINVAL); + if (req->TotalImageSize != cmd->len) + return (EINVAL); + + /* + * The value of the first two elements is specified in the + * Fusion-MPT Message Passing Interface document. + */ + tc.ContextSize = 0; + tc.DetailsLength = 12; + tc.ImageOffset = 0; + tc.ImageSize = cmd->len; + + cm->cm_flags |= MPS_CM_FLAGS_DATAOUT; + + return (mps_push_sge(cm, &tc, sizeof tc, 0)); +} + +/* + * Prepare the mps_command for a FW_UPLOAD request. + */ +static int +mpi_pre_fw_upload(struct mps_command *cm, struct mps_usr_command *cmd) +{ + MPI2_FW_UPLOAD_REQUEST *req = (void *)cm->cm_req; + MPI2_FW_UPLOAD_REPLY *rpl; + MPI2_FW_UPLOAD_TCSGE tc; + + /* + * This code assumes there is room in the request's SGL for + * the TransactionContext plus at least a SGL chain element. + */ + CTASSERT(sizeof req->SGL >= sizeof tc + MPS_SGC_SIZE); + + if (cmd->req_len != sizeof *req) + return (EINVAL); + if (cmd->rpl_len != sizeof *rpl) + return (EINVAL); + + mpi_init_sge(cm, req, &req->SGL); + if (cmd->len == 0) { + /* Perhaps just asking what the size of the fw is? */ + return (0); + } + + bzero(&tc, sizeof tc); + + /* + * The value of the first two elements is specified in the + * Fusion-MPT Message Passing Interface document. + */ + tc.ContextSize = 0; + tc.DetailsLength = 12; + /* + * XXX Is there any reason to fetch a partial image? I.e. to + * set ImageOffset to something other than 0? + */ + tc.ImageOffset = 0; + tc.ImageSize = cmd->len; + + return (mps_push_sge(cm, &tc, sizeof tc, 0)); +} + +/* + * Prepare the mps_command for a SATA_PASSTHROUGH request. + */ +static int +mpi_pre_sata_passthrough(struct mps_command *cm, struct mps_usr_command *cmd) +{ + MPI2_SATA_PASSTHROUGH_REQUEST *req = (void *)cm->cm_req; + MPI2_SATA_PASSTHROUGH_REPLY *rpl; + + if (cmd->req_len != sizeof *req) + return (EINVAL); + if (cmd->rpl_len != sizeof *rpl) + return (EINVAL); + + mpi_init_sge(cm, req, &req->SGL); + return (0); +} + +/* + * Prepare the mps_command for a SMP_PASSTHROUGH request. + */ +static int +mpi_pre_smp_passthrough(struct mps_command *cm, struct mps_usr_command *cmd) +{ + MPI2_SMP_PASSTHROUGH_REQUEST *req = (void *)cm->cm_req; + MPI2_SMP_PASSTHROUGH_REPLY *rpl; + + if (cmd->req_len != sizeof *req) + return (EINVAL); + if (cmd->rpl_len != sizeof *rpl) + return (EINVAL); + + mpi_init_sge(cm, req, &req->SGL); + return (0); +} + +/* + * Prepare the mps_command for a CONFIG request. + */ +static int +mpi_pre_config(struct mps_command *cm, struct mps_usr_command *cmd) +{ + MPI2_CONFIG_REQUEST *req = (void *)cm->cm_req; + MPI2_CONFIG_REPLY *rpl; + + if (cmd->req_len != sizeof *req) + return (EINVAL); + if (cmd->rpl_len != sizeof *rpl) + return (EINVAL); + + mpi_init_sge(cm, req, &req->PageBufferSGE); + return (0); +} + +/* + * Prepare the mps_command for a SAS_IO_UNIT_CONTROL request. + */ +static int +mpi_pre_sas_io_unit_control(struct mps_command *cm, + struct mps_usr_command *cmd) +{ + + cm->cm_sge = NULL; + cm->cm_sglsize = 0; + return (0); +} + +/* + * A set of functions to prepare an mps_command for the various + * supported requests. + */ +struct mps_user_func { + U8 Function; + mps_user_f *f_pre; +} mps_user_func_list[] = { + { MPI2_FUNCTION_IOC_FACTS, mpi_pre_ioc_facts }, + { MPI2_FUNCTION_PORT_FACTS, mpi_pre_port_facts }, + { MPI2_FUNCTION_FW_DOWNLOAD, mpi_pre_fw_download }, + { MPI2_FUNCTION_FW_UPLOAD, mpi_pre_fw_upload }, + { MPI2_FUNCTION_SATA_PASSTHROUGH, mpi_pre_sata_passthrough }, + { MPI2_FUNCTION_SMP_PASSTHROUGH, mpi_pre_smp_passthrough}, + { MPI2_FUNCTION_CONFIG, mpi_pre_config}, + { MPI2_FUNCTION_SAS_IO_UNIT_CONTROL, mpi_pre_sas_io_unit_control }, + { 0xFF, NULL } /* list end */ +}; + +static int +mps_user_setup_request(struct mps_command *cm, struct mps_usr_command *cmd) +{ + MPI2_REQUEST_HEADER *hdr = (MPI2_REQUEST_HEADER *)cm->cm_req; + struct mps_user_func *f; + + for (f = mps_user_func_list; f->f_pre != NULL; f++) { + if (hdr->Function == f->Function) + return (f->f_pre(cm, cmd)); + } + return (EINVAL); +} + +static int +mps_user_command(struct mps_softc *sc, struct mps_usr_command *cmd) +{ + MPI2_REQUEST_HEADER *hdr; + MPI2_DEFAULT_REPLY *rpl; + void *buf = NULL; + struct mps_command *cm = NULL; + int err = 0; + int sz; + + mps_lock(sc); + cm = mps_alloc_command(sc); + + if (cm == NULL) { + mps_printf(sc, "mps_user_command: no mps requests\n"); + err = ENOMEM; + goto Ret; + } + mps_unlock(sc); + + hdr = (MPI2_REQUEST_HEADER *)cm->cm_req; + + mps_dprint(sc, MPS_INFO, "mps_user_command: req %p %d rpl %p %d\n", + cmd->req, cmd->req_len, cmd->rpl, cmd->rpl_len ); + + if (cmd->req_len > (int)sc->facts->IOCRequestFrameSize * 4) { + err = EINVAL; + goto RetFreeUnlocked; + } + err = copyin(cmd->req, hdr, cmd->req_len); + if (err != 0) + goto RetFreeUnlocked; + + mps_dprint(sc, MPS_INFO, "mps_user_command: Function %02X " + "MsgFlags %02X\n", hdr->Function, hdr->MsgFlags ); + + err = mps_user_setup_request(cm, cmd); + if (err != 0) { + mps_printf(sc, "mps_user_command: unsupported function 0x%X\n", + hdr->Function ); + goto RetFreeUnlocked; + } + + if (cmd->len > 0) { + buf = kmalloc(cmd->len, M_MPSUSER, M_WAITOK|M_ZERO); + cm->cm_data = buf; + cm->cm_length = cmd->len; + } else { + cm->cm_data = NULL; + cm->cm_length = 0; + } + + cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE; + cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; + + mps_lock(sc); + err = mps_wait_command(sc, cm, 0); + + if (err) { + mps_printf(sc, "%s: invalid request: error %d\n", + __func__, err); + goto Ret; + } + + rpl = (MPI2_DEFAULT_REPLY *)cm->cm_reply; + sz = rpl->MsgLength * 4; + + if (sz > cmd->rpl_len) { + mps_printf(sc, + "mps_user_command: reply buffer too small %d required %d\n", + cmd->rpl_len, sz ); + err = EINVAL; + sz = cmd->rpl_len; + } + + mps_unlock(sc); + copyout(rpl, cmd->rpl, sz); + if (buf != NULL) + copyout(buf, cmd->buf, cmd->len); + mps_dprint(sc, MPS_INFO, "mps_user_command: reply size %d\n", sz ); + +RetFreeUnlocked: + mps_lock(sc); + if (cm != NULL) + mps_free_command(sc, cm); +Ret: + mps_unlock(sc); + if (buf != NULL) + kfree(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 = kmalloc(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) + kfree(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)) + kprintf("%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, + (u_int)reply->IOCLogInfo, (u_int)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, (u_int)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 */ + &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) +{ + struct mps_softc *sc; + struct mps_cfg_page_req *page_req; + struct mps_ext_cfg_page_req *ext_page_req; + void *mps_page; + int error, reset_loop; + + mps_page = NULL; + sc = dev->si_drv1; + page_req = (void *)arg; + ext_page_req = (void *)arg; + + switch (cmd) { + case MPSIO_READ_CFG_HEADER: + mps_lock(sc); + error = mps_user_read_cfg_header(sc, page_req); + mps_unlock(sc); + break; + case MPSIO_READ_CFG_PAGE: + mps_page = kmalloc(page_req->len, M_MPSUSER, M_WAITOK | M_ZERO); + error = copyin(page_req->buf, mps_page, + sizeof(MPI2_CONFIG_PAGE_HEADER)); + if (error) + break; + mps_lock(sc); + error = mps_user_read_cfg_page(sc, page_req, mps_page); + mps_unlock(sc); + if (error) + break; + error = copyout(mps_page, page_req->buf, page_req->len); + break; + case MPSIO_READ_EXT_CFG_HEADER: + mps_lock(sc); + error = mps_user_read_extcfg_header(sc, ext_page_req); + mps_unlock(sc); + break; + case MPSIO_READ_EXT_CFG_PAGE: + mps_page = kmalloc(ext_page_req->len, M_MPSUSER, M_WAITOK|M_ZERO); + error = copyin(ext_page_req->buf, mps_page, + sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER)); + if (error) + break; + mps_lock(sc); + error = mps_user_read_extcfg_page(sc, ext_page_req, mps_page); + mps_unlock(sc); + if (error) + break; + error = copyout(mps_page, ext_page_req->buf, ext_page_req->len); + break; + case MPSIO_WRITE_CFG_PAGE: + mps_page = kmalloc(page_req->len, M_MPSUSER, M_WAITOK|M_ZERO); + error = copyin(page_req->buf, mps_page, page_req->len); + if (error) + break; + mps_lock(sc); + error = mps_user_write_cfg_page(sc, page_req, mps_page); + mps_unlock(sc); + break; + 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) { + kprintf("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; + } + + if (mps_page != NULL) + kfree(mps_page, M_MPSUSER); + + return (error); +} + +#ifdef COMPAT_FREEBSD32 + +struct mps_cfg_page_req32 { + MPI2_CONFIG_PAGE_HEADER header; + uint32_t page_address; + uint32_t buf; + int len; + uint16_t ioc_status; +}; + +struct mps_ext_cfg_page_req32 { + MPI2_CONFIG_EXTENDED_PAGE_HEADER header; + uint32_t page_address; + uint32_t buf; + int len; + uint16_t ioc_status; +}; + +struct mps_raid_action32 { + uint8_t action; + uint8_t volume_bus; + uint8_t volume_id; + uint8_t phys_disk_num; + uint32_t action_data_word; + uint32_t buf; + int len; + uint32_t volume_status; + uint32_t action_data[4]; + uint16_t action_status; + uint16_t ioc_status; + uint8_t write; +}; + +struct mps_usr_command32 { + uint32_t req; + uint32_t req_len; + uint32_t rpl; + uint32_t rpl_len; + uint32_t buf; + int len; + uint32_t flags; +}; + +#define MPSIO_READ_CFG_HEADER32 _IOWR('M', 200, struct mps_cfg_page_req32) +#define MPSIO_READ_CFG_PAGE32 _IOWR('M', 201, struct mps_cfg_page_req32) +#define MPSIO_READ_EXT_CFG_HEADER32 _IOWR('M', 202, struct mps_ext_cfg_page_req32) +#define MPSIO_READ_EXT_CFG_PAGE32 _IOWR('M', 203, struct mps_ext_cfg_page_req32) +#define MPSIO_WRITE_CFG_PAGE32 _IOWR('M', 204, struct mps_cfg_page_req32) +#define MPSIO_RAID_ACTION32 _IOWR('M', 205, struct mps_raid_action32) +#define MPSIO_MPS_COMMAND32 _IOWR('M', 210, struct mps_usr_command32) + +static int +mps_ioctl32(struct cdev *dev, u_long cmd32, void *_arg, int flag, + struct thread *td) +{ + struct mps_cfg_page_req32 *page32 = _arg; + struct mps_ext_cfg_page_req32 *ext32 = _arg; + struct mps_raid_action32 *raid32 = _arg; + struct mps_usr_command32 *user32 = _arg; + union { + struct mps_cfg_page_req page; + struct mps_ext_cfg_page_req ext; + struct mps_raid_action raid; + struct mps_usr_command user; + } arg; + u_long cmd; + int error; + + switch (cmd32) { + case MPSIO_READ_CFG_HEADER32: + case MPSIO_READ_CFG_PAGE32: + case MPSIO_WRITE_CFG_PAGE32: + if (cmd32 == MPSIO_READ_CFG_HEADER32) + cmd = MPSIO_READ_CFG_HEADER; + else if (cmd32 == MPSIO_READ_CFG_PAGE32) + cmd = MPSIO_READ_CFG_PAGE; + else + cmd = MPSIO_WRITE_CFG_PAGE; + CP(*page32, arg.page, header); + CP(*page32, arg.page, page_address); + PTRIN_CP(*page32, arg.page, buf); + CP(*page32, arg.page, len); + CP(*page32, arg.page, ioc_status); + break; + + case MPSIO_READ_EXT_CFG_HEADER32: + case MPSIO_READ_EXT_CFG_PAGE32: + if (cmd32 == MPSIO_READ_EXT_CFG_HEADER32) + cmd = MPSIO_READ_EXT_CFG_HEADER; + else + cmd = MPSIO_READ_EXT_CFG_PAGE; + CP(*ext32, arg.ext, header); + CP(*ext32, arg.ext, page_address); + PTRIN_CP(*ext32, arg.ext, buf); + CP(*ext32, arg.ext, len); + CP(*ext32, arg.ext, ioc_status); + break; + + case MPSIO_RAID_ACTION32: + cmd = MPSIO_RAID_ACTION; + CP(*raid32, arg.raid, action); + CP(*raid32, arg.raid, volume_bus); + CP(*raid32, arg.raid, volume_id); + CP(*raid32, arg.raid, phys_disk_num); + CP(*raid32, arg.raid, action_data_word); + PTRIN_CP(*raid32, arg.raid, buf); + CP(*raid32, arg.raid, len); + CP(*raid32, arg.raid, volume_status); + bcopy(raid32->action_data, arg.raid.action_data, + sizeof arg.raid.action_data); + CP(*raid32, arg.raid, ioc_status); + CP(*raid32, arg.raid, write); + break; + + case MPSIO_MPS_COMMAND32: + cmd = MPSIO_MPS_COMMAND; + PTRIN_CP(*user32, arg.user, req); + CP(*user32, arg.user, req_len); + PTRIN_CP(*user32, arg.user, rpl); + CP(*user32, arg.user, rpl_len); + PTRIN_CP(*user32, arg.user, buf); + CP(*user32, arg.user, len); + CP(*user32, arg.user, flags); + break; + default: + return (ENOIOCTL); + } + + error = mps_ioctl(dev, cmd, &arg, flag, td); + if (error == 0 && (cmd32 & IOC_OUT) != 0) { + switch (cmd32) { + case MPSIO_READ_CFG_HEADER32: + case MPSIO_READ_CFG_PAGE32: + case MPSIO_WRITE_CFG_PAGE32: + CP(arg.page, *page32, header); + CP(arg.page, *page32, page_address); + PTROUT_CP(arg.page, *page32, buf); + CP(arg.page, *page32, len); + CP(arg.page, *page32, ioc_status); + break; + + case MPSIO_READ_EXT_CFG_HEADER32: + case MPSIO_READ_EXT_CFG_PAGE32: + CP(arg.ext, *ext32, header); + CP(arg.ext, *ext32, page_address); + PTROUT_CP(arg.ext, *ext32, buf); + CP(arg.ext, *ext32, len); + CP(arg.ext, *ext32, ioc_status); + break; + + case MPSIO_RAID_ACTION32: + CP(arg.raid, *raid32, action); + CP(arg.raid, *raid32, volume_bus); + CP(arg.raid, *raid32, volume_id); + CP(arg.raid, *raid32, phys_disk_num); + CP(arg.raid, *raid32, action_data_word); + PTROUT_CP(arg.raid, *raid32, buf); + CP(arg.raid, *raid32, len); + CP(arg.raid, *raid32, volume_status); + bcopy(arg.raid.action_data, raid32->action_data, + sizeof arg.raid.action_data); + CP(arg.raid, *raid32, ioc_status); + CP(arg.raid, *raid32, write); + break; + + case MPSIO_MPS_COMMAND32: + PTROUT_CP(arg.user, *user32, req); + CP(arg.user, *user32, req_len); + PTROUT_CP(arg.user, *user32, rpl); + CP(arg.user, *user32, rpl_len); + PTROUT_CP(arg.user, *user32, buf); + CP(arg.user, *user32, len); + CP(arg.user, *user32, flags); + break; + } + } + + return (error); +} +#endif /* COMPAT_FREEBSD32 */ + +static int +mps_ioctl_devsw(struct dev_ioctl_args *ap) +{ + cdev_t dev = ap->a_head.a_dev; + u_long com = ap->a_cmd; + caddr_t arg = ap->a_data; + int flag = ap->a_fflag; + +#ifdef COMPAT_FREEBSD32 + if (SV_CURPROC_FLAG(SV_ILP32)) + return (mps_ioctl32(dev, com, arg, flag, td)); +#endif + return (mps_ioctl(dev, com, arg, flag)); +} diff --git a/sys/dev/raid/mps/mpsvar.h b/sys/dev/raid/mps/mpsvar.h new file mode 100644 index 0000000000..17d853da43 --- /dev/null +++ b/sys/dev/raid/mps/mpsvar.h @@ -0,0 +1,768 @@ +/*- + * Copyright (c) 2009 Yahoo! Inc. + * 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. + * + * $FreeBSD: src/sys/dev/mps/mpsvar.h,v 1.9 2012/01/26 18:17:21 ken Exp $ + */ +/*- + * 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: src/sys/dev/mps/mpsvar.h,v 1.9 2012/01/26 18:17:21 ken Exp $ + */ + +#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 +#define MPS_EVT_REPLY_FRAMES 32 +#define MPS_REPLY_FRAMES MPS_REQ_FRAMES +#define MPS_CHAIN_FRAMES 2048 +#define MPS_SENSE_LEN SSD_FULL_SIZE +#define MPS_MSI_COUNT 1 +#define MPS_SGE64_SIZE 12 +#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); + +typedef void mps_evt_callback_t(struct mps_softc *, uintptr_t, + MPI2_EVENT_NOTIFICATION_REPLY *reply); +typedef void mps_command_callback_t(struct mps_softc *, struct mps_command *cm); + +struct mps_chain { + TAILQ_ENTRY(mps_chain) chain_link; + MPI2_SGE_IO_UNION *chain; + uint32_t chain_busaddr; +}; + +/* + * This needs to be at least 2 to support SMP passthrough. + */ +#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; + u_int cm_sglsize; + MPI2_SGE_IO_UNION *cm_sge; + uint8_t *cm_req; + uint8_t *cm_reply; + uint32_t cm_reply_data; + mps_command_callback_t *cm_complete; + 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) +#define MPS_CM_FLAGS_SGE_SIMPLE (1 << 2) +#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_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) +#define MPS_CM_FLAGS_ERROR_MASK MPS_CM_FLAGS_CHAIN_FAILED + u_int cm_state; +#define MPS_CM_STATE_FREE 0 +#define MPS_CM_STATE_BUSY 1 +#define MPS_CM_STATE_TIMEDOUT 2 + bus_dmamap_t cm_dmamap; + struct scsi_sense_data *cm_sense; + TAILQ_HEAD(, mps_chain) cm_chain_list; + uint32_t cm_req_busaddr; + uint32_t cm_sense_busaddr; + 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; + void *data; + uint8_t mask[16]; +}; + +struct mps_softc { + device_t mps_dev; + struct cdev *mps_cdev; + u_int mps_flags; +#define MPS_FLAGS_INTX (1 << 0) +#define MPS_FLAGS_MSI (1 << 1) +#define MPS_FLAGS_BUSY (1 << 2) +#define MPS_FLAGS_SHUTDOWN (1 << 3) +#define MPS_FLAGS_DIAGRESET (1 << 4) +#define MPS_FLAGS_ATTACH_DONE (1 << 5) +#define MPS_FLAGS_WD_AVAILABLE (1 << 6) + u_int mps_debug; +#if 0 /* XXX swildner */ + u_int disable_msix; +#endif + u_int enable_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; + + 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; + int replypostindex; + int replyfreeindex; + + struct resource *mps_regs_resource; + bus_space_handle_t mps_bhandle; + bus_space_tag_t mps_btag; + int mps_regs_rid; + + bus_dma_tag_t mps_parent_dmat; + bus_dma_tag_t buffer_dmat; + + MPI2_IOC_FACTS_REPLY *facts; + MPI2_PORT_FACTS_REPLY *pfacts; + int num_reqs; + int num_replies; + int fqdepth; /* Free queue */ + int pqdepth; /* Post queue */ + + uint8_t event_mask[16]; + TAILQ_HEAD(, mps_event_handle) event_list; + struct mps_event_handle *mps_log_eh; + + struct lock mps_lock; + struct intr_config_hook mps_ich; + struct resource *mps_irq[MPS_MSI_COUNT]; + void *mps_intrhand[MPS_MSI_COUNT]; + int mps_irq_rid[MPS_MSI_COUNT]; + int mps_irq_type[MPS_MSI_COUNT]; + + uint8_t *req_frames; + bus_addr_t req_busaddr; + bus_dma_tag_t req_dmat; + bus_dmamap_t req_map; + + uint8_t *reply_frames; + bus_addr_t reply_busaddr; + bus_dma_tag_t reply_dmat; + bus_dmamap_t reply_map; + + struct scsi_sense_data *sense_frames; + bus_addr_t sense_busaddr; + bus_dma_tag_t sense_dmat; + bus_dmamap_t sense_map; + + uint8_t *chain_frames; + bus_addr_t chain_busaddr; + bus_dma_tag_t chain_dmat; + bus_dmamap_t chain_map; + + MPI2_REPLY_DESCRIPTORS_UNION *post_queue; + bus_addr_t post_busaddr; + uint32_t *free_queue; + 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 { + MPI2_CONFIG_EXT_PAGE_HEADER_UNION hdr; + u_int action; + u_int page_address; /* Attributes, not a phys address */ + u_int status; + void *buffer; + u_int length; + int timeout; + void (*callback)(struct mps_softc *, 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) +{ + return (bus_space_read_4(sc->mps_btag, sc->mps_bhandle, offset)); +} + +static __inline void +mps_regwrite(struct mps_softc *sc, uint32_t offset, uint32_t val) +{ + bus_space_write_4(sc->mps_btag, sc->mps_bhandle, offset, 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; + mps_regwrite(sc, MPI2_REPLY_FREE_HOST_INDEX_OFFSET, sc->replyfreeindex); +} + +static __inline struct mps_chain * +mps_alloc_chain(struct mps_softc *sc) +{ + struct mps_chain *chain; + + if ((chain = TAILQ_FIRST(&sc->chain_list)) != NULL) { + TAILQ_REMOVE(&sc->chain_list, chain, chain_link); + sc->chain_free--; + if (sc->chain_free < sc->chain_free_lowwater) + sc->chain_free_lowwater = sc->chain_free; + } +#if __FreeBSD_version >= 900030 + else + sc->chain_alloc_fail++; +#endif + return (chain); +} + +static __inline void +mps_free_chain(struct mps_softc *sc, struct mps_chain *chain) +{ +#if 0 + bzero(chain->chain, 128); +#endif + sc->chain_free++; + TAILQ_INSERT_TAIL(&sc->chain_list, chain, chain_link); +} + +static __inline void +mps_free_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_max_segs = 0; + cm->cm_lun = 0; + cm->cm_state = MPS_CM_STATE_FREE; + TAILQ_FOREACH_MUTABLE(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->req_list, cm, cm_link); +} + +static __inline struct mps_command * +mps_alloc_command(struct mps_softc *sc) +{ + struct mps_command *cm; + + cm = TAILQ_FIRST(&sc->req_list); + if (cm == NULL) + return (NULL); + + TAILQ_REMOVE(&sc->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_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_MUTABLE(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) +{ + lockmgr(&sc->mps_lock, LK_EXCLUSIVE); +} + +static __inline void +mps_unlock(struct mps_softc *sc) +{ + lockmgr(&sc->mps_lock, LK_RELEASE); +} + +#define MPS_INFO (1 << 0) +#define MPS_TRACE (1 << 1) +#define MPS_FAULT (1 << 2) +#define MPS_EVENT (1 << 3) +#define MPS_LOG (1 << 4) + +#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) \ + device_printf(sc->mps_dev, msg, ##args); \ +} while (0) + +#define mps_dprint_field(sc, level, msg, args...) \ +do { \ + if (sc->mps_debug & level) \ + kprintf("\t" msg, ##args); \ +} while (0) + +#define MPS_PRINTFIELD_START(sc, tag...) \ + mps_dprint((sc), MPS_INFO, ##tag); \ + mps_dprint_field((sc), MPS_INFO, ":\n") +#define MPS_PRINTFIELD_END(sc, tag) \ + mps_dprint((sc), MPS_INFO, tag "\n") +#define MPS_PRINTFIELD(sc, facts, attr, fmt) \ + mps_dprint_field((sc), MPS_INFO, #attr ": " #fmt "\n", (facts)->attr) + +#define MPS_EVENTFIELD_START(sc, tag...) \ + mps_dprint((sc), MPS_EVENT, ##tag); \ + mps_dprint_field((sc), MPS_EVENT, ":\n") +#define MPS_EVENTFIELD(sc, facts, attr, fmt) \ + mps_dprint_field((sc), MPS_EVENT, #attr ": " #fmt "\n", (facts)->attr) + +static __inline void +mps_from_u64(uint64_t data, U64 *mps) +{ + (mps)->High = (uint32_t)((data) >> 32); + (mps)->Low = (uint32_t)((data) & 0xffffffff); +} + +static __inline uint64_t +mps_to_u64(U64 *data) +{ + + return (((uint64_t)data->High << 32) | data->Low); +} + +static __inline void +mps_mask_intr(struct mps_softc *sc) +{ + uint32_t mask; + + mask = mps_regread(sc, MPI2_HOST_INTERRUPT_MASK_OFFSET); + mask |= MPI2_HIM_REPLY_INT_MASK; + mps_regwrite(sc, MPI2_HOST_INTERRUPT_MASK_OFFSET, mask); +} + +static __inline void +mps_unmask_intr(struct mps_softc *sc) +{ + uint32_t mask; + + mask = mps_regread(sc, MPI2_HOST_INTERRUPT_MASK_OFFSET); + mask &= ~MPI2_HIM_REPLY_INT_MASK; + mps_regwrite(sc, MPI2_HOST_INTERRUPT_MASK_OFFSET, mask); +} + +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 *); +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_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_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); + +#define mps_kproc_create(func, farg, proc_ptr, flags, stackpgs, fmtstr, arg) \ + kthread_create(func, farg, proc_ptr, fmtstr, arg) +#define mps_kproc_exit(arg) kthread_exit() + +#if defined(CAM_PRIORITY_XPT) +#define MPS_PRIORITY_XPT CAM_PRIORITY_XPT +#else +#define MPS_PRIORITY_XPT 5 +#endif +#endif -- 2.41.0