diff options
author | Gavin Maltby <Gavin.Maltby@Sun.COM> | 2010-04-22 17:44:11 +1000 |
---|---|---|
committer | Gavin Maltby <Gavin.Maltby@Sun.COM> | 2010-04-22 17:44:11 +1000 |
commit | 392e836b07e8da771953e4d64233b2abe4393efe (patch) | |
tree | 86ab03dfb47574bf1e433d66bb90ee853efd78f3 /usr/src | |
parent | 8d7fafffed373567f52062b634e61fd50858b8d9 (diff) | |
download | illumos-gate-392e836b07e8da771953e4d64233b2abe4393efe.tar.gz |
6935519 raise telemetry for failed scsa-initiated enumeration commands
Diffstat (limited to 'usr/src')
-rw-r--r-- | usr/src/cmd/fm/modules/common/eversholt/platform.c | 2 | ||||
-rw-r--r-- | usr/src/lib/fm/topo/libtopo/common/dev.c | 190 | ||||
-rw-r--r-- | usr/src/lib/fm/topo/libtopo/common/topo_hc.h | 2 | ||||
-rw-r--r-- | usr/src/uts/common/io/pciex/pcie_fault.c | 4 | ||||
-rw-r--r-- | usr/src/uts/common/io/scsi/conf/scsi_confsubr.c | 797 | ||||
-rw-r--r-- | usr/src/uts/common/io/scsi/impl/scsi_fm.c | 39 | ||||
-rw-r--r-- | usr/src/uts/common/io/scsi/impl/scsi_hba.c | 24 | ||||
-rw-r--r-- | usr/src/uts/common/io/scsi/targets/sd.c | 49 | ||||
-rw-r--r-- | usr/src/uts/common/os/ddifm.c | 43 | ||||
-rw-r--r-- | usr/src/uts/common/os/devid_cache.c | 185 | ||||
-rw-r--r-- | usr/src/uts/common/os/fm.c | 52 | ||||
-rw-r--r-- | usr/src/uts/common/os/pcifm.c | 5 | ||||
-rw-r--r-- | usr/src/uts/common/sys/fm/protocol.h | 6 | ||||
-rw-r--r-- | usr/src/uts/common/sys/scsi/scsi_fm.h | 6 | ||||
-rw-r--r-- | usr/src/uts/sun4u/io/pci/pci_ecc.c | 48 | ||||
-rw-r--r-- | usr/src/uts/sun4u/io/pci/pcisch.c | 6 | ||||
-rw-r--r-- | usr/src/uts/sun4u/opl/io/mc-opl.c | 6 |
17 files changed, 1179 insertions, 285 deletions
diff --git a/usr/src/cmd/fm/modules/common/eversholt/platform.c b/usr/src/cmd/fm/modules/common/eversholt/platform.c index 2c2fca684a..2c9b40e632 100644 --- a/usr/src/cmd/fm/modules/common/eversholt/platform.c +++ b/usr/src/cmd/fm/modules/common/eversholt/platform.c @@ -261,7 +261,7 @@ platform_getpath(nvlist_t *nvl) if (nvlist_lookup_string(dfmri, FM_FMRI_DEV_ID, &devid) == 0) type = DT_DEVID; - else if (nvlist_lookup_string(nvl, + else if (nvlist_lookup_string(dfmri, TOPO_STORAGE_TARGET_PORT_L0ID, &tp) == 0) type = DT_TP; else if (nvlist_lookup_string(dfmri, diff --git a/usr/src/lib/fm/topo/libtopo/common/dev.c b/usr/src/lib/fm/topo/libtopo/common/dev.c index b87b287997..f71e124f94 100644 --- a/usr/src/lib/fm/topo/libtopo/common/dev.c +++ b/usr/src/lib/fm/topo/libtopo/common/dev.c @@ -20,8 +20,7 @@ */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. */ #include <limits.h> @@ -128,26 +127,23 @@ dev_release(topo_mod_t *mod, tnode_t *node) static ssize_t fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen) { - nvlist_t *anvl = NULL; - nvpair_t *apair; - uint8_t version; - ssize_t size = 0; - char *devid = NULL; + char *devid = NULL, *tpl0id = NULL; char *devpath = NULL; - char *aname, *aval; + ssize_t size = 0; + uint8_t version; int err; if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 || version > FM_DEV_SCHEME_VERSION) return (-1); - /* Get authority, if present */ - err = nvlist_lookup_nvlist(nvl, FM_FMRI_AUTHORITY, &anvl); + /* Get devid, if present */ + err = nvlist_lookup_string(nvl, FM_FMRI_DEV_ID, &devid); if (err != 0 && err != ENOENT) return (-1); - /* Get devid, if present */ - err = nvlist_lookup_string(nvl, FM_FMRI_DEV_ID, &devid); + /* Get target-port-l0id, if present */ + err = nvlist_lookup_string(nvl, FM_FMRI_DEV_TGTPTLUN0, &tpl0id); if (err != 0 && err != ENOENT) return (-1); @@ -156,31 +152,40 @@ fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen) if (err != 0 || devpath == NULL) return (-1); - - /* dev:// */ + /* + * dev:/// + * + * The dev scheme does not render fmri authority information + * in the string form of an fmri. It is meaningless to + * transmit a dev scheme fmri outside of the immediate fault + * manager. + */ topo_fmristr_build(&size, - buf, buflen, FM_FMRI_SCHEME_DEV, NULL, "://"); - - /* authority, if any */ - if (anvl != NULL) { - for (apair = nvlist_next_nvpair(anvl, NULL); - apair != NULL; apair = nvlist_next_nvpair(anvl, apair)) { - if (nvpair_type(apair) != DATA_TYPE_STRING || - nvpair_value_string(apair, &aval) != 0) - continue; - aname = nvpair_name(apair); - topo_fmristr_build(&size, buf, buflen, ":", NULL, NULL); - topo_fmristr_build(&size, buf, buflen, "=", - aname, aval); - } - } + buf, buflen, FM_FMRI_SCHEME_DEV, NULL, ":///"); /* device-id part, topo_fmristr_build does nothing if devid is NULL */ topo_fmristr_build(&size, - buf, buflen, devid, "/:" FM_FMRI_DEV_ID "=", NULL); + buf, buflen, devid, ":" FM_FMRI_DEV_ID "=", NULL); + + /* target-port-l0id part */ + topo_fmristr_build(&size, + buf, buflen, tpl0id, ":" FM_FMRI_DEV_TGTPTLUN0 "=", NULL); - /* device-path part */ - topo_fmristr_build(&size, buf, buflen, devpath, "/", NULL); + /* + * device-path part; the devpath should always start with a / + * so you'd think we don't need to add a further / prefix here; + * however past implementation has always added the / if + * there is a devid component so we continue to do that + * so strings match exactly as before. So we can have: + * + * dev:////pci@0,0/... + * dev:///<devid-and-tpl0>//pci@0,0/... + * + * where <devid-and-tpl0> = + * [:devid=<devid>][:target-port-l0id=<tpl0>] + */ + topo_fmristr_build(&size, buf, buflen, devpath, + devid || tpl0id ? "/" : NULL, NULL); return (size); } @@ -223,10 +228,11 @@ static int dev_fmri_str2nvl(topo_mod_t *mod, tnode_t *node, topo_version_t version, nvlist_t *in, nvlist_t **out) { + char *cur, *devid = NULL, *tpl0id = NULL; + char *str, *strcp; nvlist_t *fmri; char *devpath; - char *devid = NULL; - char *str; + size_t len; int err; if (version > TOPO_METH_STR2NVL_VERSION) @@ -235,42 +241,124 @@ dev_fmri_str2nvl(topo_mod_t *mod, tnode_t *node, topo_version_t version, if (nvlist_lookup_string(in, "fmri-string", &str) != 0) return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); - /* We're expecting a string version of a dev scheme FMRI */ - if (strncmp(str, "dev:///", 7) != 0) + len = strlen(str); + + /* + * We're expecting a string version of a dev scheme FMRI, and + * no fmri authority information. + * + * The shortest legal string would be "dev:////" (len 8) for a string + * with no FMRI auth info, no devid or target-port-l0id and + * an empty devpath string. + */ + if (len < 8 || strncmp(str, "dev:///", 7) != 0) return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); - devpath = str + 7; - if (strncmp(devpath, ":" FM_FMRI_DEV_ID "=", 7) == 0) { - char *n; - int len; + strcp = alloca(len + 1); + (void) memcpy(strcp, str, len); + strcp[len] = '\0'; + cur = strcp + 7; /* already parsed "dev:///" */ - n = strchr(devpath + 7, '/'); - if (n == NULL) + /* + * If the first character after the "/" that terminates the (empty) + * fmri authority is a colon then we have devid and/or target-port-l0id + * info. They could be in either order. + * + * If not a colon then it must be the / that begins the devpath. + */ + if (*cur == ':') { + char *eos, *part[2]; + int i; + /* + * Look ahead to the "/" that starts the devpath. If not + * found or if straight after the : then we're busted. + */ + eos = devpath = strchr(cur, '/'); + if (devpath == NULL || devpath == cur + 1) + return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); + + part[0] = ++cur; + + /* + * Replace the initial "/" of the devpath with a NUL + * to terminate the string before it. We'll undo this + * before rendering devpath. + */ + *eos = '\0'; + + /* + * We should now have a NUL-terminated string matching + * foo=<pat1>[:bar=<pat2>] (we stepped over the initial :) + * Look for a second colon; if found there must be space + * after it for the additional component, but no more colons. + */ + if ((part[1] = strchr(cur, ':')) != NULL) { + if (part[1] + 1 == eos || + strchr(part[1] + 1, ':') != NULL) + return (topo_mod_seterrno(mod, + EMOD_FMRI_MALFORM)); + *part[1] = '\0'; /* terminate part[0] */ + part[1]++; + } + + for (i = 0; i < 2; i++) { + char *eq; + + if (!part[i]) + continue; + + if ((eq = strchr(part[i], '=')) == NULL || + *(eq + 1) == '\0') + return (topo_mod_seterrno(mod, + EMOD_FMRI_MALFORM)); + + *eq = '\0'; + if (strcmp(part[i], FM_FMRI_DEV_ID) == 0) + devid = eq + 1; + else if (strcmp(part[i], FM_FMRI_DEV_TGTPTLUN0) == 0) + tpl0id = eq + 1; + else + return (topo_mod_seterrno(mod, + EMOD_FMRI_MALFORM)); + } + + if (devid == NULL && tpl0id == NULL) return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); - len = n - (devpath + 7); - devid = alloca(len + 1); - (void) memcpy(devid, devpath + 7, len); - devid[len] = 0; - devpath = n + 1; - } - /* the device-path should start with a slash */ - if (*devpath != '/') + cur = devpath; /* initial slash is NULled */ + } else if (*cur != '/') { + /* the device-path should start with a slash */ return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); + } else { + devpath = cur; + } if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0) return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); err = nvlist_add_uint8(fmri, FM_VERSION, FM_DEV_SCHEME_VERSION); err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_DEV); - err |= nvlist_add_string(fmri, FM_FMRI_DEV_PATH, devpath); + if (devid != NULL) err |= nvlist_add_string(fmri, FM_FMRI_DEV_ID, devid); + if (tpl0id != NULL) + err |= nvlist_add_string(fmri, FM_FMRI_DEV_TGTPTLUN0, tpl0id); + + if (devid != NULL || tpl0id != NULL) + *devpath = '/'; /* we NULed this earlier; put it back */ + + /* step over repeated initial / in the devpath */ + while (*(devpath + 1) == '/') + devpath++; + + err |= nvlist_add_string(fmri, FM_FMRI_DEV_PATH, devpath); + if (err != 0) { nvlist_free(fmri); return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); } + *out = fmri; return (0); 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 67858ffd3e..5a6d095112 100644 --- a/usr/src/lib/fm/topo/libtopo/common/topo_hc.h +++ b/usr/src/lib/fm/topo/libtopo/common/topo_hc.h @@ -128,8 +128,6 @@ extern "C" { #define TOPO_STORAGE_MANUFACTURER "manufacturer" #define TOPO_STORAGE_MODEL "model" #define TOPO_STORAGE_FIRMWARE_REV "firmware-revision" -#define TOPO_STORAGE_TARGET_PORT_L0ID "target-port-l0id" -#define TOPO_STORAGE_TARGET_PORT_L0IDS "target-port-l0ids" #define TOPO_STORAGE_SAS_PHY_MASK "receptacle-pm" #define TOPO_STORAGE_SAS_CONNECTOR_TYPE "sas-connector-type" diff --git a/usr/src/uts/common/io/pciex/pcie_fault.c b/usr/src/uts/common/io/pciex/pcie_fault.c index c17b4e97d7..a7eebd00c4 100644 --- a/usr/src/uts/common/io/pciex/pcie_fault.c +++ b/usr/src/uts/common/io/pciex/pcie_fault.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. */ #include <sys/sysmacros.h> @@ -2649,7 +2649,7 @@ pf_ereport_setup(dev_info_t *dip, uint64_t ena, nvlist_t **ereport, } fm_fmri_dev_set(*detector, FM_DEV_SCHEME_VERSION, NULL, - device_path, NULL); + device_path, NULL, NULL); if (ena == 0) ena = fm_ena_generate(0, FM_ENA_FMT1); diff --git a/usr/src/uts/common/io/scsi/conf/scsi_confsubr.c b/usr/src/uts/common/io/scsi/conf/scsi_confsubr.c index 3d5c9d4b0a..1b2dc7ab52 100644 --- a/usr/src/uts/common/io/scsi/conf/scsi_confsubr.c +++ b/usr/src/uts/common/io/scsi/conf/scsi_confsubr.c @@ -19,8 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved. */ /* @@ -34,6 +33,7 @@ #include <sys/scsi/scsi.h> #include <sys/modctl.h> #include <sys/bitmap.h> +#include <sys/fm/protocol.h> /* * macro for filling in lun value for scsi-1 support @@ -57,6 +57,31 @@ static struct modlinkage modlinkage = { MODREV_1, (void *)&modlmisc, NULL }; +/* + * Contexts from which we call scsi_test + */ +enum scsi_test_ctxt { + /* + * Those in scsi_hba_probe_pi() + */ + STC_PROBE_FIRST_INQ, + STC_PROBE_FIRST_INQ_RETRY, + STC_PROBE_PARTIAL_SUCCESS, + STC_PROBE_RQSENSE1, + STC_PROBE_CHK_CLEARED, + STC_PROBE_RQSENSE2, + STC_PROBE_INQ_FINAL, + /* + * Those in check_vpd_page_support8083() + */ + STC_VPD_CHECK, + /* + * Those in scsi_device_identity() + */ + STC_IDENTITY_PG80, + STC_IDENTITY_PG83, +}; + static void create_inquiry_props(struct scsi_device *); static int scsi_check_ss2_LUN_limit(struct scsi_device *); @@ -67,7 +92,8 @@ static int check_vpd_page_support8083(struct scsi_device *sd, int (*callback)(), int *, int *); static int send_scsi_INQUIRY(struct scsi_device *sd, int (*callback)(), uchar_t *bufaddr, size_t buflen, - uchar_t evpd, uchar_t page_code, size_t *lenp); + uchar_t evpd, uchar_t page_code, size_t *lenp, + enum scsi_test_ctxt); /* * this int-array HBA-node property keeps track of strictly SCSI-2 @@ -138,6 +164,50 @@ int scsi_probe_debug = 0; int scsi_test_busy_timeout = SCSI_POLL_TIMEOUT; /* in seconds */ int scsi_test_busy_delay = 10000; /* 10msec in usec */ + +/* + * Returns from scsi_test. + * + * SCSI_TEST_CMPLT_GOOD => TRAN_ACCEPT, CMD_CMPLT, STATUS_GOOD + * + * SCSI_TEST_CMPLT_BUSY => TRAN_ACCEPT, CMD_CMPLT, STATUS_BUSY + * + * SCSI_TEST_CMPLT_CHECK => TRAN_ACCEPT, CMD_CMPLT, STATUS_CHECK + * + * SCSI_TEST_CMPLT_OTHER => TRAN_ACCEPT, CMD_CMPLT, !STATUS_{GOOD,BUSY,CHECK} + * + * SCSI_TEST_CMD_INCOMPLETE => TRAN_ACCEPT, CMD_INCOMPLETE + * + * SCSI_TEST_NOTCMPLT => TRAN_ACCEPT, pkt_reason != CMD_{CMPLT,INCOMPLETE} + * + * SCSI_TEST_TRAN_BUSY => (Repeated) TRAN_BUSY from attempt scsi_transport + * + * SCSI_TEST_TRAN_REJECT => TRAN_BADPKT or TRAN_FATAL_ERROR + * + */ +#define SCSI_TEST_CMPLT_GOOD 0x01U +#define SCSI_TEST_CMPLT_BUSY 0x02U +#define SCSI_TEST_CMPLT_CHECK 0x04U +#define SCSI_TEST_CMPLT_OTHER 0x08U + +#define SCSI_TEST_CMPLTMASK \ + (SCSI_TEST_CMPLT_GOOD | SCSI_TEST_CMPLT_BUSY | \ + SCSI_TEST_CMPLT_CHECK | SCSI_TEST_CMPLT_OTHER) + +#define SCSI_TEST_PARTCMPLTMASK \ + (SCSI_TEST_CMPLTMASK & ~SCSI_TEST_CMPLT_GOOD) + +#define SCSI_TEST_CMD_INCOMPLETE 0x10U +#define SCSI_TEST_NOTCMPLT 0x20U +#define SCSI_TEST_TRAN_BUSY 0x40U +#define SCSI_TEST_TRAN_REJECT 0x80U + +#define SCSI_TEST_FAILMASK \ + (SCSI_TEST_CMD_INCOMPLETE | SCSI_TEST_NOTCMPLT | \ + SCSI_TEST_TRAN_BUSY | SCSI_TEST_TRAN_REJECT) + +#define SCSI_TEST_FAILURE(x) (((x) & SCSI_TEST_FAILMASK) != 0) + /* * architecture dependent allocation restrictions. For x86, we'll set * dma_attr_addr_hi to scsi_max_phys_addr and dma_attr_sgllen to @@ -386,12 +456,454 @@ scsi_unprobe(struct scsi_device *sd) } /* + * We log all scsi_test failures (as long as we are SE_HP etc). The + * following table controls the "driver-assessment" payload item + * in the ereports we raise. If a scsi_test return features in the + * retry mask then the calling context will retry; if it features in + * the fatal mask then the caller will not retry (although higher-level + * software might); if in neither (which shouldn't happen - you either + * retry or give up) default to 'retry'. + */ +static const struct scsi_test_profile { + enum scsi_test_ctxt stp_ctxt; /* Calling context */ + uint32_t stp_retrymask; /* Returns caller will retry for */ + uint32_t stp_fatalmask; /* Returns caller considers fatal */ +} scsi_test_profile[] = { + /* + * This caller will retry on SCSI_TEST_FAILMASK as long as it was + * not SCSI_TEST_CMD_INCOMPLETE which is terminal. A return from + * SCSI_TEST_PARTCMPLTMASK (command complete but status other than + * STATUS_GOOD) is not terminal and we'll move on to the context + * of STC_PROBE_PARTIAL_SUCCESS so that's a retry, too. + */ + { + STC_PROBE_FIRST_INQ, + SCSI_TEST_FAILMASK & ~SCSI_TEST_CMD_INCOMPLETE | + SCSI_TEST_PARTCMPLTMASK, + SCSI_TEST_CMD_INCOMPLETE + }, + + /* + * If the first inquiry fails outright we always retry just once + * (except for SCSI_TEST_CMD_INCOMPLETE as above). A return in + * SCSI_TEST_FAILMASK is terminal; for SCSI_TEST_PARTCMPLTMASK + * we will retry at STC_PROBE_PARTIAL_SUCCESS. + */ + { + STC_PROBE_FIRST_INQ_RETRY, + SCSI_TEST_PARTCMPLTMASK, + SCSI_TEST_FAILMASK + }, + + /* + * If we've met with partial success we retry at caller context + * STC_PROBE_PARTIAL_SUCCESS. Any SCSI_TEST_FAILMASK return + * here is terminal, as too is SCSI_TEST_CMPLT_BUSY. A return in + * SCSI_TEST_PARTCMPLTMASK and we will continue with further + * inquiry attempts. + */ + { + STC_PROBE_PARTIAL_SUCCESS, + SCSI_TEST_PARTCMPLTMASK & ~SCSI_TEST_CMPLT_BUSY, + SCSI_TEST_FAILMASK | SCSI_TEST_CMPLT_BUSY + }, + + /* + * If we get past the above target busy case then we will + * perform a sense request if scsi_test indicates STATUS_CHECK + * and ARQ was not done. We are not interested in logging telemetry + * for transports that do not perform ARQ automatically. + */ + { + STC_PROBE_RQSENSE1, + 0, + 0 + }, + + /* + * If "something" responded to the probe but then the next inquiry + * sees a change of heart then we fail the probe on any of + * SCSI_TEST_FAILMASK or SCSI_TEST_CMPLT_BUSY. For other values + * in SCSI_TEST_PARTCMPLTMASK we soldier on. + */ + { + STC_PROBE_CHK_CLEARED, + SCSI_TEST_PARTCMPLTMASK & ~SCSI_TEST_CMPLT_BUSY, + SCSI_TEST_FAILMASK | SCSI_TEST_CMPLT_BUSY + }, + + /* + * If after all that there we still have STATUS_CHECK from the + * inquiry status then we resend the sense request but the + * result is ignored (just clearing the condition). Do not + * log. + */ + { + STC_PROBE_RQSENSE2, + 0, + 0 + }, + + /* + * After the above sense request we once again send an inquiry. + * If it fails outright or STATUS_CHECK persists we give up. + * Any partial result is considered success. + */ + { + STC_PROBE_INQ_FINAL, + 0, + SCSI_TEST_FAILMASK | SCSI_TEST_CMPLT_CHECK + }, + + /* + * check_vpd_page_support8083 called from scsi_device_identity + * performs an inquiry with EVPD set (and page necessarily 0) + * to see what pages are supported. + * + * Some devices do not support this command and therefore + * check_vpd_page_support8083 only returns an error of kmem_zalloc + * fails. If the send_scsi_INQUIRY does not meet with complete + * success (SCSI_TEST_CMPLT_GOOD) it returns -1, othewise 0. + * So any scsi_test failure here will cause us to assume no page + * 80/83 support, and we will proceed without devid support. + * So -1 returns from send_scsi_INQUIRY are not terminal. + */ + { + STC_VPD_CHECK, + 0, + 0 + }, + + /* + * If the above inquiry claims pg80 support then scsi_device_identity + * will perform a send_scsi_INQUIRY to retrieve that page. + * Anything other than SCSI_TEST_CMPLT_GOOD is a failure and will + * cause scsi_device_identity to return non-zero at which point the + * caller goes to SCSIPROBE_FAILURE. + */ + { + STC_IDENTITY_PG80, + 0, + SCSI_TEST_FAILMASK | SCSI_TEST_CMPLTMASK + }, + + /* + * Similarly for pg83 + */ + { + STC_IDENTITY_PG83, + 0, + SCSI_TEST_FAILMASK | SCSI_TEST_CMPLTMASK + } +}; + +int scsi_test_ereport_disable = 0; + +extern int e_devid_cache_path_to_devid(char *, char *, char *, ddi_devid_t *); + +static void +scsi_test_ereport_post(struct scsi_pkt *pkt, enum scsi_test_ctxt ctxt, + uint32_t stresult) +{ + char *nodename = NULL, *devidstr_buf = NULL, *devidstr = NULL; + const struct scsi_test_profile *tp = &scsi_test_profile[ctxt]; + char ua[SCSI_MAXNAMELEN], nodenamebuf[SCSI_MAXNAMELEN]; + union scsi_cdb *cdbp = (union scsi_cdb *)pkt->pkt_cdbp; + struct scsi_address *ap = &pkt->pkt_address; + char *tgt_port, *tpl0 = NULL; + ddi_devid_t devid = NULL; + dev_info_t *probe, *hba; + struct scsi_device *sd; + scsi_lun64_t lun64; + const char *d_ass; + const char *class; + char *pathbuf; + nvlist_t *pl; + uint64_t wwn; + int err = 0; + int dad = 0; + size_t len; + int lun; + + if (scsi_test_ereport_disable) + return; + + ASSERT(tp->stp_ctxt == ctxt); + + if ((sd = scsi_address_device(ap)) == NULL) + return; /* Not SCSI_HBA_ADDR_COMPLEX */ + + probe = sd->sd_dev; + hba = ddi_get_parent(probe); + + /* + * We only raise telemetry for SE_HP style enumeration + */ + if (!ndi_dev_is_hotplug_node(hba)) + return; + + /* + * scsi_fm_ereport_post will use the hba for the fm-enabled devinfo + */ + if (!DDI_FM_EREPORT_CAP(ddi_fm_capable(hba))) + return; + + /* + * Retrieve the unit address we were probing and the target + * port component thereof. + */ + if (!scsi_ua_get(sd, ua, sizeof (ua)) || + scsi_device_prop_lookup_string(sd, SCSI_DEVICE_PROP_PATH, + SCSI_ADDR_PROP_TARGET_PORT, &tgt_port) != DDI_PROP_SUCCESS) + return; + + /* + * Determine whether unit address is location based or identity (wwn) + * based. If we can't convert the target port address to a wwn then + * we're location based. + */ + if (scsi_wwnstr_to_wwn(tgt_port, &wwn) == DDI_FAILURE) + return; + + /* + * Get lun and lun64 + */ + lun = scsi_device_prop_get_int(sd, SCSI_DEVICE_PROP_PATH, + SCSI_ADDR_PROP_LUN, 0); + lun64 = scsi_device_prop_get_int64(sd, SCSI_DEVICE_PROP_PATH, + SCSI_ADDR_PROP_LUN64, lun); + + /* + * We are guaranteed not to be in interrupt or any other + * problematic context. So instead of repeated varargs + * style calls to scsi_fm_ereport_post for each flavour of + * ereport we have the luxury of being able to allocate + * and build an nvlist here. + * + * The ereports we raise here are all under the category + * ereport.io.scsi.cmd.disk category, namely + * + * ereport.io.scsi.cmd.disk. + * {dev.rqs.derr,dev.serr,tran}. + * + * For all ereports we also add the scsi_test specific payload. + * If we have it then we always include the devid in the payload + * (but only in the detector for device-as-detector ereports). + * + * Inherited From Member Name + * -------------------- ------------------- + * .cmd driver-assessment + * .cmd op-code + * .cmd cdb + * .cmd pkt-reason + * .cmd pkt-state + * .cmd pkt-stats + * .cmd.disk stat-code + * - scsi-test-return + * - scsi-test-context + */ + + if (nvlist_alloc(&pl, NV_UNIQUE_NAME, 0) != 0) + return; + + err |= nvlist_add_uint8(pl, "op-code", cdbp->scc_cmd); + err |= nvlist_add_uint8_array(pl, "cdb", pkt->pkt_cdbp, + pkt->pkt_cdblen); + err |= nvlist_add_uint8(pl, "pkt-reason", pkt->pkt_reason); + err |= nvlist_add_uint32(pl, "pkt-state", pkt->pkt_state); + err |= nvlist_add_uint32(pl, "pkt-stats", pkt->pkt_statistics); + err |= nvlist_add_uint32(pl, "stat-code", *pkt->pkt_scbp); + err |= nvlist_add_uint32(pl, "scsi-test-return", stresult); + err |= nvlist_add_int32(pl, "scsi-test-context", ctxt); + + switch (stresult) { + case SCSI_TEST_CMPLT_BUSY: + dad = 1; + class = "cmd.disk.dev.serr"; + break; + + case SCSI_TEST_CMPLT_CHECK: + dad = 1; + + if ((pkt->pkt_state & STATE_ARQ_DONE)) { + struct scsi_arq_status *arqstat; + uint8_t key, asc, ascq; + uint8_t *sensep; + + class = "cmd.disk.dev.rqs.derr"; + arqstat = (struct scsi_arq_status *)pkt->pkt_scbp; + sensep = (uint8_t *)&arqstat->sts_sensedata; + key = scsi_sense_key(sensep); + asc = scsi_sense_asc(sensep); + ascq = scsi_sense_ascq(sensep); + + /* + * Add to payload. + */ + err |= nvlist_add_uint8(pl, "key", key); + err |= nvlist_add_uint8(pl, "asc", asc); + err |= nvlist_add_uint8(pl, "ascq", ascq); + err |= nvlist_add_uint8_array(pl, "sense-data", + sensep, sizeof (arqstat->sts_sensedata)); + } else { + class = "cmd.disk.dev.serr"; + } + + break; + + case SCSI_TEST_CMPLT_OTHER: + dad = 1; + class = "cmd.disk.dev.serr"; + break; + + case SCSI_TEST_CMD_INCOMPLETE: + case SCSI_TEST_NOTCMPLT: + case SCSI_TEST_TRAN_BUSY: + case SCSI_TEST_TRAN_REJECT: + class = "cmd.disk.tran"; + break; + } + + /* + * Determine driver-assessment and add to payload. + */ + if (dad) { + /* + * While higher level software can retry the enumeration + * the belief is that any device-as-detector style error + * will be persistent and will survive retries. So we + * can make a local determination of driver assessment. + * Some day it may be more elegant to raise an ereport from + * scsi_tgtmap_scsi_deactivate to confirm retries failed, + * and correlate that ereport during diagnosis. + */ + if (stresult & tp->stp_fatalmask) + d_ass = (const char *)"fatal"; + else if (stresult & tp->stp_retrymask) + d_ass = (const char *)"retry"; + else + d_ass = (const char *)"retry"; + } else { + /* We do not diagnose transport errors (yet) */ + d_ass = (const char *)"retry"; + } + + err |= nvlist_add_string(pl, "driver-assessment", d_ass); + + /* + * If we're hoping for a device-as-detector style ereport then + * we're going to need a devid for the detector FMRI. We + * don't have the devid because the target won't talk to us. + * But we do know which hba iport we were probing out of, and + * we know the unit address that was being probed (but not + * what type of device is or should be there). So we + * search the devid cache for any cached devid matching + * path <iport-path>/<nodename>@<unit-address> with nodename + * wildcarded. If a match is made we are returned not only the + * devid but also the nodename for the path that cached that + * entry. + * + * We also attempt to dig up a devid even for transport errors; + * we'll include that in the payload but not in the detector FMRI. + */ + + pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP); + (void) ddi_pathname(hba, pathbuf); + + if (e_devid_cache_path_to_devid(pathbuf, ua, nodenamebuf, + &devid) == DDI_SUCCESS) { + nodename = nodenamebuf; + devidstr = devidstr_buf = ddi_devid_str_encode(devid, NULL); + kmem_free(devid, ddi_devid_sizeof(devid)); + err |= nvlist_add_string(pl, "devid", devidstr); + } + + /* + * If this is lun 0 we will include the target-port-l0id + * in the dev scheme detector for device-as-detector. + */ + if (dad && (lun == 0 || lun64 == 0)) + tpl0 = tgt_port; + + /* Construct the devpath to use in the detector */ + (void) ddi_pathname(hba, pathbuf); + len = strlen(pathbuf); + (void) snprintf(pathbuf + len, MAXPATHLEN - len, "/%s@%s", + nodename ? nodename : "unknown", ua); + + /* + * Let's review. + * + * Device-as-detector ereports for which the attempted lookup of + * devid and nodename succeeded: + * + * - pathbuf has the full device path including nodename we + * dug up from the devid cache + * + * - class is one of cmd.disk.{dev.rqs.derr,dev.serr} + * + * - devidstr is non NULL and a valid devid string + * + * Would-be device-as-detector ereport for which the attempted lookup + * of devid failed: + * + * - pathbuf has a device path with leaf nodename of "unknown" + * but still including the unit-address + * - class is one of cmd.disk.{dev.rqs.derr,dev.serr} + * + * Transport errors: + * + * class is cmd.disk.tran + * devidstr is NULL + * + * - we may have succeeded in looking up a devid and nodename - + * the devid we'll have added to the payload but we must not + * add to detector FMRI, and if we have have nodename then + * we have a full devpath otherwise one with "unknown" for + * nodename + */ + + if (err) + (void) nvlist_add_boolean_value(pl, "payload-incomplete", + B_TRUE); + + scsi_fm_ereport_post( + sd, + 0, /* path_instance - always 0 */ + pathbuf, /* devpath for detector */ + class, /* ereport class suffix */ + 0, /* ENA - generate for us */ + dad ? devidstr : NULL, /* dtcr devid, dev-as-det only */ + tpl0, /* target-port-l0id */ + DDI_SLEEP, + pl, /* preconstructed payload */ + FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0, + NULL); + + nvlist_free(pl); + if (devidstr_buf) + ddi_devid_str_free(devidstr_buf); + kmem_free(pathbuf, MAXPATHLEN); +} + +#ifdef DEBUG +/* + * Testing - fake scsi_test fails + */ +char scsi_test_fail_ua[SCSI_MAXNAMELEN]; /* unit address to object to */ +int scsi_test_fail_rc = TRAN_ACCEPT; /* scsi_transport return */ +uchar_t scsi_test_fail_pkt_reason = CMD_CMPLT; /* pkt_reason */ +uchar_t scsi_test_fail_status = STATUS_BUSY; /* status */ +uint_t scsi_test_fail_repeat = (uint_t)-1; /* number of times to fail ua */ +#endif + +/* * This is like scsi_poll, but only does retry for TRAN_BUSY. */ -static int -scsi_test(struct scsi_pkt *pkt) +static uint32_t +scsi_test(struct scsi_pkt *pkt, enum scsi_test_ctxt ctxt) { - int rval = -1; + uint32_t rval; int wait_usec; int rc; extern int do_polled_io; @@ -427,19 +939,77 @@ scsi_test(struct scsi_pkt *pkt) } } - if (rc != TRAN_ACCEPT) { - goto exit; - } else if (pkt->pkt_reason == CMD_INCOMPLETE && pkt->pkt_state == 0) { - goto exit; - } else if (pkt->pkt_reason != CMD_CMPLT) { - goto exit; - } else if (((*pkt->pkt_scbp) & STATUS_MASK) == STATUS_BUSY) { - rval = 0; - } else { - rval = 0; +#ifdef DEBUG + if (scsi_test_fail_ua[0] != '\0' && scsi_test_fail_repeat > 0) { + struct scsi_address *ap = &pkt->pkt_address; + struct scsi_device *sd; + dev_info_t *probe; + char ua[SCSI_MAXNAMELEN]; + + if ((sd = scsi_address_device(ap)) != NULL) { + probe = sd->sd_dev; + + if (probe && scsi_ua_get(sd, ua, sizeof (ua)) && + strncmp(ua, scsi_test_fail_ua, sizeof (ua)) == 0) { + scsi_test_fail_repeat--; + rc = scsi_test_fail_rc; + if (rc == TRAN_ACCEPT) + pkt->pkt_reason = + scsi_test_fail_pkt_reason; + *pkt->pkt_scbp = scsi_test_fail_status; + if (scsi_test_fail_status == STATUS_CHECK) + pkt->pkt_state |= STATE_ARQ_DONE; + + } + } + } +#endif + + switch (rc) { + case TRAN_ACCEPT: + switch (pkt->pkt_reason) { + case CMD_CMPLT: + switch ((*pkt->pkt_scbp) & STATUS_MASK) { + case STATUS_GOOD: + rval = SCSI_TEST_CMPLT_GOOD; + break; + + case STATUS_BUSY: + rval = SCSI_TEST_CMPLT_BUSY; + break; + + case STATUS_CHECK: + rval = SCSI_TEST_CMPLT_CHECK; + break; + + default: + rval = SCSI_TEST_CMPLT_OTHER; + break; + } + break; + + case CMD_INCOMPLETE: + rval = SCSI_TEST_CMD_INCOMPLETE; + break; + + default: + rval = SCSI_TEST_NOTCMPLT; + break; + } + break; + + case TRAN_BUSY: + rval = SCSI_TEST_TRAN_BUSY; + break; + + default: + rval = SCSI_TEST_TRAN_REJECT; + break; } -exit: + if (rval != SCSI_TEST_CMPLT_GOOD) + scsi_test_ereport_post(pkt, ctxt, rval); + return (rval); } @@ -510,6 +1080,7 @@ scsi_hba_probe_pi(struct scsi_device *sd, int (*callback)(), int pi) struct buf *rq_bp = NULL; int (*cb_flag)(); int pass = 1; + uint32_t str; if (sd->sd_inq == NULL) { sd->sd_inq = (struct scsi_inquiry *) @@ -565,35 +1136,41 @@ scsi_hba_probe_pi(struct scsi_device *sd, int (*callback)(), int pi) bzero((caddr_t)sd->sd_inq, SUN_INQSIZE); again: FILL_SCSI1_LUN(sd, inq_pkt); - if (scsi_test(inq_pkt) < 0) { - if (inq_pkt->pkt_reason == CMD_INCOMPLETE) { + str = scsi_test(inq_pkt, STC_PROBE_FIRST_INQ); + if (SCSI_TEST_FAILURE(str)) { + if (str == SCSI_TEST_CMD_INCOMPLETE) { rval = SCSIPROBE_NORESP; goto out; - } else { - /* - * retry one more time - */ - if (scsi_test(inq_pkt) < 0) { - rval = SCSIPROBE_FAILURE; - goto out; - } + } + + /* + * Retry one more time for anything other than CMD_INCOMPLETE. + */ + str = scsi_test(inq_pkt, STC_PROBE_FIRST_INQ_RETRY); + if (SCSI_TEST_FAILURE(str)) { + rval = SCSIPROBE_FAILURE; + goto out; } } /* - * if we are lucky, this inquiry succeeded + * Did the inquiry complete and transfer inquiry information, + * perhaps after retry? */ - if ((inq_pkt->pkt_reason == CMD_CMPLT) && - (((*inq_pkt->pkt_scbp) & STATUS_MASK) == 0)) { + if (str == SCSI_TEST_CMPLT_GOOD) goto done; - } /* - * the second inquiry, allows the host adapter to negotiate + * We get here for SCSI_TEST_CMPLT_{BUSY,CHECK,OTHER}. We term + * this "partial success" in that at least something is talking + * to us. + * + * A second inquiry allows the host adapter to negotiate * synchronous transfer period and offset */ - if (scsi_test(inq_pkt) < 0) { - if (inq_pkt->pkt_reason == CMD_INCOMPLETE) + str = scsi_test(inq_pkt, STC_PROBE_PARTIAL_SUCCESS); + if (SCSI_TEST_FAILURE(str)) { + if (str == SCSI_TEST_CMD_INCOMPLETE) rval = SCSIPROBE_NORESP; else rval = SCSIPROBE_FAILURE; @@ -601,72 +1178,73 @@ again: FILL_SCSI1_LUN(sd, inq_pkt); } /* - * if target is still busy, give up now + * If target is still busy, give up now. + * XXX There's no interval between retries - scsi_test should + * probably have a builtin retry on target busy. */ - if (((struct scsi_status *)inq_pkt->pkt_scbp)->sts_busy) { + if (str == SCSI_TEST_CMPLT_BUSY) { rval = SCSIPROBE_BUSY; goto out; } /* - * do a rqsense if there was a check condition and ARQ was not done + * At this point we are SCSI_TEST_CMPLT_GOOD, SCSI_TEST_CMPLT_CHECK + * or SCSI_TEST_CMPLT_OTHER. + * + * Do a rqsense if there was a check condition and ARQ was not done */ - if ((inq_pkt->pkt_state & STATE_ARQ_DONE) == 0) { - if (((struct scsi_status *)inq_pkt->pkt_scbp)->sts_chk) { - - /* - * prepare rqsense packet - * there is no real need for this because the - * check condition should have been cleared by now. - */ - rq_bp = scsi_alloc_consistent_buf(ROUTE, - (struct buf *)NULL, - (uint_t)SENSE_LENGTH, B_READ, cb_flag, NULL); - if (rq_bp == NULL) { - goto out; - } + if (str == SCSI_TEST_CMPLT_CHECK && + (inq_pkt->pkt_state & STATE_ARQ_DONE) == 0) { + /* + * prepare rqsense packet + * there is no real need for this because the + * check condition should have been cleared by now. + */ + rq_bp = scsi_alloc_consistent_buf(ROUTE, (struct buf *)NULL, + (uint_t)SENSE_LENGTH, B_READ, cb_flag, NULL); + if (rq_bp == NULL) { + goto out; + } - rq_pkt = scsi_init_pkt(ROUTE, (struct scsi_pkt *)NULL, - rq_bp, CDB_GROUP0, 1, 0, PKT_CONSISTENT, callback, - NULL); + rq_pkt = scsi_init_pkt(ROUTE, (struct scsi_pkt *)NULL, + rq_bp, CDB_GROUP0, 1, 0, PKT_CONSISTENT, callback, NULL); - if (rq_pkt == NULL) { - if (rq_bp->b_error == 0) - rval = SCSIPROBE_NOMEM_CB; - goto out; - } - ASSERT(rq_bp->b_error == 0); + if (rq_pkt == NULL) { + if (rq_bp->b_error == 0) + rval = SCSIPROBE_NOMEM_CB; + goto out; + } + ASSERT(rq_bp->b_error == 0); - (void) scsi_setup_cdb((union scsi_cdb *)rq_pkt-> - pkt_cdbp, SCMD_REQUEST_SENSE, 0, SENSE_LENGTH, 0); - FILL_SCSI1_LUN(sd, rq_pkt); - rq_pkt->pkt_flags = FLAG_NOINTR|FLAG_NOPARITY; + (void) scsi_setup_cdb((union scsi_cdb *)rq_pkt-> + pkt_cdbp, SCMD_REQUEST_SENSE, 0, SENSE_LENGTH, 0); + FILL_SCSI1_LUN(sd, rq_pkt); + rq_pkt->pkt_flags = FLAG_NOINTR|FLAG_NOPARITY; - /* - * set transport path - */ - if (pi && scsi_pkt_allocated_correctly(rq_pkt)) { - rq_pkt->pkt_path_instance = pi; - rq_pkt->pkt_flags |= FLAG_PKT_PATH_INSTANCE; - } + /* + * set transport path + */ + if (pi && scsi_pkt_allocated_correctly(rq_pkt)) { + rq_pkt->pkt_path_instance = pi; + rq_pkt->pkt_flags |= FLAG_PKT_PATH_INSTANCE; + } - /* - * The FILL_SCSI1_LUN above will find "inq_ansi != 1" - * on first pass, see "again" comment above. - * - * The controller type is as yet unknown, so we - * have to do a throwaway non-extended request sense, - * and hope that that clears the check condition for - * that unit until we can find out what kind of drive - * it is. A non-extended request sense is specified - * by stating that the sense block has 0 length, - * which is taken to mean that it is four bytes in - * length. - */ - if (scsi_test(rq_pkt) < 0) { - rval = SCSIPROBE_FAILURE; - goto out; - } + /* + * The FILL_SCSI1_LUN above will find "inq_ansi != 1" + * on first pass, see "again" comment above. + * + * The controller type is as yet unknown, so we + * have to do a throwaway non-extended request sense, + * and hope that that clears the check condition for + * that unit until we can find out what kind of drive + * it is. A non-extended request sense is specified + * by stating that the sense block has 0 length, + * which is taken to mean that it is four bytes in + * length. + */ + if (SCSI_TEST_FAILURE(scsi_test(rq_pkt, STC_PROBE_RQSENSE1))) { + rval = SCSIPROBE_FAILURE; + goto out; } } @@ -678,12 +1256,13 @@ again: FILL_SCSI1_LUN(sd, inq_pkt); * lie about a unit being ready, e.g., the Emulex MD21). */ - if (scsi_test(inq_pkt) < 0) { + str = scsi_test(inq_pkt, STC_PROBE_CHK_CLEARED); + if (SCSI_TEST_FAILURE(str)) { rval = SCSIPROBE_FAILURE; goto out; } - if (((struct scsi_status *)inq_pkt->pkt_scbp)->sts_busy) { + if (str == SCSI_TEST_CMPLT_BUSY) { rval = SCSIPROBE_BUSY; goto out; } @@ -697,23 +1276,22 @@ again: FILL_SCSI1_LUN(sd, inq_pkt); * target/lun). */ - if (((struct scsi_status *)inq_pkt->pkt_scbp)->sts_chk) { + if (str == SCSI_TEST_CMPLT_CHECK) { /* * try a request sense if we have a pkt, otherwise * just retry the inquiry one more time */ - if (rq_pkt) { - (void) scsi_test(rq_pkt); - } + if (rq_pkt) + (void) scsi_test(rq_pkt, STC_PROBE_RQSENSE2); /* * retry inquiry */ - if (scsi_test(inq_pkt) < 0) { + str = scsi_test(inq_pkt, STC_PROBE_INQ_FINAL); + if (SCSI_TEST_FAILURE(str)) { rval = SCSIPROBE_FAILURE; goto out; - } - if (((struct scsi_status *)inq_pkt->pkt_scbp)->sts_chk) { + } else if (str == SCSI_TEST_CMPLT_CHECK) { rval = SCSIPROBE_FAILURE; goto out; } @@ -997,12 +1575,16 @@ scsi_device_prop_update_inqstring(struct scsi_device *sd, /* * Interfaces associated with SCSI_HBA_ADDR_COMPLEX * per-scsi_device HBA private data support. + * + * scsi_address_device returns NULL if we're not SCSI_HBA_ADDR_COMPLEX, + * thereby allowing use of scsi_address_device as a test for + * SCSI_HBA_ADDR_COMPLEX. */ struct scsi_device * scsi_address_device(struct scsi_address *sa) { - ASSERT(sa->a_hba_tran->tran_hba_flags & SCSI_HBA_ADDR_COMPLEX); - return (sa->a.a_sd); + return ((sa->a_hba_tran->tran_hba_flags & SCSI_HBA_ADDR_COMPLEX) ? + sa->a.a_sd : NULL); } void @@ -1431,7 +2013,7 @@ scsi_device_identity(struct scsi_device *sd, int (*callback)()) } rval = send_scsi_INQUIRY(sd, callback, inq80, - MAX_INQUIRY_SIZE, 0x01, 0x80, &len); + MAX_INQUIRY_SIZE, 0x01, 0x80, &len, STC_IDENTITY_PG80); if (rval) goto out; /* should have worked */ @@ -1454,7 +2036,7 @@ scsi_device_identity(struct scsi_device *sd, int (*callback)()) } rval = send_scsi_INQUIRY(sd, callback, inq83, - MAX_INQUIRY_SIZE, 0x01, 0x83, &len); + MAX_INQUIRY_SIZE, 0x01, 0x83, &len, STC_IDENTITY_PG83); if (rval) goto out; /* should have worked */ @@ -1511,7 +2093,7 @@ check_vpd_page_support8083(struct scsi_device *sd, int (*callback)(), /* issue page 0 (Supported VPD Pages) INQUIRY with evpd set */ rval = send_scsi_INQUIRY(sd, callback, - page_list, MAX_INQUIRY_SIZE_EVPD, 1, 0, NULL); + page_list, MAX_INQUIRY_SIZE_EVPD, 1, 0, NULL, STC_VPD_CHECK); /* * Now we must validate that the device accepted the command (some @@ -1559,7 +2141,8 @@ check_vpd_page_support8083(struct scsi_device *sd, int (*callback)(), static int send_scsi_INQUIRY(struct scsi_device *sd, int (*callback)(), uchar_t *bufaddr, size_t buflen, - uchar_t evpd, uchar_t page_code, size_t *lenp) + uchar_t evpd, uchar_t page_code, size_t *lenp, + enum scsi_test_ctxt ctxt) { int (*cb_flag)(); struct buf *inq_bp; @@ -1601,9 +2184,7 @@ send_scsi_INQUIRY(struct scsi_device *sd, int (*callback)(), * NOPARITY is used. Also seems like we should check pkt_stat for * STATE_XFERRED_DATA. */ - if ((scsi_test(inq_pkt) == 0) && - (inq_pkt->pkt_reason == CMD_CMPLT) && - (((*inq_pkt->pkt_scbp) & STATUS_MASK) == 0)) { + if (scsi_test(inq_pkt, ctxt) == SCSI_TEST_CMPLT_GOOD) { ASSERT(inq_pkt->pkt_resid >= 0); ASSERT(inq_pkt->pkt_resid <= buflen); @@ -1614,6 +2195,10 @@ send_scsi_INQUIRY(struct scsi_device *sd, int (*callback)(), rval = 0; } + /* + * XXX We should retry on target busy + */ + out: if (inq_pkt) scsi_destroy_pkt(inq_pkt); if (inq_bp) diff --git a/usr/src/uts/common/io/scsi/impl/scsi_fm.c b/usr/src/uts/common/io/scsi/impl/scsi_fm.c index b4dad350f5..1ae635b482 100644 --- a/usr/src/uts/common/io/scsi/impl/scsi_fm.c +++ b/usr/src/uts/common/io/scsi/impl/scsi_fm.c @@ -19,8 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. */ /* @@ -36,7 +35,8 @@ /* consolidation private interface to generate dev scheme ereport */ extern void fm_dev_ereport_postv(dev_info_t *dip, dev_info_t *eqdip, const char *devpath, const char *minor_name, const char *devid, - const char *error_class, uint64_t ena, int sflag, va_list ap); + const char *tpl0, const char *error_class, uint64_t ena, int sflag, + nvlist_t *, va_list ap); extern char *mdi_pi_pathname_by_instance(int); #define FM_SCSI_CLASS "scsi" @@ -88,30 +88,45 @@ scsi_fm_fini(struct scsi_device *sd) /* * - * scsi_fm_erepot_post - Post an ereport. + * scsi_fm_ereport_post - Post an ereport */ void scsi_fm_ereport_post(struct scsi_device *sd, int path_instance, - const char *error_class, uint64_t ena, char *devid, int sflag, ...) + char *devpath, const char *error_class, uint64_t ena, + char *devid, char *tpl0, int sflag, nvlist_t *pl, ...) { char class[ERPT_CLASS_SZ]; dev_info_t *dip = sd->sd_dev; - char *devpath, *minor_name; + dev_info_t *eqdip = dip; + char *minor_name; va_list ap; + /* + * If the scsi_device eqdip is not yet ereport capable, send the + * report based on parent capabilities. This is needed for + * telemetry during enumeration. + */ + if (!DDI_FM_EREPORT_CAP(ddi_fm_capable(eqdip))) + eqdip = ddi_get_parent(eqdip); + /* Add "scsi." as a prefix to the class */ (void) snprintf(class, ERPT_CLASS_SZ, "%s.%s", FM_SCSI_CLASS, error_class); /* - * Get the path: If pkt_path_instance is non-zero then the packet was + * Get the path: + * + * If path_instance is non-zero then the packet was * sent to scsi_vhci. We return the pathinfo path_string associated * with the path_instance path - which refers to the actual hardware. + * + * If path_instance is zero then use the devpath provided by the + * caller; if it was NULL then this will cause fm_dev_ereport_post + * to use the devinfo path of the first devi we pass to it, ie + * sd->sd_dev. */ if (path_instance) devpath = mdi_pi_pathname_by_instance(path_instance); - else - devpath = NULL; /* * Set the minor_name to NULL. The block location of a media error @@ -130,8 +145,8 @@ scsi_fm_ereport_post(struct scsi_device *sd, int path_instance, */ /* Post the ereport */ - va_start(ap, sflag); - fm_dev_ereport_postv(dip, dip, devpath, minor_name, devid, - class, ena, sflag, ap); + va_start(ap, pl); + fm_dev_ereport_postv(dip, eqdip, devpath, minor_name, devid, tpl0, + class, ena, sflag, pl, ap); va_end(ap); } diff --git a/usr/src/uts/common/io/scsi/impl/scsi_hba.c b/usr/src/uts/common/io/scsi/impl/scsi_hba.c index 7d1725d33e..990603f5a2 100644 --- a/usr/src/uts/common/io/scsi/impl/scsi_hba.c +++ b/usr/src/uts/common/io/scsi/impl/scsi_hba.c @@ -43,6 +43,7 @@ #include <sys/damap.h> #include <sys/time.h> #include <sys/sunldi.h> +#include <sys/fm/protocol.h> extern struct scsi_pkt *scsi_init_cache_pkt(struct scsi_address *, struct scsi_pkt *, struct buf *, int, int, int, int, @@ -600,8 +601,9 @@ scsi_hba_log(int level, const char *func, dev_info_t *self, dev_info_t *child, mutex_exit(&scsi_hba_log_mutex); } -static int scsi_enumeration_failed_panic = 0; -static int scsi_enumeration_failed_hotplug = 1; +int scsi_enumeration_failed_panic = 0; +int scsi_enumeration_failed_hotplug = 1; + static void scsi_enumeration_failed(dev_info_t *child, scsi_enum_t se, char *arg, char *when) @@ -5094,6 +5096,9 @@ scsi_findchild(dev_info_t *self, char *name, char *addr, int init, * If no driver binds to the device using driver_alias we establish the driver * passed in as the node name. */ + +extern int e_devid_cache_pathinfo(mdi_pathinfo_t *, ddi_devid_t); + static int scsi_device_createchild(dev_info_t *self, char *addr, scsi_enum_t se, struct scsi_device *sdprobe, dev_info_t **dchildp, mdi_pathinfo_t **pchildp) @@ -5380,6 +5385,21 @@ scsi_device_createchild(dev_info_t *self, char *addr, scsi_enum_t se, "pathinfo %s created, no devid", mdi_pi_spathname(pchild))); } + + /* + * The above has registered devid for the device under + * the client node. Now register it under the full pHCI + * path to the device. We'll get an entry equivalent to + * booting with mpxio disabled. This is needed for + * telemetry during enumeration. + */ + if (e_devid_cache_pathinfo(pchild, devid) == DDI_SUCCESS) { + SCSI_HBA_LOG((_LOG(2), NULL, dchild, + "pathinfo @%s created with devid", addr)); + } else { + SCSI_HBA_LOG((_LOG(1), NULL, dchild, + "pathinfo @%s devid cache failed", addr)); + } } /* free the node name and compatible information */ diff --git a/usr/src/uts/common/io/scsi/targets/sd.c b/usr/src/uts/common/io/scsi/targets/sd.c index ab6c1e1d0a..c25241d38e 100644 --- a/usr/src/uts/common/io/scsi/targets/sd.c +++ b/usr/src/uts/common/io/scsi/targets/sd.c @@ -31223,6 +31223,9 @@ sd_tg_getinfo(dev_info_t *devi, int cmd, void *arg, void *tg_cookie) * * Context: Kernel thread or interrupt context. */ + +#define DEVID_IF_KNOWN(d) "devid", DATA_TYPE_STRING, (d) ? (d) : "unknown" + static void sd_ssc_ereport_post(sd_ssc_t *ssc, enum sd_driver_assessment drv_assess) { @@ -31318,9 +31321,11 @@ sd_ssc_ereport_post(sd_ssc_t *ssc, enum sd_driver_assessment drv_assess) * driver-assessment will always be "recovered" here. */ if (drv_assess == SD_FM_DRV_RECOVERY) { - scsi_fm_ereport_post(un->un_sd, uscsi_path_instance, - "cmd.disk.recovered", uscsi_ena, devid, DDI_NOSLEEP, + scsi_fm_ereport_post(un->un_sd, uscsi_path_instance, NULL, + "cmd.disk.recovered", uscsi_ena, devid, NULL, + DDI_NOSLEEP, NULL, FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0, + DEVID_IF_KNOWN(devid), "driver-assessment", DATA_TYPE_STRING, assessment, "op-code", DATA_TYPE_UINT8, op_code, "cdb", DATA_TYPE_UINT8_ARRAY, @@ -31344,8 +31349,10 @@ sd_ssc_ereport_post(sd_ssc_t *ssc, enum sd_driver_assessment drv_assess) if (ssc->ssc_flags & ssc_invalid_flags) { if (ssc->ssc_flags & SSC_FLAGS_INVALID_SENSE) { scsi_fm_ereport_post(un->un_sd, uscsi_path_instance, - "cmd.disk.dev.uderr", uscsi_ena, devid, DDI_NOSLEEP, + NULL, "cmd.disk.dev.uderr", uscsi_ena, devid, + NULL, DDI_NOSLEEP, NULL, FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0, + DEVID_IF_KNOWN(devid), "driver-assessment", DATA_TYPE_STRING, drv_assess == SD_FM_DRV_FATAL ? "fail" : assessment, @@ -31371,8 +31378,11 @@ sd_ssc_ereport_post(sd_ssc_t *ssc, enum sd_driver_assessment drv_assess) * level payload or inside un-decode-info. */ scsi_fm_ereport_post(un->un_sd, uscsi_path_instance, - "cmd.disk.dev.uderr", uscsi_ena, devid, DDI_NOSLEEP, + NULL, + "cmd.disk.dev.uderr", uscsi_ena, devid, + NULL, DDI_NOSLEEP, NULL, FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0, + DEVID_IF_KNOWN(devid), "driver-assessment", DATA_TYPE_STRING, drv_assess == SD_FM_DRV_FATAL ? "fail" : assessment, @@ -31409,9 +31419,10 @@ sd_ssc_ereport_post(sd_ssc_t *ssc, enum sd_driver_assessment drv_assess) if (ssc->ssc_flags & SSC_FLAGS_TRAN_ABORT) ssc->ssc_flags &= ~SSC_FLAGS_TRAN_ABORT; - scsi_fm_ereport_post(un->un_sd, uscsi_path_instance, - "cmd.disk.tran", uscsi_ena, NULL, DDI_NOSLEEP, FM_VERSION, - DATA_TYPE_UINT8, FM_EREPORT_VERS0, + scsi_fm_ereport_post(un->un_sd, uscsi_path_instance, NULL, + "cmd.disk.tran", uscsi_ena, NULL, NULL, DDI_NOSLEEP, NULL, + FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0, + DEVID_IF_KNOWN(devid), "driver-assessment", DATA_TYPE_STRING, drv_assess == SD_FM_DRV_FATAL ? "fail" : assessment, "op-code", DATA_TYPE_UINT8, op_code, @@ -31445,10 +31456,12 @@ sd_ssc_ereport_post(sd_ssc_t *ssc, enum sd_driver_assessment drv_assess) * drv_assess is SD_FM_DRV_FATAL. */ scsi_fm_ereport_post(un->un_sd, - uscsi_path_instance, + uscsi_path_instance, NULL, "cmd.disk.dev.rqs.merr", - uscsi_ena, devid, DDI_NOSLEEP, FM_VERSION, - DATA_TYPE_UINT8, FM_EREPORT_VERS0, + uscsi_ena, devid, NULL, DDI_NOSLEEP, NULL, + FM_VERSION, DATA_TYPE_UINT8, + FM_EREPORT_VERS0, + DEVID_IF_KNOWN(devid), "driver-assessment", DATA_TYPE_STRING, drv_assess == SD_FM_DRV_FATAL ? @@ -31492,11 +31505,13 @@ sd_ssc_ereport_post(sd_ssc_t *ssc, enum sd_driver_assessment drv_assess) * SD_FM_DRV_FATAL. */ scsi_fm_ereport_post(un->un_sd, - uscsi_path_instance, + uscsi_path_instance, NULL, "cmd.disk.dev.rqs.derr", - uscsi_ena, devid, DDI_NOSLEEP, + uscsi_ena, devid, + NULL, DDI_NOSLEEP, NULL, FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0, + DEVID_IF_KNOWN(devid), "driver-assessment", DATA_TYPE_STRING, drv_assess == SD_FM_DRV_FATAL ? @@ -31545,10 +31560,12 @@ sd_ssc_ereport_post(sd_ssc_t *ssc, enum sd_driver_assessment drv_assess) * driver-assessment will be set based on parameter * drv_assess. */ - scsi_fm_ereport_post(un->un_sd, - uscsi_path_instance, "cmd.disk.dev.serr", uscsi_ena, - devid, DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, - FM_EREPORT_VERS0, + scsi_fm_ereport_post(un->un_sd, uscsi_path_instance, + NULL, + "cmd.disk.dev.serr", uscsi_ena, + devid, NULL, DDI_NOSLEEP, NULL, + FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0, + DEVID_IF_KNOWN(devid), "driver-assessment", DATA_TYPE_STRING, drv_assess == SD_FM_DRV_FATAL ? "fail" : assessment, "op-code", DATA_TYPE_UINT8, op_code, diff --git a/usr/src/uts/common/os/ddifm.c b/usr/src/uts/common/os/ddifm.c index a78d949665..8ad563aad2 100644 --- a/usr/src/uts/common/os/ddifm.c +++ b/usr/src/uts/common/os/ddifm.c @@ -19,8 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. */ /* @@ -317,11 +316,29 @@ i_ddi_drv_ereport_post(dev_info_t *dip, const char *error_class, * non-null devpath, minor_name, and devid arguments depending on * wether MPXIO is enabled, and wether a transport or non-transport * error is being posted. + * + * Additional event payload is specified via the varargs plist and, if + * not NULL, the nvlist passed in (such an nvlist will be merged into + * the payload; the caller is responsible for freeing this nvlist). + * Do not specify any high-level protocol event member names as part of the + * payload - eg no payload to be named "class", "version", "detector" etc + * or they will replace the members we construct here. + * + * The 'target-port-l0id' argument is SCSI specific. It is used + * by SCSI enumeration code when a devid is unavailable. If non-NULL + * the property-value becomes part of the ereport detector. The value + * specified might match one of the target-port-l0ids values of a + * libtopo disk chassis node. When libtopo finds a disk with a guaranteed + * unique wWWN target-port of a single-lun 'real' disk, it can add + * the target-port value to the libtopo disk chassis node target-port-l0ids + * string array property. Kernel code has no idea if this type of + * libtopo chassis node exists, or if matching will in fact occur. */ void fm_dev_ereport_postv(dev_info_t *dip, dev_info_t *eqdip, const char *devpath, const char *minor_name, const char *devid, - const char *error_class, uint64_t ena, int sflag, va_list ap) + const char *tpl0, const char *error_class, uint64_t ena, int sflag, + nvlist_t *pl, va_list ap) { nv_alloc_t *nva = NULL; struct i_ddi_fmhdl *fmhdl = NULL; @@ -334,6 +351,7 @@ fm_dev_ereport_postv(dev_info_t *dip, dev_info_t *eqdip, char class[ERPT_CLASS_SZ]; char path[MAXPATHLEN]; + ASSERT(ap != NULL); /* must supply at least ereport version */ ASSERT(dip && eqdip && error_class); /* @@ -376,7 +394,7 @@ fm_dev_ereport_postv(dev_info_t *dip, dev_info_t *eqdip, /* * Form parts of an ereport: * A: version - * B: error_class + * B: error_class * C: ena * D: detector (path and optional devid authority) * E: payload @@ -414,12 +432,17 @@ fm_dev_ereport_postv(dev_info_t *dip, dev_info_t *eqdip, (void) strlcat(path, minor_name, sizeof (path)); } detector = fm_nvlist_create(nva); - fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL, path, devid); + fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL, path, + devid, tpl0); /* Pull parts of ereport together into ereport. */ fm_ereport_set(ereport, version, class, ena, detector, NULL); - /* Add the payload to ereport. */ + /* Merge any preconstructed payload into the event. */ + if (pl) + (void) nvlist_merge(ereport, pl, 0); + + /* Add any remaining (after version) varargs payload to ereport. */ name = va_arg(ap, char *); (void) i_fm_payload_set(ereport, name, ap); @@ -466,8 +489,8 @@ ddi_fm_ereport_post(dev_info_t *dip, ASSERT(dip && error_class); va_start(ap, sflag); - fm_dev_ereport_postv(dip, dip, NULL, NULL, NULL, - error_class, ena, sflag, ap); + fm_dev_ereport_postv(dip, dip, NULL, NULL, NULL, NULL, + error_class, ena, sflag, NULL, ap); va_end(ap); } @@ -479,8 +502,8 @@ ndi_fm_ereport_post(dev_info_t *dip, ASSERT(dip && error_class && (sflag == DDI_SLEEP)); va_start(ap, sflag); - fm_dev_ereport_postv(dip, ddi_get_parent(dip), NULL, NULL, NULL, - error_class, ena, sflag, ap); + fm_dev_ereport_postv(dip, ddi_get_parent(dip), NULL, NULL, NULL, NULL, + error_class, ena, sflag, NULL, ap); va_end(ap); } diff --git a/usr/src/uts/common/os/devid_cache.c b/usr/src/uts/common/os/devid_cache.c index 22d55d535a..dcb3af26d2 100644 --- a/usr/src/uts/common/os/devid_cache.c +++ b/usr/src/uts/common/os/devid_cache.c @@ -32,6 +32,7 @@ #include <sys/hwconf.h> #include <sys/sunddi.h> #include <sys/sunndi.h> +#include <sys/sunmdi.h> #include <sys/ddi_impldefs.h> #include <sys/ndi_impldefs.h> #include <sys/kobj.h> @@ -79,7 +80,7 @@ * Number of times discovery will be attempted prior to mounting root. * Must be done at least once to recover from corrupted or missing * devid cache backing store. Probably there's no reason to ever - * set this to greater than one as a missing device will remain + * set this to greater than one as a missing device will remain * unavailable no matter how often the system searches for it. * * devid_discovery_postboot (default 1) @@ -482,9 +483,14 @@ e_ddi_devid_discovery(ddi_devid_t devid) * As part of registering a devid for a device, * update the devid cache with this device/devid pair * or note that this combination has registered. + * + * If a devpath is provided it will be used as the path to register the + * devid against, otherwise we use ddi_pathname(dip). In both cases + * we duplicate the path string so that it can be cached/freed indepdently + * of the original owner. */ -int -e_devid_cache_register(dev_info_t *dip, ddi_devid_t devid) +static int +e_devid_cache_register_cmn(dev_info_t *dip, ddi_devid_t devid, char *devpath) { nvp_devid_t *np; nvp_devid_t *new_nvp; @@ -496,23 +502,29 @@ e_devid_cache_register(dev_info_t *dip, ddi_devid_t devid) list_t *listp; int is_dirty = 0; - /* - * We are willing to accept DS_BOUND nodes if we can form a full - * ddi_pathname (i.e. the node is part way to becomming - * DS_INITIALIZED and devi_addr/ddi_get_name_addr are non-NULL). - */ - if (ddi_get_name_addr(dip) == NULL) { - return (DDI_FAILURE); - } ASSERT(ddi_devid_valid(devid) == DDI_SUCCESS); - fullpath = kmem_alloc(MAXPATHLEN, KM_SLEEP); - (void) ddi_pathname(dip, fullpath); - pathlen = strlen(fullpath) + 1; - path = kmem_alloc(pathlen, KM_SLEEP); - bcopy(fullpath, path, pathlen); - kmem_free(fullpath, MAXPATHLEN); + if (devpath) { + pathlen = strlen(devpath) + 1; + path = kmem_alloc(pathlen, KM_SLEEP); + bcopy(devpath, path, pathlen); + } else { + /* + * We are willing to accept DS_BOUND nodes if we can form a full + * ddi_pathname (i.e. the node is part way to becomming + * DS_INITIALIZED and devi_addr/ddi_get_name_addr are non-NULL). + */ + if (ddi_get_name_addr(dip) == NULL) + return (DDI_FAILURE); + + fullpath = kmem_alloc(MAXPATHLEN, KM_SLEEP); + (void) ddi_pathname(dip, fullpath); + pathlen = strlen(fullpath) + 1; + path = kmem_alloc(pathlen, KM_SLEEP); + bcopy(fullpath, path, pathlen); + kmem_free(fullpath, MAXPATHLEN); + } DEVID_LOG_REG(("register", devid, path)); @@ -606,6 +618,12 @@ exit: return (DDI_SUCCESS); } +int +e_devid_cache_register(dev_info_t *dip, ddi_devid_t devid) +{ + return (e_devid_cache_register_cmn(dip, devid, NULL)); +} + /* * Unregister a device's devid * Called as an instance detachs @@ -636,6 +654,15 @@ e_devid_cache_unregister(dev_info_t *dip) rw_exit(nvf_lock(dcfd_handle)); } +int +e_devid_cache_pathinfo(mdi_pathinfo_t *pip, ddi_devid_t devid) +{ + char *path = mdi_pi_pathname(pip); + + return (e_devid_cache_register_cmn(mdi_pi_get_client(pip), devid, + path)); +} + /* * Purge devid cache of stale devids */ @@ -987,6 +1014,130 @@ e_devid_cache_free_devt_list(int ndevts, dev_t *devt_list) kmem_free(devt_list, ndevts * sizeof (dev_t *)); } +/* + * If given a full path and NULL ua, search for a cache entry + * whose path matches the full path. On a cache hit duplicate the + * devid of the matched entry into the given devid (caller + * must free); nodenamebuf is not touched for this usage. + * + * Given a path and a non-NULL unit address, search the cache for any entry + * matching "<path>/%@<unit-address>" where '%' is a wildcard meaning + * any node name. The path should not end a '/'. On a cache hit + * duplicate the devid as before (caller must free) and copy into + * the caller-provided nodenamebuf (if not NULL) the nodename of the + * matched entry. + * + * We must not make use of nvp_dip since that may be NULL for cached + * entries that are not present in the current tree. + */ +int +e_devid_cache_path_to_devid(char *path, char *ua, + char *nodenamebuf, ddi_devid_t *devidp) +{ + size_t pathlen, ualen; + int rv = DDI_FAILURE; + nvp_devid_t *np; + list_t *listp; + char *cand; + + if (path == NULL || *path == '\0' || (ua && *ua == '\0') || + devidp == NULL) + return (DDI_FAILURE); + + *devidp = NULL; + + if (ua) { + pathlen = strlen(path); + ualen = strlen(ua); + } + + rw_enter(nvf_lock(dcfd_handle), RW_READER); + + listp = nvf_list(dcfd_handle); + for (np = list_head(listp); np; np = list_next(listp, np)) { + size_t nodelen, candlen, n; + ddi_devid_t devid_dup; + char *uasep, *node; + + if (np->nvp_devid == NULL) + continue; + + if (ddi_devid_valid(np->nvp_devid) != DDI_SUCCESS) { + DEVIDERR((CE_CONT, + "pathsearch: invalid devid %s\n", + np->nvp_devpath)); + continue; + } + + cand = np->nvp_devpath; /* candidate path */ + + /* If a full pathname was provided the compare is easy */ + if (ua == NULL) { + if (strcmp(cand, path) == 0) + goto match; + else + continue; + } + + /* + * The compare for initial path plus ua and unknown nodename + * is trickier. + * + * Does the initial path component match 'path'? + */ + if (strncmp(path, cand, pathlen) != 0) + continue; + + candlen = strlen(cand); + + /* + * The next character must be a '/' and there must be no + * further '/' thereafter. Begin by checking that the + * candidate is long enough to include at mininum a + * "/<nodename>@<ua>" after the initial portion already + * matched assuming a nodename length of 1. + */ + if (candlen < pathlen + 1 + 1 + 1 + ualen || + cand[pathlen] != '/' || + strchr(cand + pathlen + 1, '/') != NULL) + continue; + + node = cand + pathlen + 1; /* <node>@<ua> string */ + + /* + * Find the '@' before the unit address. Check for + * unit address match. + */ + if ((uasep = strchr(node, '@')) == NULL) + continue; + + /* + * Check we still have enough length and that ua matches + */ + nodelen = (uintptr_t)uasep - (uintptr_t)node; + if (candlen < pathlen + 1 + nodelen + 1 + ualen || + strncmp(ua, uasep + 1, ualen) != 0) + continue; +match: + n = ddi_devid_sizeof(np->nvp_devid); + devid_dup = kmem_alloc(n, KM_SLEEP); /* caller must free */ + (void) bcopy(np->nvp_devid, devid_dup, n); + *devidp = devid_dup; + + if (ua && nodenamebuf) { + (void) strncpy(nodenamebuf, node, nodelen); + nodenamebuf[nodelen] = '\0'; + } + + rv = DDI_SUCCESS; + break; + } + + rw_exit(nvf_lock(dcfd_handle)); + + return (rv); +} + #ifdef DEBUG static void devid_log(char *fmt, ddi_devid_t devid, char *path) diff --git a/usr/src/uts/common/os/fm.c b/usr/src/uts/common/os/fm.c index 7dec9d8b91..78943eda82 100644 --- a/usr/src/uts/common/os/fm.c +++ b/usr/src/uts/common/os/fm.c @@ -19,8 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. */ /* @@ -793,6 +792,14 @@ fm_payload_set(nvlist_t *payload, ...) * detector nvlist_t <detector> * ereport-payload nvlist_t <var args> * + * We don't actually add a 'version' member to the payload. Really, + * the version quoted to us by our caller is that of the category 1 + * "ereport" event class (and we require FM_EREPORT_VERS0) but + * the payload version of the actual leaf class event under construction + * may be something else. Callers should supply a version in the varargs, + * or (better) we could take two version arguments - one for the + * ereport category 1 classification (expect FM_EREPORT_VERS0) and one + * for the leaf class. */ void fm_ereport_set(nvlist_t *ereport, int version, const char *erpt_class, @@ -925,46 +932,41 @@ fm_fmri_hc_set(nvlist_t *fmri, int version, const nvlist_t *auth, * version uint8_t 0 * auth nvlist_t <auth> * devpath string <devpath> - * devid string <devid> + * [devid] string <devid> + * [target-port-l0id] string <target-port-lun0-id> * * Note that auth and devid are optional members. */ void fm_fmri_dev_set(nvlist_t *fmri_dev, int version, const nvlist_t *auth, - const char *devpath, const char *devid) + const char *devpath, const char *devid, const char *tpl0) { - if (version != DEV_SCHEME_VERSION0) { - atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1); - return; - } + int err = 0; - if (nvlist_add_uint8(fmri_dev, FM_VERSION, version) != 0) { + if (version != DEV_SCHEME_VERSION0) { atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1); return; } - if (nvlist_add_string(fmri_dev, FM_FMRI_SCHEME, - FM_FMRI_SCHEME_DEV) != 0) { - atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1); - return; - } + err |= nvlist_add_uint8(fmri_dev, FM_VERSION, version); + err |= nvlist_add_string(fmri_dev, FM_FMRI_SCHEME, FM_FMRI_SCHEME_DEV); if (auth != NULL) { - if (nvlist_add_nvlist(fmri_dev, FM_FMRI_AUTHORITY, - (nvlist_t *)auth) != 0) { - atomic_add_64( - &erpt_kstat_data.fmri_set_failed.value.ui64, 1); - } + err |= nvlist_add_nvlist(fmri_dev, FM_FMRI_AUTHORITY, + (nvlist_t *)auth); } - if (nvlist_add_string(fmri_dev, FM_FMRI_DEV_PATH, devpath) != 0) { - atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1); - } + err |= nvlist_add_string(fmri_dev, FM_FMRI_DEV_PATH, devpath); if (devid != NULL) - if (nvlist_add_string(fmri_dev, FM_FMRI_DEV_ID, devid) != 0) - atomic_add_64( - &erpt_kstat_data.fmri_set_failed.value.ui64, 1); + err |= nvlist_add_string(fmri_dev, FM_FMRI_DEV_ID, devid); + + if (tpl0 != NULL) + err |= nvlist_add_string(fmri_dev, FM_FMRI_DEV_TGTPTLUN0, tpl0); + + if (err) + atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1); + } /* diff --git a/usr/src/uts/common/os/pcifm.c b/usr/src/uts/common/os/pcifm.c index 8a1c343b9c..fa6e819a5b 100644 --- a/usr/src/uts/common/os/pcifm.c +++ b/usr/src/uts/common/os/pcifm.c @@ -20,8 +20,7 @@ */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. */ #include <sys/types.h> @@ -1187,7 +1186,7 @@ pci_fm_ereport_post(dev_info_t *dip, const char *error_class, uint64_t ena, (void) ddi_pathname(dip, device_path); fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL, - device_path, NULL); + device_path, NULL, NULL); (void) snprintf(ddi_error_class, FM_MAX_CLASS, "%s.%s", DDI_IO_CLASS, error_class); fm_ereport_set(ereport, version, ddi_error_class, ena, detector, NULL); diff --git a/usr/src/uts/common/sys/fm/protocol.h b/usr/src/uts/common/sys/fm/protocol.h index fbf614caa2..c4103c48a4 100644 --- a/usr/src/uts/common/sys/fm/protocol.h +++ b/usr/src/uts/common/sys/fm/protocol.h @@ -20,8 +20,7 @@ */ /* - * Copyright 2010 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _SYS_FM_PROTOCOL_H @@ -247,6 +246,7 @@ extern "C" { /* dev scheme member names */ #define FM_FMRI_DEV_ID "devid" +#define FM_FMRI_DEV_TGTPTLUN0 "target-port-l0id" #define FM_FMRI_DEV_PATH "device-path" /* pkg scheme member names */ @@ -315,7 +315,7 @@ extern int i_fm_payload_set(nvlist_t *, const char *, va_list); extern void fm_fmri_hc_set(nvlist_t *, int, const nvlist_t *, nvlist_t *, int, ...); extern void fm_fmri_dev_set(nvlist_t *, int, const nvlist_t *, const char *, - const char *); + const char *, const char *); extern void fm_fmri_de_set(nvlist_t *, int, const nvlist_t *, const char *); extern void fm_fmri_cpu_set(nvlist_t *, int, const nvlist_t *, uint32_t, uint8_t *, const char *); diff --git a/usr/src/uts/common/sys/scsi/scsi_fm.h b/usr/src/uts/common/sys/scsi/scsi_fm.h index 9001fcb156..e232da04b1 100644 --- a/usr/src/uts/common/sys/scsi/scsi_fm.h +++ b/usr/src/uts/common/sys/scsi/scsi_fm.h @@ -19,8 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _SYS_SCSI_SCSI_FM_H @@ -40,7 +39,8 @@ void scsi_fm_fini(struct scsi_device *); /* ereport generation: */ void scsi_fm_ereport_post(struct scsi_device *sd, int path_instance, - const char *error_class, uint64_t ena, char *devid, int sflag, ...); + char *devpath, const char *error_class, uint64_t ena, + char *devid, char *tpl0, int sflag, nvlist_t *pl, ...); #ifdef __cplusplus } diff --git a/usr/src/uts/sun4u/io/pci/pci_ecc.c b/usr/src/uts/sun4u/io/pci/pci_ecc.c index e731b1578c..a4f11d7842 100644 --- a/usr/src/uts/sun4u/io/pci/pci_ecc.c +++ b/usr/src/uts/sun4u/io/pci/pci_ecc.c @@ -19,12 +19,9 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * PCI ECC support */ @@ -92,9 +89,9 @@ ecc_create(pci_t *pci_p) DEBUG1(DBG_ATTACH, dip, "ecc_create: csr=%x\n", ecc_p->ecc_csr_pa); DEBUG2(DBG_ATTACH, dip, "ecc_create: ue_afsr=%x, ue_afar=%x\n", - ecc_p->ecc_ue.ecc_afsr_pa, ecc_p->ecc_ue.ecc_afar_pa); + ecc_p->ecc_ue.ecc_afsr_pa, ecc_p->ecc_ue.ecc_afar_pa); DEBUG2(DBG_ATTACH, dip, "ecc_create: ce_afsr=%x, ce_afar=%x\n", - ecc_p->ecc_ce.ecc_afsr_pa, ecc_p->ecc_ce.ecc_afar_pa); + ecc_p->ecc_ce.ecc_afsr_pa, ecc_p->ecc_ce.ecc_afar_pa); ecc_configure(pci_p); @@ -167,11 +164,11 @@ ecc_configure(pci_t *pci_p) */ DEBUG0(DBG_ATTACH, dip, "ecc_configure: clearing UE and CE errors\n"); l = (COMMON_ECC_UE_AFSR_E_MASK << COMMON_ECC_UE_AFSR_PE_SHIFT) | - (COMMON_ECC_UE_AFSR_E_MASK << COMMON_ECC_UE_AFSR_SE_SHIFT); + (COMMON_ECC_UE_AFSR_E_MASK << COMMON_ECC_UE_AFSR_SE_SHIFT); stdphysio(ecc_p->ecc_ue.ecc_afsr_pa, l); l = (COMMON_ECC_CE_AFSR_E_MASK << COMMON_ECC_CE_AFSR_PE_SHIFT) | - (COMMON_ECC_CE_AFSR_E_MASK << COMMON_ECC_CE_AFSR_SE_SHIFT); + (COMMON_ECC_CE_AFSR_E_MASK << COMMON_ECC_CE_AFSR_SE_SHIFT); stdphysio(ecc_p->ecc_ce.ecc_afsr_pa, l); /* @@ -339,19 +336,19 @@ ecc_errstate_get(ecc_errstate_t *ecc_err_p) ecc_err_p->ecc_afar = lddphysio(ecc_err_p->ecc_ii_p.ecc_afar_pa); ecc_err_p->ecc_offset = ((ecc_err_p->ecc_afsr & - ecc_err_p->ecc_ii_p.ecc_offset_mask) >> - ecc_err_p->ecc_ii_p.ecc_offset_shift) << - ecc_err_p->ecc_ii_p.ecc_size_log2; + ecc_err_p->ecc_ii_p.ecc_offset_mask) >> + ecc_err_p->ecc_ii_p.ecc_offset_shift) << + ecc_err_p->ecc_ii_p.ecc_size_log2; ecc_err_p->ecc_aflt.flt_id = gethrtime(); ecc_err_p->ecc_aflt.flt_stat = ecc_err_p->ecc_afsr; ecc_err_p->ecc_aflt.flt_addr = P2ALIGN(ecc_err_p->ecc_afar, 64) + - ecc_err_p->ecc_offset; + ecc_err_p->ecc_offset; ecc_err_p->ecc_aflt.flt_bus_id = bus_id; ecc_err_p->ecc_aflt.flt_inst = CPU->cpu_id; ecc_err_p->ecc_aflt.flt_status = ECC_IOBUS; - ecc_err_p->ecc_aflt.flt_in_memory = (pf_is_memory - (ecc_err_p->ecc_afar >> MMU_PAGESHIFT))? 1: 0; + ecc_err_p->ecc_aflt.flt_in_memory = + (pf_is_memory(ecc_err_p->ecc_afar >> MMU_PAGESHIFT))? 1: 0; ecc_err_p->ecc_aflt.flt_class = BUS_FAULT; } @@ -433,21 +430,21 @@ ecc_err_handler(ecc_errstate_t *ecc_err_p) ecc_errstate_get(ecc_err_p); pri_err = (ecc_err_p->ecc_afsr >> COMMON_ECC_UE_AFSR_PE_SHIFT) & - COMMON_ECC_UE_AFSR_E_MASK; + COMMON_ECC_UE_AFSR_E_MASK; sec_err = (ecc_err_p->ecc_afsr >> COMMON_ECC_UE_AFSR_SE_SHIFT) & - COMMON_ECC_UE_AFSR_E_MASK; + COMMON_ECC_UE_AFSR_E_MASK; switch (ecc_ii_p->ecc_type) { case CBNINTR_UE: if (pri_err) { ecc_err_p->ecc_aflt.flt_synd = - pci_ecc_get_synd(ecc_err_p->ecc_afsr); + pci_ecc_get_synd(ecc_err_p->ecc_afsr); ecc_err_p->ecc_pri = 1; pci_ecc_classify(pri_err, ecc_err_p); errorq_dispatch(pci_ecc_queue, (void *)ecc_err_p, - sizeof (ecc_errstate_t), - ecc_err_p->ecc_aflt.flt_panic); + sizeof (ecc_errstate_t), + ecc_err_p->ecc_aflt.flt_panic); } if (sec_err) { ecc_sec_err = *ecc_err_p; @@ -474,7 +471,7 @@ ecc_err_handler(ecc_errstate_t *ecc_err_p) ecc_err_p->ecc_aflt.flt_panic = 1; if (ecc_err_p->ecc_aflt.flt_panic && - ecc_err_p->ecc_aflt.flt_in_memory) + ecc_err_p->ecc_aflt.flt_in_memory) panic_aflt = ecc_err_p->ecc_aflt; if (ecc_err_p->ecc_aflt.flt_panic) { @@ -492,10 +489,10 @@ ecc_err_handler(ecc_errstate_t *ecc_err_p) ecc_err_p->ecc_pri = 1; pci_ecc_classify(pri_err, ecc_err_p); ecc_err_p->ecc_aflt.flt_synd = - pci_ecc_get_synd(ecc_err_p->ecc_afsr); + pci_ecc_get_synd(ecc_err_p->ecc_afsr); ce_scrub(&ecc_err_p->ecc_aflt); errorq_dispatch(pci_ecc_queue, (void *)ecc_err_p, - sizeof (ecc_errstate_t), ERRORQ_ASYNC); + sizeof (ecc_errstate_t), ERRORQ_ASYNC); nonfatal++; } if (sec_err) { @@ -587,7 +584,7 @@ ecc_err_drain(void *not_used, ecc_errstate_t *ecc_err, errorq_elem_t *eqep) } ecc_cpu_call(ecc, ecc_err->ecc_unum, (ecc_type == CBNINTR_UE) ? - ECC_IO_UE : ECC_IO_CE); + ECC_IO_UE : ECC_IO_CE); switch (ecc_type) { case CBNINTR_UE: @@ -681,7 +678,7 @@ ecc_ereport_post(dev_info_t *dip, ecc_errstate_t *ecc_err) ecc_err->ecc_bridge_type, ecc_err->ecc_aflt.flt_erpt_class); ecc_err->ecc_ena = ecc_err->ecc_ena ? ecc_err->ecc_ena : - fm_ena_generate(0, FM_ENA_FMT1); + fm_ena_generate(0, FM_ENA_FMT1); eqep = errorq_reserve(fmhdl->fh_errorq); if (eqep == NULL) @@ -701,7 +698,8 @@ ecc_ereport_post(dev_info_t *dip, ecc_errstate_t *ecc_err) if (ptr) *ptr = '\0'; - fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL, dev_path, NULL); + fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL, dev_path, + NULL, NULL); if (ecc_err->ecc_pri) { if ((ecc_err->ecc_fmri = fm_nvlist_create(nva)) != NULL) { diff --git a/usr/src/uts/sun4u/io/pci/pcisch.c b/usr/src/uts/sun4u/io/pci/pcisch.c index 1100f8754a..27916bcc74 100644 --- a/usr/src/uts/sun4u/io/pci/pcisch.c +++ b/usr/src/uts/sun4u/io/pci/pcisch.c @@ -19,8 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. */ /* @@ -2839,7 +2838,8 @@ cb_ereport_post(dev_info_t *dip, uint64_t ena, cb_errstate_t *cb_err) if (ptr) *ptr = '\0'; - fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL, dev_path, NULL); + fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL, dev_path, + NULL, NULL); DEBUG1(DBG_ERR_INTR, dip, "cb_ereport_post: ereport_set: %s", buf); diff --git a/usr/src/uts/sun4u/opl/io/mc-opl.c b/usr/src/uts/sun4u/opl/io/mc-opl.c index e76abf21a3..7d267110da 100644 --- a/usr/src/uts/sun4u/opl/io/mc-opl.c +++ b/usr/src/uts/sun4u/opl/io/mc-opl.c @@ -19,14 +19,12 @@ * CDDL HEADER END */ /* - * Copyright 2010 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. */ /* * All Rights Reserved, Copyright (c) FUJITSU LIMITED 2008 */ - #include <sys/types.h> #include <sys/sysmacros.h> #include <sys/conf.h> @@ -968,7 +966,7 @@ mc_ereport_post(mc_aflt_t *mc_aflt) flt_stat = mc_aflt->mflt_stat[0]; (void) ddi_pathname(mc_aflt->mflt_mcp->mc_dip, device_path); (void) fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL, - device_path, NULL); + device_path, NULL, NULL); /* * Encode all the common data into the ereport. |