diff options
author | cth <none@none> | 2008-05-14 04:05:43 -0700 |
---|---|---|
committer | cth <none@none> | 2008-05-14 04:05:43 -0700 |
commit | 602ca9ea8f9ce0933f0944601cc5d230e91a950d (patch) | |
tree | c107b80f6dfed397e1b561d33718e65ba8ee19e7 /usr/src/uts/common/os/ddifm.c | |
parent | 4f7e1866327a77aa6dbef06a88fd04eda82a08f0 (diff) | |
download | illumos-joyent-602ca9ea8f9ce0933f0944601cc5d230e91a950d.tar.gz |
PSARC/2007/522 Disk enumeration for Sun Fire X4200 and X4200 M2
PSARC/2008/077 Multiplexed I/O Enhancements to Support FMA
5039931 glm fails to use DDI-compliant interface for scsi_pkt(9S) allocation
5039932 mpt fails to use DDI-compliant interface for scsi_pkt(9S) allocation
5039935 esp fails to use DDI-compliant interface for scsi_pkt(9S) allocation
5039936 fas fails to use DDI-compliant interface for scsi_pkt(9S) allocation
5039937 ifp fails to use DDI-compliant interface for scsi_pkt(9S) allocation
5039938 isp fails to use DDI-compliant interface for scsi_pkt(9S) allocation
5039941 sf fails to use DDI-compliant interface for scsi_pkt(9S) allocation
6276696 USCSI should support a path selection mechanism in conjunction with scsi_vhci
6284426 di_path_addr should have its second argument removed.
6425326 prtconf pathinfo output should show path's pHCI unit-address (di_path_addr)
6657250 devid should be available at interrupt time
6657251 libtopo: disk enumeration needs to be shared by multiple enumeration strategies
6657252 libtopo: xmlgen files should use consistent format
6657253 fmdump: add support for filtering on nvpair (and value)
6657254 eversholt: support devid-based mapping to topology
6657255 eversholt: define property indicating ereport may not map to topology
6657256 SCSA should detect scsi_pkt allocation violations
6657257 Multiplexed I/O Enhancements to Support FMA
6657258 libnvpair: need nvlist_lookup_nvpair peer that supports embeded nvlist
6695221 scsa1394 fails to use DDI-compliant interface for scsi_pkt(9S) allocation
6695222 ata has dependency on scsi_device(9S) size
6695223 ncrs fails to use DDI-compliant interface for scsi_pkt(9S) allocation
6695224 st fails to use DDI-compliant interface for scsi_pkt(9S) allocation
Diffstat (limited to 'usr/src/uts/common/os/ddifm.c')
-rw-r--r-- | usr/src/uts/common/os/ddifm.c | 319 |
1 files changed, 175 insertions, 144 deletions
diff --git a/usr/src/uts/common/os/ddifm.c b/usr/src/uts/common/os/ddifm.c index a111620eae..20a8cf99cc 100644 --- a/usr/src/uts/common/os/ddifm.c +++ b/usr/src/uts/common/os/ddifm.c @@ -212,119 +212,6 @@ ddi_fm_service_impact(dev_info_t *dip, int svc_impact) mutex_exit(&(DEVI(dip)->devi_lock)); } -static int -erpt_post_sleep(dev_info_t *dip, const char *error_class, uint64_t ena, - uint8_t version, va_list ap) -{ - char *devid, *name; - char device_path[MAXPATHLEN]; - char ddi_error_class[ERPT_CLASS_SZ]; - nvlist_t *ereport, *detector = NULL; - - /* - * Driver defect - should not call with DDI_SLEEP while - * in interrupt context - */ - if (servicing_interrupt()) { - i_ddi_drv_ereport_post(dip, DVR_ECONTEXT, NULL, DDI_NOSLEEP); - return (1); - } - - if ((ereport = fm_nvlist_create(NULL)) == NULL) - return (1); - - /* - * Use the dev_path/devid for this device instance. - */ - detector = fm_nvlist_create(NULL); - if (dip == ddi_root_node()) { - device_path[0] = '/'; - device_path[1] = '\0'; - } else { - (void) ddi_pathname(dip, device_path); - } - - if (ddi_prop_lookup_string(DDI_DEV_T_NONE, dip, - DDI_PROP_DONTPASS, DEVID_PROP_NAME, &devid) == DDI_SUCCESS) { - fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL, - device_path, devid); - ddi_prop_free(devid); - } else { - fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL, - device_path, NULL); - } - - if (ena == 0) - ena = fm_ena_generate(0, FM_ENA_FMT1); - - (void) snprintf(ddi_error_class, ERPT_CLASS_SZ, "%s.%s", - DDI_IO_CLASS, error_class); - - fm_ereport_set(ereport, version, ddi_error_class, - ena, detector, NULL); - - name = va_arg(ap, char *); - (void) i_fm_payload_set(ereport, name, ap); - - fm_ereport_post(ereport, EVCH_SLEEP); - fm_nvlist_destroy(ereport, FM_NVA_FREE); - fm_nvlist_destroy(detector, FM_NVA_FREE); - - return (0); -} - -static int -erpt_post_nosleep(dev_info_t *dip, struct i_ddi_fmhdl *fmhdl, - const char *error_class, uint64_t ena, uint8_t version, va_list ap) -{ - char *name; - char device_path[MAXPATHLEN]; - char ddi_error_class[ERPT_CLASS_SZ]; - nvlist_t *ereport, *detector; - nv_alloc_t *nva; - errorq_elem_t *eqep; - - eqep = errorq_reserve(fmhdl->fh_errorq); - if (eqep == NULL) - return (1); - - ereport = errorq_elem_nvl(fmhdl->fh_errorq, eqep); - nva = errorq_elem_nva(fmhdl->fh_errorq, eqep); - - ASSERT(ereport); - ASSERT(nva); - - /* - * Use the dev_path/devid for this device instance. - */ - detector = fm_nvlist_create(nva); - if (dip == ddi_root_node()) { - device_path[0] = '/'; - device_path[1] = '\0'; - } else { - (void) ddi_pathname(dip, device_path); - } - - fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL, - device_path, NULL); - - if (ena == 0) - ena = fm_ena_generate(0, FM_ENA_FMT1); - - (void) snprintf(ddi_error_class, ERPT_CLASS_SZ, "%s.%s", - DDI_IO_CLASS, error_class); - - fm_ereport_set(ereport, version, ddi_error_class, - ena, detector, NULL); - - name = va_arg(ap, char *); - (void) i_fm_payload_set(ereport, name, ap); - - errorq_commit(fmhdl->fh_errorq, eqep, ERRORQ_ASYNC); - - return (0); -} - void i_ddi_drv_ereport_post(dev_info_t *dip, const char *error_class, nvlist_t *errp, int sflag) @@ -404,54 +291,198 @@ i_ddi_drv_ereport_post(dev_info_t *dip, const char *error_class, } /* - * Generate an error report for consumption by the Solaris Fault Manager, - * fmd(1M). Valid ereport classes are defined in /usr/include/sys/fm/io. The - * ENA should be set if this error is a result of an error status returned - * from ddi_dma_err_check() or ddi_acc_err_check(). Otherwise, an ENA - * value of 0 is appropriate. - * - * If sflag == DDI_NOSLEEP, ddi_fm_ereport_post () may be called - * from user, kernel, interrupt or high-interrupt context. Otherwise, - * ddi_fm_ereport_post() must be called from user or kernel context. + * fm_dev_ereport_postv: Common consolidation private interface to + * post a device tree oriented dev_scheme ereport. The device tree is + * composed of the following entities: devinfo nodes, minor nodes, and + * pathinfo nodes. All entities are associated with some devinfo node, + * either directly or indirectly. The intended devinfo node association + * for the ereport is communicated by the 'dip' argument. A minor node, + * an entity below 'dip', is represented by a non-null 'minor_name' + * argument. An application specific caller, like scsi_fm_ereport_post, + * can override the devinfo path with a pathinfo path via a non-null + * 'devpath' argument - in this case 'dip' is the MPXIO client node and + * devpath should be the path through the pHCI devinfo node to the + * pathinfo node. + * + * This interface also allows the caller to decide if the error being + * reported is know to be associated with a specific device identity + * via the 'devid' argument. The caller needs to control wether the + * devid appears as an authority in the FMRI because for some types of + * errors, like transport errors, the identity of the device on the + * other end of the transport is not guaranteed to be the current + * identity of the dip. For transport errors the caller should specify + * a NULL devid, even when there is a valid devid associated with the dip. + * + * The ddi_fm_ereport_post() implementation calls this interface with + * just a dip: devpath, minor_name, and devid are all NULL. The + * scsi_fm_ereport_post() implementation may call this interface with + * 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. */ void -ddi_fm_ereport_post(dev_info_t *dip, const char *error_class, uint64_t ena, - int sflag, ...) +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) { - int ret; - char *name; - data_type_t type; - uint8_t version; - va_list ap; - struct i_ddi_fmhdl *fmhdl = DEVI(dip)->devi_fmhdl; + struct i_ddi_fmhdl *fmhdl; + errorq_elem_t *eqep; + nv_alloc_t *nva; + nvlist_t *ereport = NULL; + nvlist_t *detector = NULL; + char *name; + data_type_t type; + uint8_t version; + char class[ERPT_CLASS_SZ]; + char path[MAXPATHLEN]; + + ASSERT(dip && eqdip && error_class); - if (!DDI_FM_EREPORT_CAP(ddi_fm_capable(dip))) - return; + /* + * This interface should be called with a fm_capable eqdip. The + * ddi_fm_ereport_post* interfaces call with eqdip == dip, + * ndi_fm_ereport_post* interfaces call with eqdip == ddi_parent(dip). + */ + if (!DDI_FM_EREPORT_CAP(ddi_fm_capable(eqdip))) + goto err; - ASSERT(fmhdl); + /* get ereport nvlist handle */ + if ((sflag == DDI_SLEEP) && !panicstr) { + /* + * Driver defect - should not call with DDI_SLEEP while in + * interrupt context. + */ + if (servicing_interrupt()) { + i_ddi_drv_ereport_post(dip, DVR_ECONTEXT, NULL, sflag); + goto err; + } - va_start(ap, sflag); + /* Use normal interfaces to allocate memory. */ + if ((ereport = fm_nvlist_create(NULL)) == NULL) + goto err; + nva = NULL; + } else { + /* Use errorq interfaces to avoid memory allocation. */ + fmhdl = DEVI(eqdip)->devi_fmhdl; + ASSERT(fmhdl); + eqep = errorq_reserve(fmhdl->fh_errorq); + if (eqep == NULL) + goto err; + + ereport = errorq_elem_nvl(fmhdl->fh_errorq, eqep); + nva = errorq_elem_nva(fmhdl->fh_errorq, eqep); + ASSERT(nva); + } + ASSERT(ereport); - /* First payload tuple should be the version */ + /* + * Form parts of an ereport: + * A: version + * B: error_class + * C: ena + * D: detector (path and optional devid authority) + * E: payload + * + * A: ereport version: first payload tuple must be the version. + */ name = va_arg(ap, char *); type = va_arg(ap, data_type_t); version = va_arg(ap, uint_t); - if (strcmp(name, FM_VERSION) != 0 && type != DATA_TYPE_UINT8) { - va_end(ap); + if ((strcmp(name, FM_VERSION) != 0) || (type != DATA_TYPE_UINT8)) { i_ddi_drv_ereport_post(dip, DVR_EVER, NULL, sflag); - return; + goto err; + } + + /* B: ereport error_class: add "io." prefix to class. */ + (void) snprintf(class, ERPT_CLASS_SZ, "%s.%s", + DDI_IO_CLASS, error_class); + + /* C: ereport ena: if not passed in, generate new ena. */ + if (ena == 0) + ena = fm_ena_generate(0, FM_ENA_FMT1); + + /* D: detector: form dev scheme fmri with path and devid. */ + if (devpath) { + (void) strlcpy(path, devpath, sizeof (path)); + } else { + /* derive devpath from dip */ + if (dip == ddi_root_node()) + (void) strcpy(path, "/"); + else + (void) ddi_pathname(dip, path); + } + if (minor_name) { + (void) strlcat(path, ":", sizeof (path)); + (void) strlcat(path, minor_name, sizeof (path)); } + detector = fm_nvlist_create(nva); + fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL, path, devid); + + /* Pull parts of ereport together into ereport. */ + fm_ereport_set(ereport, version, class, ena, detector, NULL); + + /* Add the payload to ereport. */ + name = va_arg(ap, char *); + (void) i_fm_payload_set(ereport, name, ap); - if (sflag == DDI_SLEEP) - ret = erpt_post_sleep(dip, error_class, ena, version, ap); + /* Post the ereport. */ + if (nva) + errorq_commit(fmhdl->fh_errorq, eqep, ERRORQ_ASYNC); else - ret = erpt_post_nosleep(dip, fmhdl, error_class, ena, version, - ap); - va_end(ap); + fm_ereport_post(ereport, EVCH_SLEEP); + goto out; - if (ret != 0) + /* Count errors as drops. */ +err: if (fmhdl) atomic_add_64(&fmhdl->fh_kstat.fek_erpt_dropped.value.ui64, 1); +out: if (ereport && (nva == NULL)) + fm_nvlist_destroy(ereport, FM_NVA_FREE); + if (detector && (nva == NULL)) + fm_nvlist_destroy(detector, FM_NVA_FREE); +} + +/* + * Generate an error report for consumption by the Solaris Fault Manager, + * fmd(1M). Valid ereport classes are defined in /usr/include/sys/fm/io. + * + * The ENA should be set if this error is a result of an error status + * returned from ddi_dma_err_check() or ddi_acc_err_check(). Otherwise, + * an ENA value of 0 is appropriate. + * + * If sflag == DDI_NOSLEEP, ddi_fm_ereport_post () may be called + * from user, kernel, interrupt or high-interrupt context. Otherwise, + * ddi_fm_ereport_post() must be called from user or kernel context. + * + * The ndi_interfaces are provided for use by nexus drivers to post + * ereports about children who may not themselves be fm_capable. + * + * All interfaces end up in the common fm_dev_ereport_postv code above. + */ +void +ddi_fm_ereport_post(dev_info_t *dip, + const char *error_class, uint64_t ena, int sflag, ...) +{ + va_list ap; + + ASSERT(dip && error_class); + va_start(ap, sflag); + fm_dev_ereport_postv(dip, dip, NULL, NULL, NULL, + error_class, ena, sflag, ap); + va_end(ap); +} + +void +ndi_fm_ereport_post(dev_info_t *dip, + const char *error_class, uint64_t ena, int sflag, ...) +{ + va_list ap; + + 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); + va_end(ap); } /* |