From aed5247ff899ec457005d93dfbdb4ffd74574695 Mon Sep 17 00:00:00 2001 From: "Joshua M. Clulow" Date: Thu, 7 Mar 2013 11:48:32 -0800 Subject: [PATCH] 4018 mpt_sas: allow physical topology enumeration in libtopo 4019 mpt_sas: expose LED controls to libtopo Reviewed by: Keith Wesolowski Reviewed by: Hans Rosenfeld Reviewed by: Albert Lee Approved by: Albert Lee --- usr/src/lib/fm/topo/libtopo/common/topo_hc.h | 5 + usr/src/lib/fm/topo/libtopo/common/topo_xml.c | 24 +- usr/src/lib/fm/topo/modules/common/Makefile | 2 +- usr/src/lib/fm/topo/modules/common/disk/Makefile | 5 +- usr/src/lib/fm/topo/modules/common/disk/disk.c | 47 +++- usr/src/lib/fm/topo/modules/common/disk/disk.h | 12 + .../lib/fm/topo/modules/common/disk/disk_drivers.h | 31 ++ .../lib/fm/topo/modules/common/disk/disk_mptsas.c | 120 ++++++++ .../common/{disk => fac_prov_mptsas}/Makefile | 9 +- .../common/fac_prov_mptsas/fac_prov_mptsas.c | 244 ++++++++++++++++ usr/src/pkg/manifests/service-fault-management.mf | 1 + .../uts/common/io/scsi/adapters/mpt_sas/mptsas.c | 311 ++++++++++++++++----- .../sys/scsi/adapters/mpt_sas/mptsas_ioctl.h | 57 ++++ .../common/sys/scsi/adapters/mpt_sas/mptsas_var.h | 2 + 14 files changed, 772 insertions(+), 98 deletions(-) create mode 100644 usr/src/lib/fm/topo/modules/common/disk/disk_drivers.h create mode 100644 usr/src/lib/fm/topo/modules/common/disk/disk_mptsas.c copy usr/src/lib/fm/topo/modules/common/{disk => fac_prov_mptsas}/Makefile (86%) create mode 100644 usr/src/lib/fm/topo/modules/common/fac_prov_mptsas/fac_prov_mptsas.c diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_hc.h b/usr/src/lib/fm/topo/libtopo/common/topo_hc.h index 1c3a92f804..7b29adad69 100644 --- a/usr/src/lib/fm/topo/libtopo/common/topo_hc.h +++ b/usr/src/lib/fm/topo/libtopo/common/topo_hc.h @@ -21,6 +21,7 @@ /* * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, Joyent, Inc. All rights reserved. */ #ifndef _TOPO_HC_H @@ -115,6 +116,10 @@ extern "C" { #define TOPO_PGROUP_BINDING "binding" #define TOPO_BINDING_OCCUPANT "occupant-path" +#define TOPO_BINDING_DRIVER "driver" +#define TOPO_BINDING_DEVCTL "devctl" +#define TOPO_BINDING_ENCLOSURE "enclosure" +#define TOPO_BINDING_SLOT "slot" #define TOPO_PGROUP_STORAGE "storage" #define TOPO_STORAGE_INITIATOR_PORT "initiator-port" diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_xml.c b/usr/src/lib/fm/topo/libtopo/common/topo_xml.c index 9e4c18c250..f26d5f6ef9 100644 --- a/usr/src/lib/fm/topo/libtopo/common/topo_xml.c +++ b/usr/src/lib/fm/topo/libtopo/common/topo_xml.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, Joyent, Inc. All rights reserved. */ #include @@ -1376,16 +1377,10 @@ fac_enum_process(topo_mod_t *mp, xmlNodePtr pn, tnode_t *ptn) if ((fprov = xmlGetProp(cn, (xmlChar *)Provider)) == NULL) goto fenumdone; - - if (xmlStrcmp(fprov, (xmlChar *)"fac_prov_ipmi") != 0) { - topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, - "Invalid provider specified: %s\n", fprov); - goto fenumdone; - } - /* - * Invoke enum entry point in fac provider which will cause the - * facility enumeration node method to be registered. + * Invoke enum entry point in facility provider which will + * cause the facility enumeration node method to be + * registered. */ if (fac_enum_run(mp, ptn, (const char *)fprov) != 0) { topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, @@ -1440,12 +1435,6 @@ fac_process(topo_mod_t *mp, xmlNodePtr pn, tf_rdata_t *rd, tnode_t *ptn) xmlStrcmp(ftype, (xmlChar *)Indicator) != 0) goto facdone; - if (xmlStrcmp(provider, (xmlChar *)"fac_prov_ipmi") != 0) { - topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "fac_process: " - "Invalid provider attr value: %s\n", provider); - goto facdone; - } - if ((ntn = topo_node_facbind(mp, ptn, (char *)fname, (char *)ftype)) == NULL) goto facdone; @@ -1463,8 +1452,9 @@ fac_process(topo_mod_t *mp, xmlNodePtr pn, tf_rdata_t *rd, tnode_t *ptn) } } /* - * Invoke enum entry point in fac_prov_ipmi module, which will - * cause the provider methods to be registered on this node + * Invoke enum entry point in the facility provider module, + * which will cause the provider methods to be registered on + * this node */ if (fac_enum_run(mp, ntn, (const char *)provider) != 0) { topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "fac_process: " diff --git a/usr/src/lib/fm/topo/modules/common/Makefile b/usr/src/lib/fm/topo/modules/common/Makefile index 0b0f965a70..fa38496755 100644 --- a/usr/src/lib/fm/topo/modules/common/Makefile +++ b/usr/src/lib/fm/topo/modules/common/Makefile @@ -23,11 +23,11 @@ # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -#pragma ident "%Z%%M% %I% %E% SMI" SUBDIRS = \ disk \ fac_prov_ipmi \ + fac_prov_mptsas \ ipmi \ ses \ xfp diff --git a/usr/src/lib/fm/topo/modules/common/disk/Makefile b/usr/src/lib/fm/topo/modules/common/disk/Makefile index b4821a6a82..4b4c965050 100644 --- a/usr/src/lib/fm/topo/modules/common/disk/Makefile +++ b/usr/src/lib/fm/topo/modules/common/disk/Makefile @@ -22,14 +22,15 @@ # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -#pragma ident "%Z%%M% %I% %E% SMI" MODULE = disk CLASS = common -MODULESRCS = disk.c disk_common.c +MODULESRCS = disk.c disk_common.c disk_mptsas.c include ../../Makefile.plugin +CPPFLAGS += -I$(SRC)/uts/common + LDLIBS += -ldevinfo -ldevid -lcfgadm -ldiskstatus diff --git a/usr/src/lib/fm/topo/modules/common/disk/disk.c b/usr/src/lib/fm/topo/modules/common/disk/disk.c index 427402c934..ec53198906 100644 --- a/usr/src/lib/fm/topo/modules/common/disk/disk.c +++ b/usr/src/lib/fm/topo/modules/common/disk/disk.c @@ -21,6 +21,9 @@ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. */ +/* + * Copyright (c) 2013, Joyent, Inc. All rights reserved. + */ #include #include @@ -33,6 +36,7 @@ #include #include #include "disk.h" +#include "disk_drivers.h" static int disk_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t, topo_instance_t, void *, void *); @@ -43,13 +47,38 @@ static const topo_modops_t disk_ops = static const topo_modinfo_t disk_info = {DISK, FM_FMRI_SCHEME_HC, DISK_VERSION, &disk_ops}; +static int +disk_declare_driver(topo_mod_t *mod, tnode_t *baynode, topo_list_t *dlistp, + char *driver) +{ + int err; + + if (strcmp("mpt_sas", driver) == 0) { + char *sas_address = NULL; + tnode_t *child = NULL; + + if ((err = disk_mptsas_find_disk(mod, baynode, + &sas_address)) != 0) + return (err); + + err = disk_declare_addr(mod, baynode, dlistp, + sas_address, &child); + topo_mod_strfree(mod, sas_address); + + return (err); + } + + topo_mod_dprintf(mod, "unknown disk driver '%s'\n", driver); + return (-1); +} + /*ARGSUSED*/ static int disk_enum(topo_mod_t *mod, tnode_t *baynode, const char *name, topo_instance_t min, topo_instance_t max, void *arg, void *notused) { - char *device; + char *device, *driver; int err; nvlist_t *fmri; topo_list_t *dlistp = topo_mod_getspecific(mod); @@ -75,6 +104,22 @@ disk_enum(topo_mod_t *mod, tnode_t *baynode, nvlist_free(fmri); /* + * For internal storage, first check to see if we need to + * request more detail from an HBA driver. + */ + if (topo_prop_get_string(baynode, TOPO_PGROUP_BINDING, + TOPO_BINDING_DRIVER, &driver, &err) == 0) { + err = disk_declare_driver(mod, baynode, dlistp, driver); + + topo_mod_strfree(mod, driver); + return (err); + } else if (err != ETOPO_PROP_NOENT) { + topo_mod_dprintf(mod, "disk_enum: " + "binding error %s\n", topo_strerror(err)); + return (-1); + } + + /* * For internal storage, get the path to the occupant from the * binding group of the bay node */ diff --git a/usr/src/lib/fm/topo/modules/common/disk/disk.h b/usr/src/lib/fm/topo/modules/common/disk/disk.h index 3fabc4c334..e61a54974b 100644 --- a/usr/src/lib/fm/topo/modules/common/disk/disk.h +++ b/usr/src/lib/fm/topo/modules/common/disk/disk.h @@ -22,6 +22,9 @@ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. */ +/* + * Copyright (c) 2013, Joyent, Inc. All rights reserved. + */ #ifndef _DISK_H #define _DISK_H @@ -54,6 +57,15 @@ extern "C" { */ #define TOPO_PGROUP_BINDING "binding" #define TOPO_BINDING_OCCUPANT "occupant-path" +#define TOPO_BINDING_DRIVER "driver" + +/* + * The binding group required in platform specific xml that describes 'bay' + * nodes containing disks attached to an HBA using the 'mpt_sas' driver. + */ +#define TOPO_BINDING_DEVCTL "devctl" +#define TOPO_BINDING_ENCLOSURE "enclosure" +#define TOPO_BINDING_SLOT "slot" /* * device node information. diff --git a/usr/src/lib/fm/topo/modules/common/disk/disk_drivers.h b/usr/src/lib/fm/topo/modules/common/disk/disk_drivers.h new file mode 100644 index 0000000000..6851e2fe27 --- /dev/null +++ b/usr/src/lib/fm/topo/modules/common/disk/disk_drivers.h @@ -0,0 +1,31 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ +/* + * Copyright (c) 2013, Joyent, Inc. All rights reserved. + */ + +#ifndef _DISK_DRIVERS_H +#define _DISK_DRIVERS_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int disk_mptsas_find_disk(topo_mod_t *, tnode_t *, char **); + +#ifdef __cplusplus +} +#endif + +#endif /* _DISK_DRIVERS_H */ diff --git a/usr/src/lib/fm/topo/modules/common/disk/disk_mptsas.c b/usr/src/lib/fm/topo/modules/common/disk/disk_mptsas.c new file mode 100644 index 0000000000..db853c6695 --- /dev/null +++ b/usr/src/lib/fm/topo/modules/common/disk/disk_mptsas.c @@ -0,0 +1,120 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ +/* + * Copyright (c) 2013, Joyent, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "disk.h" +#include "disk_drivers.h" + +static int +get_sas_address(topo_mod_t *mod, char *devctl, uint32_t enclosure, + uint32_t slot, char **sas_address) +{ + int fd, err, i; + mptsas_get_disk_info_t gdi; + mptsas_disk_info_t *di; + size_t disz; + + bzero(&gdi, sizeof (gdi)); + + if ((fd = open(devctl, O_RDWR)) == -1) { + topo_mod_dprintf(mod, "could not open '%s' for ioctl: %s\n", + devctl, strerror(errno)); + return (-1); + } + + if (ioctl(fd, MPTIOCTL_GET_DISK_INFO, &gdi) == -1) { + topo_mod_dprintf(mod, "ioctl 1 on '%s' failed: %s\n", devctl, + strerror(errno)); + (void) close(fd); + return (-1); + } + + gdi.DiskInfoArraySize = disz = sizeof (mptsas_disk_info_t) * + gdi.DiskCount; + gdi.PtrDiskInfoArray = di = topo_mod_alloc(mod, disz); + if (di == NULL) { + topo_mod_dprintf(mod, "memory allocation failed\n"); + (void) close(fd); + return (-1); + } + + if (ioctl(fd, MPTIOCTL_GET_DISK_INFO, &gdi) == -1) { + topo_mod_dprintf(mod, "ioctl 2 on '%s' failed: %s\n", devctl, + strerror(errno)); + topo_mod_free(mod, di, disz); + (void) close(fd); + return (-1); + } + + err = -1; + for (i = 0; i < gdi.DiskCount; i++) { + if (di[i].Enclosure == enclosure && di[i].Slot == slot) { + char sas[17]; /* 16 hex digits and NUL */ + (void) snprintf(sas, 17, "%llx", di[i].SasAddress); + topo_mod_dprintf(mod, "found mpt_sas disk (%d/%d) " + "with adddress %s\n", enclosure, slot, sas); + *sas_address = topo_mod_strdup(mod, sas); + err = 0; + break; + } + } + + topo_mod_free(mod, di, disz); + (void) close(fd); + return (err); +} + +int +disk_mptsas_find_disk(topo_mod_t *mod, tnode_t *baynode, char **sas_address) +{ + char *devctl = NULL; + uint32_t enclosure, slot; + int err; + + /* + * Get the required properties from the node. These come from + * the static XML mapping. + */ + if (topo_prop_get_string(baynode, TOPO_PGROUP_BINDING, + TOPO_BINDING_DEVCTL, &devctl, &err) != 0 || + topo_prop_get_uint32(baynode, TOPO_PGROUP_BINDING, + TOPO_BINDING_ENCLOSURE, &enclosure, &err) != 0 || + topo_prop_get_uint32(baynode, TOPO_PGROUP_BINDING, + TOPO_BINDING_SLOT, &slot, &err) != 0) { + if (devctl != NULL) + topo_mod_strfree(mod, devctl); + topo_mod_dprintf(mod, "bay node was missing mpt_sas binding " + "properties\n"); + return (-1); + } + + return (get_sas_address(mod, devctl, enclosure, slot, sas_address)); + +} diff --git a/usr/src/lib/fm/topo/modules/common/disk/Makefile b/usr/src/lib/fm/topo/modules/common/fac_prov_mptsas/Makefile similarity index 86% copy from usr/src/lib/fm/topo/modules/common/disk/Makefile copy to usr/src/lib/fm/topo/modules/common/fac_prov_mptsas/Makefile index b4821a6a82..31838e1bc0 100644 --- a/usr/src/lib/fm/topo/modules/common/disk/Makefile +++ b/usr/src/lib/fm/topo/modules/common/fac_prov_mptsas/Makefile @@ -21,15 +21,14 @@ # # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. +# Copyright (c) 2013, Joyent, Inc. All rights reserved. # -#pragma ident "%Z%%M% %I% %E% SMI" -MODULE = disk +MODULE = fac_prov_mptsas CLASS = common -MODULESRCS = disk.c disk_common.c +MODULESRCS = fac_prov_mptsas.c include ../../Makefile.plugin -LDLIBS += -ldevinfo -ldevid -lcfgadm -ldiskstatus - +CPPFLAGS += -I$(SRC)/uts/common diff --git a/usr/src/lib/fm/topo/modules/common/fac_prov_mptsas/fac_prov_mptsas.c b/usr/src/lib/fm/topo/modules/common/fac_prov_mptsas/fac_prov_mptsas.c new file mode 100644 index 0000000000..a49a131811 --- /dev/null +++ b/usr/src/lib/fm/topo/modules/common/fac_prov_mptsas/fac_prov_mptsas.c @@ -0,0 +1,244 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2013, Joyent, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sys/scsi/adapters/mpt_sas/mptsas_ioctl.h" + +#define TOPO_METH_MPTSAS_LED_MODE_VERSION 0 + +static int fac_prov_mptsas_enum(topo_mod_t *, tnode_t *, const char *, + topo_instance_t, topo_instance_t, void *, void *); + +/* + * mpt_sas facility provider methods + */ +static int mptsas_led_mode(topo_mod_t *, tnode_t *, topo_version_t, + nvlist_t *, nvlist_t **); + +const topo_modops_t mptsas_ops = { fac_prov_mptsas_enum, NULL }; + +const topo_modinfo_t mptsas_info = + { "mpt_sas facility provider", FM_FMRI_SCHEME_HC, TOPO_VERSION, + &mptsas_ops }; + +static const topo_method_t mptsas_fac_methods[] = { + { "mptsas_led_mode", TOPO_PROP_METH_DESC, + TOPO_METH_MPTSAS_LED_MODE_VERSION, + TOPO_STABILITY_INTERNAL, mptsas_led_mode }, + { NULL } +}; + +/*ARGSUSED*/ +int +_topo_init(topo_mod_t *mod, topo_version_t version) +{ + if (getenv("TOPOFACMPTSASDEBUG") != NULL) + topo_mod_setdebug(mod); + + return (topo_mod_register(mod, &mptsas_info, TOPO_VERSION)); +} + +void +_topo_fini(topo_mod_t *mod) +{ + topo_mod_unregister(mod); +} + +static int +do_led_control(topo_mod_t *mod, char *devctl, uint16_t enclosure, + uint16_t slot, uint8_t led, uint32_t *ledmode, boolean_t set) +{ + int fd; + mptsas_led_control_t lc; + + bzero(&lc, sizeof (lc)); + + lc.Command = set ? MPTSAS_LEDCTL_FLAG_SET : MPTSAS_LEDCTL_FLAG_GET; + lc.Enclosure = enclosure; + lc.Slot = slot; + lc.Led = led; + lc.LedStatus = *ledmode; + + if ((fd = open(devctl, (set ? O_RDWR : O_RDONLY))) == -1) { + topo_mod_dprintf(mod, "devctl open failed: %s", + strerror(errno)); + return (-1); + } + + if (ioctl(fd, MPTIOCTL_LED_CONTROL, &lc) == -1) { + if (errno == ENOENT) { + /* + * If there is not presently a target attached for + * a particular enclosure/slot pair then the driver + * does not track LED status for this bay. Assume + * all LEDs are off. + */ + lc.LedStatus = 0; + } else { + topo_mod_dprintf(mod, "led control ioctl failed: %s", + strerror(errno)); + (void) close(fd); + return (-1); + } + } + + *ledmode = lc.LedStatus ? TOPO_LED_STATE_ON : TOPO_LED_STATE_OFF; + + (void) close(fd); + return (0); +} + +static int +mptsas_led_mode(topo_mod_t *mod, tnode_t *node, topo_version_t vers, + nvlist_t *in, nvlist_t **nvout) +{ + int err, ret = 0; + tnode_t *pnode = topo_node_parent(node); + uint32_t type, ledmode = 0; + nvlist_t *pargs, *nvl; + char *driver = NULL, *devctl = NULL; + uint32_t enclosure, slot; + uint8_t mptsas_led; + boolean_t set; + + if (vers > TOPO_METH_MPTSAS_LED_MODE_VERSION) + return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW)); + + if (topo_prop_get_string(pnode, TOPO_PGROUP_BINDING, + TOPO_BINDING_DRIVER, &driver, &err) != 0 || + strcmp("mpt_sas", driver) != 0) { + topo_mod_dprintf(mod, "%s: Facility driver was not mpt_sas", + __func__); + ret = topo_mod_seterrno(mod, EMOD_NVL_INVAL); + goto out; + } + if (topo_prop_get_uint32(node, TOPO_PGROUP_FACILITY, TOPO_FACILITY_TYPE, + &type, &err) != 0) { + topo_mod_dprintf(mod, "%s: Failed to lookup %s property " + "(%s)", __func__, TOPO_FACILITY_TYPE, topo_strerror(err)); + return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); + } + switch (type) { + case (TOPO_LED_TYPE_SERVICE): + mptsas_led = MPTSAS_LEDCTL_LED_FAIL; + break; + case (TOPO_LED_TYPE_LOCATE): + mptsas_led = MPTSAS_LEDCTL_LED_IDENT; + break; + case (TOPO_LED_TYPE_OK2RM): + mptsas_led = MPTSAS_LEDCTL_LED_OK2RM; + break; + default: + topo_mod_dprintf(mod, "%s: Invalid LED type: 0x%x\n", __func__, + type); + return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); + } + if (topo_prop_get_string(pnode, TOPO_PGROUP_BINDING, + TOPO_BINDING_DEVCTL, &devctl, &err) != 0 || + topo_prop_get_uint32(pnode, TOPO_PGROUP_BINDING, + TOPO_BINDING_ENCLOSURE, &enclosure, &err) != 0 || + topo_prop_get_uint32(pnode, TOPO_PGROUP_BINDING, + TOPO_BINDING_SLOT, &slot, &err) != 0) { + topo_mod_dprintf(mod, "%s: Facility was missing mpt_sas binding" + " properties\n", __func__); + ret = topo_mod_seterrno(mod, EMOD_NVL_INVAL); + goto out; + } + + if ((nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &pargs) == 0) && + nvlist_exists(pargs, TOPO_PROP_VAL_VAL)) { + /* + * Set the LED mode + */ + set = B_TRUE; + if ((ret = nvlist_lookup_uint32(pargs, TOPO_PROP_VAL_VAL, + &ledmode)) != 0) { + topo_mod_dprintf(mod, "%s: Failed to lookup %s nvpair " + "(%s)\n", __func__, TOPO_PROP_VAL_VAL, + strerror(ret)); + ret = topo_mod_seterrno(mod, EMOD_NVL_INVAL); + goto out; + } + topo_mod_dprintf(mod, "%s: Setting LED mode to %s\n", __func__, + ledmode ? "ON" : "OFF"); + } else { + /* + * Get the LED mode + */ + set = B_FALSE; + topo_mod_dprintf(mod, "%s: Getting LED mode\n", __func__); + } + + if (do_led_control(mod, devctl, enclosure, slot, mptsas_led, &ledmode, + set) != 0) { + topo_mod_dprintf(mod, "%s: do_led_control failed", __func__); + ret = topo_mod_seterrno(mod, EMOD_UNKNOWN); + goto out; + } + + if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 || + nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, TOPO_LED_MODE) != 0 || + nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_UINT32) != 0 || + nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL, ledmode) != 0) { + topo_mod_dprintf(mod, "%s: Failed to allocate 'out' nvlist\n", + __func__); + nvlist_free(nvl); + ret = topo_mod_seterrno(mod, EMOD_NOMEM); + goto out; + } + *nvout = nvl; + +out: + if (driver != NULL) + topo_mod_strfree(mod, driver); + if (devctl != NULL) + topo_mod_strfree(mod, devctl); + return (ret); +} + +/*ARGSUSED*/ +static int +fac_prov_mptsas_enum(topo_mod_t *mod, tnode_t *rnode, const char *name, + topo_instance_t min, topo_instance_t max, void *arg, void *unused) +{ + if (topo_node_flags(rnode) == TOPO_NODE_FACILITY) { + if (topo_method_register(mod, rnode, mptsas_fac_methods) != 0) { + topo_mod_dprintf(mod, "%s: topo_method_register() " + "failed: %s", __func__, topo_mod_errmsg(mod)); + return (-1); + } + return (0); + } + + topo_mod_dprintf(mod, "%s: unexpected node flags %x", __func__, + topo_node_flags(rnode)); + return (-1); +} diff --git a/usr/src/pkg/manifests/service-fault-management.mf b/usr/src/pkg/manifests/service-fault-management.mf index 27506c47f6..c2dd3890ee 100644 --- a/usr/src/pkg/manifests/service-fault-management.mf +++ b/usr/src/pkg/manifests/service-fault-management.mf @@ -567,6 +567,7 @@ file path=usr/lib/fm/llib-ltopo.ln variant.opensolaris.zone=__NODEFAULT file path=usr/lib/fm/topo/maps/xfp-hc-topology.xml mode=0444 file path=usr/lib/fm/topo/plugins/disk.so mode=0555 file path=usr/lib/fm/topo/plugins/fac_prov_ipmi.so mode=0555 +file path=usr/lib/fm/topo/plugins/fac_prov_mptsas.so mode=0555 file path=usr/lib/fm/topo/plugins/ipmi.so mode=0555 file path=usr/lib/fm/topo/plugins/ses.so mode=0555 file path=usr/lib/fm/topo/plugins/xfp.so mode=0555 diff --git a/usr/src/uts/common/io/scsi/adapters/mpt_sas/mptsas.c b/usr/src/uts/common/io/scsi/adapters/mpt_sas/mptsas.c index f0c9e520aa..8103ae938a 100644 --- a/usr/src/uts/common/io/scsi/adapters/mpt_sas/mptsas.c +++ b/usr/src/uts/common/io/scsi/adapters/mpt_sas/mptsas.c @@ -22,6 +22,7 @@ /* * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2012 Nexenta Systems, Inc. All rights reserved. + * Copyright (c) 2013, Joyent, Inc. All rights reserved. */ /* @@ -68,6 +69,7 @@ #include #include #include +#include #include #include #include @@ -353,8 +355,7 @@ static dev_info_t *mptsas_get_dip_from_dev(dev_t dev, mptsas_phymask_t *phymask); static mptsas_target_t *mptsas_addr_to_ptgt(mptsas_t *mpt, char *addr, mptsas_phymask_t phymask); -static int mptsas_set_led_status(mptsas_t *mpt, mptsas_target_t *ptgt, - uint32_t slotstatus); +static int mptsas_flush_led_status(mptsas_t *mpt, mptsas_target_t *ptgt); /* @@ -6054,10 +6055,8 @@ mptsas_handle_topo_change(mptsas_topo_change_list_t *topo_node, } mutex_enter(&mpt->m_mutex); - if (mptsas_set_led_status(mpt, ptgt, 0) != DDI_SUCCESS) { - NDBG14(("mptsas: clear LED for tgt %x failed", - ptgt->m_slot_num)); - } + ptgt->m_led_status = 0; + (void) mptsas_flush_led_status(mpt, ptgt); if (rval == DDI_SUCCESS) { mptsas_tgt_free(&mpt->m_active->m_tgttbl, ptgt->m_sas_wwn, ptgt->m_phymask); @@ -11267,6 +11266,163 @@ mptsas_reg_access(mptsas_t *mpt, mptsas_reg_access_t *data, int mode) } static int +led_control(mptsas_t *mpt, intptr_t data, int mode) +{ + int ret = 0; + mptsas_led_control_t lc; + mptsas_target_t *ptgt; + + if (ddi_copyin((void *)data, &lc, sizeof (lc), mode) != 0) { + return (EFAULT); + } + + if ((lc.Command != MPTSAS_LEDCTL_FLAG_SET && + lc.Command != MPTSAS_LEDCTL_FLAG_GET) || + lc.Led < MPTSAS_LEDCTL_LED_MIN || + lc.Led > MPTSAS_LEDCTL_LED_MAX || + (lc.Command == MPTSAS_LEDCTL_FLAG_SET && lc.LedStatus != 0 && + lc.LedStatus != 1)) { + return (EINVAL); + } + + if ((lc.Command == MPTSAS_LEDCTL_FLAG_SET && (mode & FWRITE) == 0) || + (lc.Command == MPTSAS_LEDCTL_FLAG_GET && (mode & FREAD) == 0)) + return (EACCES); + + /* Locate the target we're interrogating... */ + mutex_enter(&mpt->m_mutex); + ptgt = (mptsas_target_t *)mptsas_hash_traverse(&mpt->m_active->m_tgttbl, + MPTSAS_HASH_FIRST); + while (ptgt != NULL) { + if (ptgt->m_enclosure == lc.Enclosure && + ptgt->m_slot_num == lc.Slot) { + break; + } + ptgt = (mptsas_target_t *)mptsas_hash_traverse( + &mpt->m_active->m_tgttbl, MPTSAS_HASH_NEXT); + } + if (ptgt == NULL) { + /* We could not find a target for that enclosure/slot. */ + mutex_exit(&mpt->m_mutex); + return (ENOENT); + } + + if (lc.Command == MPTSAS_LEDCTL_FLAG_SET) { + /* Update our internal LED state. */ + ptgt->m_led_status &= ~(1 << (lc.Led - 1)); + ptgt->m_led_status |= lc.LedStatus << (lc.Led - 1); + + /* Flush it to the controller. */ + ret = mptsas_flush_led_status(mpt, ptgt); + mutex_exit(&mpt->m_mutex); + return (ret); + } + + /* Return our internal LED state. */ + lc.LedStatus = (ptgt->m_led_status >> (lc.Led - 1)) & 1; + mutex_exit(&mpt->m_mutex); + + if (ddi_copyout(&lc, (void *)data, sizeof (lc), mode) != 0) { + return (EFAULT); + } + + return (0); +} + +static int +get_disk_info(mptsas_t *mpt, intptr_t data, int mode) +{ + uint16_t i = 0; + uint16_t count = 0; + int ret = 0; + mptsas_target_t *ptgt; + mptsas_disk_info_t *di; + STRUCT_DECL(mptsas_get_disk_info, gdi); + + if ((mode & FREAD) == 0) + return (EACCES); + + STRUCT_INIT(gdi, get_udatamodel()); + + if (ddi_copyin((void *)data, STRUCT_BUF(gdi), STRUCT_SIZE(gdi), + mode) != 0) { + return (EFAULT); + } + + /* Find out how many targets there are. */ + mutex_enter(&mpt->m_mutex); + ptgt = (mptsas_target_t *)mptsas_hash_traverse(&mpt->m_active->m_tgttbl, + MPTSAS_HASH_FIRST); + while (ptgt != NULL) { + count++; + ptgt = (mptsas_target_t *)mptsas_hash_traverse( + &mpt->m_active->m_tgttbl, MPTSAS_HASH_NEXT); + } + mutex_exit(&mpt->m_mutex); + + /* + * If we haven't been asked to copy out information on each target, + * then just return the count. + */ + STRUCT_FSET(gdi, DiskCount, count); + if (STRUCT_FGETP(gdi, PtrDiskInfoArray) == NULL) + goto copy_out; + + /* + * If we haven't been given a large enough buffer to copy out into, + * let the caller know. + */ + if (STRUCT_FGET(gdi, DiskInfoArraySize) < + count * sizeof (mptsas_disk_info_t)) { + ret = ENOSPC; + goto copy_out; + } + + di = kmem_zalloc(count * sizeof (mptsas_disk_info_t), KM_SLEEP); + + mutex_enter(&mpt->m_mutex); + ptgt = (mptsas_target_t *)mptsas_hash_traverse(&mpt->m_active->m_tgttbl, + MPTSAS_HASH_FIRST); + while (ptgt != NULL) { + if (i >= count) { + /* + * The number of targets changed while we weren't + * looking, so give up. + */ + mutex_exit(&mpt->m_mutex); + kmem_free(di, count * sizeof (mptsas_disk_info_t)); + return (EAGAIN); + } + di[i].Instance = mpt->m_instance; + di[i].Enclosure = ptgt->m_enclosure; + di[i].Slot = ptgt->m_slot_num; + di[i].SasAddress = ptgt->m_sas_wwn; + + ptgt = (mptsas_target_t *)mptsas_hash_traverse( + &mpt->m_active->m_tgttbl, MPTSAS_HASH_NEXT); + i++; + } + mutex_exit(&mpt->m_mutex); + STRUCT_FSET(gdi, DiskCount, i); + + /* Copy out the disk information to the caller. */ + if (ddi_copyout((void *)di, STRUCT_FGETP(gdi, PtrDiskInfoArray), + i * sizeof (mptsas_disk_info_t), mode) != 0) { + ret = EFAULT; + } + + kmem_free(di, count * sizeof (mptsas_disk_info_t)); + +copy_out: + if (ddi_copyout(STRUCT_BUF(gdi), (void *)data, STRUCT_SIZE(gdi), + mode) != 0) { + ret = EFAULT; + } + + return (ret); +} + +static int mptsas_ioctl(dev_t dev, int cmd, intptr_t data, int mode, cred_t *credp, int *rval) { @@ -11282,7 +11438,6 @@ mptsas_ioctl(dev_t dev, int cmd, intptr_t data, int mode, cred_t *credp, dev_info_t *dip = NULL; mptsas_phymask_t phymask = 0; struct devctl_iocdata *dcp = NULL; - uint32_t slotstatus = 0; char *addr = NULL; mptsas_target_t *ptgt = NULL; @@ -11353,40 +11508,26 @@ mptsas_ioctl(dev_t dev, int cmd, intptr_t data, int mode, cred_t *credp, } else if (cmd == DEVCTL_DEVICE_OFFLINE) { ptgt->m_tgt_unconfigured = 1; } - slotstatus = 0; -#ifdef MPTSAS_GET_LED - /* - * The get led status can't get a valid/reasonable - * state, so ignore the get led status, and write the - * required value directly - */ - if (mptsas_get_led_status(mpt, ptgt, &slotstatus) != - DDI_SUCCESS) { - NDBG14(("mptsas_ioctl: get LED for tgt %s " - "failed %x", addr, slotstatus)); - slotstatus = 0; - } - NDBG14(("mptsas_ioctl: LED status %x for %s", - slotstatus, addr)); -#endif if (cmd == DEVCTL_DEVICE_OFFLINE) { - slotstatus |= - MPI2_SEP_REQ_SLOTSTATUS_REQUEST_REMOVE; + ptgt->m_led_status |= + (1 << (MPTSAS_LEDCTL_LED_OK2RM - 1)); } else { - slotstatus &= - ~MPI2_SEP_REQ_SLOTSTATUS_REQUEST_REMOVE; - } - if (mptsas_set_led_status(mpt, ptgt, slotstatus) != - DDI_SUCCESS) { - NDBG14(("mptsas_ioctl: set LED for tgt %s " - "failed %x", addr, slotstatus)); + ptgt->m_led_status &= + ~(1 << (MPTSAS_LEDCTL_LED_OK2RM - 1)); } + (void) mptsas_flush_led_status(mpt, ptgt); mutex_exit(&mpt->m_mutex); ndi_dc_freehdl(dcp); } goto out; } switch (cmd) { + case MPTIOCTL_GET_DISK_INFO: + status = get_disk_info(mpt, data, mode); + break; + case MPTIOCTL_LED_CONTROL: + status = led_control(mpt, data, mode); + break; case MPTIOCTL_UPDATE_FLASH: if (ddi_copyin((void *)data, &flashdata, sizeof (struct mptsas_update_flash), mode)) { @@ -13838,8 +13979,9 @@ mptsas_create_virt_lun(dev_info_t *pdip, struct scsi_inquiry *inq, char *guid, (ptgt->m_tgt_unconfigured == 0)) { rval = mdi_pi_online(*pip, 0); mutex_enter(&mpt->m_mutex); - (void) mptsas_set_led_status(mpt, ptgt, - 0); + ptgt->m_led_status = 0; + (void) mptsas_flush_led_status(mpt, + ptgt); mutex_exit(&mpt->m_mutex); } else { rval = DDI_SUCCESS; @@ -14096,11 +14238,8 @@ mptsas_create_virt_lun(dev_info_t *pdip, struct scsi_inquiry *inq, char *guid, mdi_rtn = mdi_pi_online(*pip, 0); if (mdi_rtn == MDI_SUCCESS) { mutex_enter(&mpt->m_mutex); - if (mptsas_set_led_status(mpt, ptgt, 0) != - DDI_SUCCESS) { - NDBG14(("mptsas: clear LED for slot %x " - "failed", ptgt->m_slot_num)); - } + ptgt->m_led_status = 0; + (void) mptsas_flush_led_status(mpt, ptgt); mutex_exit(&mpt->m_mutex); } if (mdi_rtn == MDI_NOT_SUPPORTED) { @@ -14458,11 +14597,8 @@ phys_create_done: } if (ndi_rtn == NDI_SUCCESS) { mutex_enter(&mpt->m_mutex); - if (mptsas_set_led_status(mpt, ptgt, 0) != - DDI_SUCCESS) { - NDBG14(("mptsas: clear LED for tgt %x " - "failed", ptgt->m_slot_num)); - } + ptgt->m_led_status = 0; + (void) mptsas_flush_led_status(mpt, ptgt); mutex_exit(&mpt->m_mutex); } @@ -15357,27 +15493,31 @@ mptsas_addr_to_ptgt(mptsas_t *mpt, char *addr, mptsas_phymask_t phymask) return (ptgt); } -#ifdef MPTSAS_GET_LED -static int -mptsas_get_led_status(mptsas_t *mpt, mptsas_target_t *ptgt, - uint32_t *slotstatus) -{ - return (mptsas_send_sep(mpt, ptgt, slotstatus, - MPI2_SEP_REQ_ACTION_READ_STATUS)); -} -#endif static int -mptsas_set_led_status(mptsas_t *mpt, mptsas_target_t *ptgt, uint32_t slotstatus) +mptsas_flush_led_status(mptsas_t *mpt, mptsas_target_t *ptgt) { + uint32_t slotstatus = 0; + + /* Build an MPI2 Slot Status based on our view of the world */ + if (ptgt->m_led_status & (1 << (MPTSAS_LEDCTL_LED_IDENT - 1))) + slotstatus |= MPI2_SEP_REQ_SLOTSTATUS_IDENTIFY_REQUEST; + if (ptgt->m_led_status & (1 << (MPTSAS_LEDCTL_LED_FAIL - 1))) + slotstatus |= MPI2_SEP_REQ_SLOTSTATUS_PREDICTED_FAULT; + if (ptgt->m_led_status & (1 << (MPTSAS_LEDCTL_LED_OK2RM - 1))) + slotstatus |= MPI2_SEP_REQ_SLOTSTATUS_REQUEST_REMOVE; + + /* Write it to the controller */ NDBG14(("mptsas_ioctl: set LED status %x for slot %x", slotstatus, ptgt->m_slot_num)); return (mptsas_send_sep(mpt, ptgt, &slotstatus, MPI2_SEP_REQ_ACTION_WRITE_STATUS)); } + /* * send sep request, use enclosure/slot addressing */ -static int mptsas_send_sep(mptsas_t *mpt, mptsas_target_t *ptgt, +static int +mptsas_send_sep(mptsas_t *mpt, mptsas_target_t *ptgt, uint32_t *status, uint8_t act) { Mpi2SepRequest_t req; @@ -15386,15 +15526,27 @@ static int mptsas_send_sep(mptsas_t *mpt, mptsas_target_t *ptgt, ASSERT(mutex_owned(&mpt->m_mutex)); + /* + * We only support SEP control of directly-attached targets, in which + * case the "SEP" we're talking to is a virtual one contained within + * the HBA itself. This is necessary because DA targets typically have + * no other mechanism for LED control. Targets for which a separate + * enclosure service processor exists should be controlled via ses(7d) + * or sgen(7d). Furthermore, since such requests can time out, they + * should be made in user context rather than in response to + * asynchronous fabric changes. + * + * In addition, we do not support this operation for RAID volumes, + * since there is no slot associated with them. + */ + if (!(ptgt->m_deviceinfo & DEVINFO_DIRECT_ATTACHED) || + ptgt->m_phymask == 0) { + return (ENOTTY); + } + bzero(&req, sizeof (req)); bzero(&rep, sizeof (rep)); - /* Do nothing for RAID volumes */ - if (ptgt->m_phymask == 0) { - NDBG14(("mptsas_send_sep: Skip RAID volumes")); - return (DDI_FAILURE); - } - req.Function = MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR; req.Action = act; req.Flags = MPI2_SEP_REQ_FLAGS_ENCLOSURE_SLOT_ADDRESS; @@ -15408,26 +15560,41 @@ static int mptsas_send_sep(mptsas_t *mpt, mptsas_target_t *ptgt, if (ret != 0) { mptsas_log(mpt, CE_NOTE, "mptsas_send_sep: passthru SEP " "Processor Request message error %d", ret); - return (DDI_FAILURE); + return (ret); } /* do passthrough success, check the ioc status */ if (LE_16(rep.IOCStatus) != MPI2_IOCSTATUS_SUCCESS) { - if ((LE_16(rep.IOCStatus) & MPI2_IOCSTATUS_MASK) == - MPI2_IOCSTATUS_INVALID_FIELD) { - mptsas_log(mpt, CE_NOTE, "send sep act %x: Not " - "supported action, loginfo %x", act, - LE_32(rep.IOCLogInfo)); - return (DDI_FAILURE); - } mptsas_log(mpt, CE_NOTE, "send_sep act %x: ioc " - "status:%x", act, LE_16(rep.IOCStatus)); - return (DDI_FAILURE); + "status:%x loginfo %x", act, LE_16(rep.IOCStatus), + LE_32(rep.IOCLogInfo)); + switch (LE_16(rep.IOCStatus) & MPI2_IOCSTATUS_MASK) { + case MPI2_IOCSTATUS_INVALID_FUNCTION: + 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_CONFIG_INVALID_ACTION: + case MPI2_IOCSTATUS_CONFIG_INVALID_TYPE: + case MPI2_IOCSTATUS_CONFIG_INVALID_PAGE: + case MPI2_IOCSTATUS_CONFIG_INVALID_DATA: + case MPI2_IOCSTATUS_CONFIG_NO_DEFAULTS: + return (EINVAL); + case MPI2_IOCSTATUS_BUSY: + return (EBUSY); + case MPI2_IOCSTATUS_INSUFFICIENT_RESOURCES: + return (EAGAIN); + case MPI2_IOCSTATUS_INVALID_SGL: + case MPI2_IOCSTATUS_INTERNAL_ERROR: + case MPI2_IOCSTATUS_CONFIG_CANT_COMMIT: + default: + return (EIO); + } } if (act != MPI2_SEP_REQ_ACTION_WRITE_STATUS) { *status = LE_32(rep.SlotStatus); } - return (DDI_SUCCESS); + return (0); } int diff --git a/usr/src/uts/common/sys/scsi/adapters/mpt_sas/mptsas_ioctl.h b/usr/src/uts/common/sys/scsi/adapters/mpt_sas/mptsas_ioctl.h index 157c9fb064..5586b490f6 100644 --- a/usr/src/uts/common/sys/scsi/adapters/mpt_sas/mptsas_ioctl.h +++ b/usr/src/uts/common/sys/scsi/adapters/mpt_sas/mptsas_ioctl.h @@ -23,6 +23,9 @@ * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ +/* + * Copyright (c) 2013, Joyent, Inc. All rights reserved. + */ /* * Copyright (c) 2000 to 2010, LSI Corporation. @@ -71,6 +74,8 @@ extern "C" { #define MPTIOCTL_GET_PCI_INFO (MPTIOCTL | 8) #define MPTIOCTL_DIAG_ACTION (MPTIOCTL | 9) #define MPTIOCTL_REG_ACCESS (MPTIOCTL | 10) +#define MPTIOCTL_GET_DISK_INFO (MPTIOCTL | 11) +#define MPTIOCTL_LED_CONTROL (MPTIOCTL | 12) /* * The following are our ioctl() return status values. If everything went @@ -296,6 +301,58 @@ typedef struct mptsas_reg_access uint32_t RegData; } mptsas_reg_access_t; +/* + * Disk Toplogy Information + */ +typedef struct mptsas_disk_info +{ + uint64_t SasAddress; + uint16_t Instance; + uint16_t Enclosure; + uint16_t Slot; +} mptsas_disk_info_t; + +typedef struct mptsas_get_disk_info +{ + uint16_t DiskCount; + mptsas_disk_info_t *PtrDiskInfoArray; + uint64_t DiskInfoArraySize; +} mptsas_get_disk_info_t; + +#ifdef _KERNEL + +typedef struct mptsas_get_disk_info32 +{ + uint16_t DiskCount; + caddr32_t PtrDiskInfoArray; + uint64_t DiskInfoArraySize; +} mptsas_get_disk_info32_t; + +#endif /* _KERNEL */ + +/* + * LED Control + */ + +typedef struct mptsas_led_control +{ + uint8_t Command; + uint16_t Enclosure; + uint16_t Slot; + uint8_t Led; + uint8_t LedStatus; +} mptsas_led_control_t; + +#define MPTSAS_LEDCTL_FLAG_SET 1 +#define MPTSAS_LEDCTL_FLAG_GET 2 + +#define MPTSAS_LEDCTL_LED_IDENT 1 +#define MPTSAS_LEDCTL_LED_FAIL 2 +#define MPTSAS_LEDCTL_LED_OK2RM 3 + +#define MPTSAS_LEDCTL_LED_MIN MPTSAS_LEDCTL_LED_IDENT +#define MPTSAS_LEDCTL_LED_MAX MPTSAS_LEDCTL_LED_OK2RM + #ifdef __cplusplus } #endif diff --git a/usr/src/uts/common/sys/scsi/adapters/mpt_sas/mptsas_var.h b/usr/src/uts/common/sys/scsi/adapters/mpt_sas/mptsas_var.h index ae6a427c72..19975b008c 100644 --- a/usr/src/uts/common/sys/scsi/adapters/mpt_sas/mptsas_var.h +++ b/usr/src/uts/common/sys/scsi/adapters/mpt_sas/mptsas_var.h @@ -22,6 +22,7 @@ /* * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2012 Nexenta Systems, Inc. All rights reserved. + * Copyright (c) 2013, Joyent, Inc. All rights reserved. */ /* @@ -208,6 +209,7 @@ typedef struct mptsas_target { uint16_t m_enclosure; uint16_t m_slot_num; uint32_t m_tgt_unconfigured; + uint8_t m_led_status; } mptsas_target_t; -- 2.11.4.GIT