ddifm.c revision eae2e508a8e70b1ec407b10bd068c080651bbe5c
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw/*
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * CDDL HEADER START
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw *
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * The contents of this file are subject to the terms of the
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * Common Development and Distribution License (the "License").
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * You may not use this file except in compliance with the License.
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw *
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * or http://www.opensolaris.org/os/licensing.
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * See the License for the specific language governing permissions
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * and limitations under the License.
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw *
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * When distributing Covered Code, include this CDDL HEADER in each
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * If applicable, add the following below this CDDL HEADER, with the
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * fields enclosed by brackets "[]" replaced with your own identifying
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * information: Portions Copyright [yyyy] [name of copyright owner]
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw *
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * CDDL HEADER END
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw */
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw/*
f96bd5c800e73e351b0b6e4bd7f00b578dad29bbAlan Wright * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * Use is subject to license terms.
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw */
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw
6537f381d2d9e7b4e2f7b29c3e7a3f13be036f2eas#pragma ident "%Z%%M% %I% %E% SMI"
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw/*
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * Fault Management for Device Drivers
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw *
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * Device drivers wishing to participate in fault management may do so by
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * first initializing their fault management state and capabilties via
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * ddi_fm_init(). If the system supports the requested FM capabilities,
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * the IO framework will intialize FM state and return a bit mask of the
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * requested capabilities.
29bd28862cfb8abbd3a0f0a4b17e08bbc3652836Alan Wright *
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * If the system does not support the requested FM capabilities,
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * the device driver must behave in accordance with the programming semantics
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * defined below for the capabilities returned from ddi_fm_init().
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * ddi_fm_init() must be called at attach(9E) time and ddi_fm_fini() must be
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * called from detach(9E) to perform FM clean-up.
6537f381d2d9e7b4e2f7b29c3e7a3f13be036f2eas *
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * Driver Fault Management Capabilities
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego *
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * DDI_FM_NOT_CAPABLE
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego *
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * This is the default fault management capability for drivers. Drivers
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * that implement no fault management capabilites or do not participate
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * in fault management activities have their FM capability bitmask set
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * to 0.
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego *
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * DDI_FM_EREPORT_CAPABLE
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego *
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * When this capability bit is set, drivers are expected to generate error
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * report events via ddi_ereport_post() for the associated faults
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * that are diagnosed by the IO fault manager DE. ddi_ereport_post()
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * may be called in any context subject to the constraints specified
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * by the interrupt iblock cookie returned during initialization.
f96bd5c800e73e351b0b6e4bd7f00b578dad29bbAlan Wright *
f96bd5c800e73e351b0b6e4bd7f00b578dad29bbAlan Wright * Error reports resulting from hardware component specific and common IO
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * fault and driver defects must be accompanied by an Eversholt fault
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * tree (.eft) by the Solaris fault manager (fmd(1M)) for
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * diagnosis.
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego *
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * DDI_FM_ERRCB_CAPABLE
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego *
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * Device drivers are expected to implement and register an error
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * handler callback function. ddi_fm_handler_register() and
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * ddi_fm_handler_unregister() must be
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * called in passive kernel context, typically during an attach(9E)
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * or detach(9E) operation. When called by the FM IO framework,
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * the callback function should check for error conditions for the
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * hardware and software under its control. All detected errors
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * should have ereport events generated for them.
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego *
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * Upon completion of the error handler callback, the driver should
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * return one of the following values:
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego *
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * #define DDI_FM_OK - no error was detected
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * #define DDI_FM_FATAL - a fatal error was detected
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * #define DDI_FM_NONFATAL - a non-fatal error was detected
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * #define DDI_FM_UNKNOWN - the error status is unknown
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego *
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * To insure single threaded access to error handling callbacks,
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * the device driver may use i_ddi_fm_handler_enter() and
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * i_ddi_fm_handler_exit() when entering and exiting the callback.
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego *
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * DDI_FM_ACCCHK_CAPABLE/DDI_FM_DMACHK_CAPABLE
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego *
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * Device drivers are expected to set-up access and DMA handles
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * with FM-specific attributes designed to allow nexus parent
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * drivers to flag any errors seen during subsequent IO transactions.
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * Drivers must set the devacc_attr_acc_flag member of their
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * ddi_device_acc_attr_t structures to DDI_FLAGERR_ACC or DDI_CAUTIOUS_ACC.
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * For DMA transactions, driver must set the dma_attr_flags of
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * their ddi_dma_attr_t structures to DDI_DMA_FLAGERR.
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego *
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * Upon completion of an IO transaction, device drivers are expected
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * to check the status of host-side hardware access and device-side
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * dma completions by calling ddi_acc_err_check() or ddi_dma_err_check()
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * respectively. If the handle is associated with an error detected by
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * the nexus parent or FM IO framework, ddi_fm_error_t data (status, ena
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * and error expectation) is returned. If status of DDI_FM_NONFATAL or
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * DDI_FM_FATAL is returned, the ena is valid and the expectation flag
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * will be set to 1 if the error was unexpected (i.e. not the result
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * of a peek or poke type operation).
6537f381d2d9e7b4e2f7b29c3e7a3f13be036f2eas *
6537f381d2d9e7b4e2f7b29c3e7a3f13be036f2eas * ddi_acc_err_check() and ddi_dma_err_check() may be called in any
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * context subject to the constraints specified by the interrupt
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * iblock cookie returned during initialization.
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego *
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * Device drivers should generate an access (DDI_FM_IO_ACC) or dma
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego * (DDI_FM_IO_DMA) data path error report if DDI_FM_NONFATAL or
6537f381d2d9e7b4e2f7b29c3e7a3f13be036f2eas * DDI_FM_FATAL is returned.
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego *
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego */
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego#include <sys/types.h>
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego#include <sys/sunddi.h>
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego#include <sys/sunndi.h>
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego#include <sys/kmem.h>
6537f381d2d9e7b4e2f7b29c3e7a3f13be036f2eas#include <sys/nvpair.h>
6537f381d2d9e7b4e2f7b29c3e7a3f13be036f2eas#include <sys/fm/protocol.h>
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego#include <sys/ndifm.h>
f96bd5c800e73e351b0b6e4bd7f00b578dad29bbAlan Wright#include <sys/ddifm.h>
f96bd5c800e73e351b0b6e4bd7f00b578dad29bbAlan Wright#include <sys/ddi_impldefs.h>
f96bd5c800e73e351b0b6e4bd7f00b578dad29bbAlan Wright#include <sys/ddi_isa.h>
f96bd5c800e73e351b0b6e4bd7f00b578dad29bbAlan Wright#include <sys/spl.h>
f96bd5c800e73e351b0b6e4bd7f00b578dad29bbAlan Wright#include <sys/varargs.h>
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw#include <sys/systm.h>
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw#include <sys/disp.h>
6537f381d2d9e7b4e2f7b29c3e7a3f13be036f2eas#include <sys/atomic.h>
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw#include <sys/errorq_impl.h>
9fb67ea305c66b6a297583b9b0db6796b0dfe497afshin salek ardakani - Sun Microsystems - Irvine United States#include <sys/kobj.h>
9fb67ea305c66b6a297583b9b0db6796b0dfe497afshin salek ardakani - Sun Microsystems - Irvine United States#include <sys/fm/util.h>
9fb67ea305c66b6a297583b9b0db6796b0dfe497afshin salek ardakani - Sun Microsystems - Irvine United States#include <sys/fm/io/ddi.h>
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw
7f667e74610492ddbce8ce60f52ece95d2401949jose borrego#define ERPT_CLASS_SZ sizeof (DDI_IO_CLASS) + sizeof (FM_EREPORT_CLASS) + \
7f667e74610492ddbce8ce60f52ece95d2401949jose borrego DDI_MAX_ERPT_CLASS + 2
7f667e74610492ddbce8ce60f52ece95d2401949jose borrego/* Globals */
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwint default_dmacache_sz = DEFAULT_DMACACHE_SZ;
7f667e74610492ddbce8ce60f52ece95d2401949jose borregoint default_acccache_sz = DEFAULT_ACCCACHE_SZ;
7f667e74610492ddbce8ce60f52ece95d2401949jose borregoint ddi_system_fmcap = 0;
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw
6537f381d2d9e7b4e2f7b29c3e7a3f13be036f2easstatic struct i_ddi_fmkstat ddifm_kstat_template = {
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw {"erpt_dropped", KSTAT_DATA_UINT64 },
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw {"fm_cache_full", KSTAT_DATA_UINT64 },
9fb67ea305c66b6a297583b9b0db6796b0dfe497afshin salek ardakani - Sun Microsystems - Irvine United States {"fm_cache_grew", KSTAT_DATA_UINT64 },
9fb67ea305c66b6a297583b9b0db6796b0dfe497afshin salek ardakani - Sun Microsystems - Irvine United States {"acc_err", KSTAT_DATA_UINT64 },
7f667e74610492ddbce8ce60f52ece95d2401949jose borrego {"dma_err", KSTAT_DATA_UINT64 }
6537f381d2d9e7b4e2f7b29c3e7a3f13be036f2eas};
6537f381d2d9e7b4e2f7b29c3e7a3f13be036f2eas
9fb67ea305c66b6a297583b9b0db6796b0dfe497afshin salek ardakani - Sun Microsystems - Irvine United States/*
9fb67ea305c66b6a297583b9b0db6796b0dfe497afshin salek ardakani - Sun Microsystems - Irvine United States * Update the service state following the detection of an
9fb67ea305c66b6a297583b9b0db6796b0dfe497afshin salek ardakani - Sun Microsystems - Irvine United States * error.
9fb67ea305c66b6a297583b9b0db6796b0dfe497afshin salek ardakani - Sun Microsystems - Irvine United States */
9fb67ea305c66b6a297583b9b0db6796b0dfe497afshin salek ardakani - Sun Microsystems - Irvine United Statesvoid
7f667e74610492ddbce8ce60f52ece95d2401949jose borregoddi_fm_service_impact(dev_info_t *dip, int svc_impact)
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw{
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw uint64_t ena;
6537f381d2d9e7b4e2f7b29c3e7a3f13be036f2eas char buf[FM_MAX_CLASS];
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw ena = fm_ena_generate(0, FM_ENA_FMT1);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw mutex_enter(&(DEVI(dip)->devi_lock));
7f667e74610492ddbce8ce60f52ece95d2401949jose borrego if (!DEVI_IS_DEVICE_OFFLINE(dip)) {
7f667e74610492ddbce8ce60f52ece95d2401949jose borrego switch (svc_impact) {
7f667e74610492ddbce8ce60f52ece95d2401949jose borrego case DDI_SERVICE_LOST:
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw DEVI_SET_DEVICE_DOWN(dip);
6537f381d2d9e7b4e2f7b29c3e7a3f13be036f2eas (void) snprintf(buf, FM_MAX_CLASS, "%s.%s",
f96bd5c800e73e351b0b6e4bd7f00b578dad29bbAlan Wright DDI_FM_SERVICE_IMPACT, DDI_FM_SERVICE_LOST);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw ddi_fm_ereport_post(dip, buf, ena, DDI_NOSLEEP,
6537f381d2d9e7b4e2f7b29c3e7a3f13be036f2eas FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0,
7f667e74610492ddbce8ce60f52ece95d2401949jose borrego NULL);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw break;
9fb67ea305c66b6a297583b9b0db6796b0dfe497afshin salek ardakani - Sun Microsystems - Irvine United States case DDI_SERVICE_DEGRADED:
9fb67ea305c66b6a297583b9b0db6796b0dfe497afshin salek ardakani - Sun Microsystems - Irvine United States DEVI_SET_DEVICE_DEGRADED(dip);
9fb67ea305c66b6a297583b9b0db6796b0dfe497afshin salek ardakani - Sun Microsystems - Irvine United States if (DEVI_IS_DEVICE_DEGRADED(dip)) {
7f667e74610492ddbce8ce60f52ece95d2401949jose borrego (void) snprintf(buf, FM_MAX_CLASS, "%s.%s",
7f667e74610492ddbce8ce60f52ece95d2401949jose borrego DDI_FM_SERVICE_IMPACT,
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw DDI_FM_SERVICE_DEGRADED);
7f667e74610492ddbce8ce60f52ece95d2401949jose borrego ddi_fm_ereport_post(dip, buf, ena, DDI_NOSLEEP,
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw FM_VERSION, DATA_TYPE_UINT8,
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw FM_EREPORT_VERS0, NULL);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw } else if (DEVI_IS_DEVICE_DOWN(dip)) {
7f667e74610492ddbce8ce60f52ece95d2401949jose borrego (void) snprintf(buf, FM_MAX_CLASS, "%s.%s",
7f667e74610492ddbce8ce60f52ece95d2401949jose borrego DDI_FM_SERVICE_IMPACT,
7f667e74610492ddbce8ce60f52ece95d2401949jose borrego DDI_FM_SERVICE_LOST);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw ddi_fm_ereport_post(dip, buf, ena, DDI_NOSLEEP,
6537f381d2d9e7b4e2f7b29c3e7a3f13be036f2eas FM_VERSION, DATA_TYPE_UINT8,
f96bd5c800e73e351b0b6e4bd7f00b578dad29bbAlan Wright FM_EREPORT_VERS0, NULL);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw }
6537f381d2d9e7b4e2f7b29c3e7a3f13be036f2eas break;
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw case DDI_SERVICE_RESTORED:
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw DEVI_SET_DEVICE_UP(dip);
6537f381d2d9e7b4e2f7b29c3e7a3f13be036f2eas (void) snprintf(buf, FM_MAX_CLASS, "%s.%s",
6537f381d2d9e7b4e2f7b29c3e7a3f13be036f2eas DDI_FM_SERVICE_IMPACT, DDI_FM_SERVICE_RESTORED);
9fb67ea305c66b6a297583b9b0db6796b0dfe497afshin salek ardakani - Sun Microsystems - Irvine United States ddi_fm_ereport_post(dip, buf, ena, DDI_NOSLEEP,
9fb67ea305c66b6a297583b9b0db6796b0dfe497afshin salek ardakani - Sun Microsystems - Irvine United States FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0,
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw NULL);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw break;
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw case DDI_SERVICE_UNAFFECTED:
6537f381d2d9e7b4e2f7b29c3e7a3f13be036f2eas (void) snprintf(buf, FM_MAX_CLASS, "%s.%s",
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw DDI_FM_SERVICE_IMPACT, DDI_FM_SERVICE_UNAFFECTED);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw ddi_fm_ereport_post(dip, buf, ena, DDI_NOSLEEP,
f96bd5c800e73e351b0b6e4bd7f00b578dad29bbAlan Wright FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0,
f96bd5c800e73e351b0b6e4bd7f00b578dad29bbAlan Wright NULL);
f96bd5c800e73e351b0b6e4bd7f00b578dad29bbAlan Wright break;
f96bd5c800e73e351b0b6e4bd7f00b578dad29bbAlan Wright default:
f96bd5c800e73e351b0b6e4bd7f00b578dad29bbAlan Wright break;
f96bd5c800e73e351b0b6e4bd7f00b578dad29bbAlan Wright }
f96bd5c800e73e351b0b6e4bd7f00b578dad29bbAlan Wright }
f96bd5c800e73e351b0b6e4bd7f00b578dad29bbAlan Wright mutex_exit(&(DEVI(dip)->devi_lock));
f96bd5c800e73e351b0b6e4bd7f00b578dad29bbAlan Wright}
f96bd5c800e73e351b0b6e4bd7f00b578dad29bbAlan Wright
f96bd5c800e73e351b0b6e4bd7f00b578dad29bbAlan Wrightstatic int
f96bd5c800e73e351b0b6e4bd7f00b578dad29bbAlan Wrighterpt_post_sleep(dev_info_t *dip, const char *error_class, uint64_t ena,
f96bd5c800e73e351b0b6e4bd7f00b578dad29bbAlan Wright uint8_t version, va_list ap)
f96bd5c800e73e351b0b6e4bd7f00b578dad29bbAlan Wright{
f96bd5c800e73e351b0b6e4bd7f00b578dad29bbAlan Wright char *devid, *name;
9fb67ea305c66b6a297583b9b0db6796b0dfe497afshin salek ardakani - Sun Microsystems - Irvine United States char device_path[MAXPATHLEN];
f96bd5c800e73e351b0b6e4bd7f00b578dad29bbAlan Wright char ddi_error_class[ERPT_CLASS_SZ];
f96bd5c800e73e351b0b6e4bd7f00b578dad29bbAlan Wright nvlist_t *ereport, *detector = NULL;
f96bd5c800e73e351b0b6e4bd7f00b578dad29bbAlan Wright
f96bd5c800e73e351b0b6e4bd7f00b578dad29bbAlan Wright /*
f96bd5c800e73e351b0b6e4bd7f00b578dad29bbAlan Wright * Driver defect - should not call with DDI_SLEEP while
f96bd5c800e73e351b0b6e4bd7f00b578dad29bbAlan Wright * in interrupt context
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego */
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego if (servicing_interrupt()) {
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego i_ddi_drv_ereport_post(dip, DVR_ECONTEXT, NULL, DDI_NOSLEEP);
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego return (1);
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego }
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego if ((ereport = fm_nvlist_create(NULL)) == NULL)
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego return (1);
89dc44ce9705974a8bc4a39f1e878a0491a5be61jose borrego
6537f381d2d9e7b4e2f7b29c3e7a3f13be036f2eas /*
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * Use the dev_path/devid for this device instance.
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw */
29bd28862cfb8abbd3a0f0a4b17e08bbc3652836Alan Wright detector = fm_nvlist_create(NULL);
29bd28862cfb8abbd3a0f0a4b17e08bbc3652836Alan Wright if (dip == ddi_root_node()) {
29bd28862cfb8abbd3a0f0a4b17e08bbc3652836Alan Wright device_path[0] = '/';
29bd28862cfb8abbd3a0f0a4b17e08bbc3652836Alan Wright device_path[1] = '\0';
29bd28862cfb8abbd3a0f0a4b17e08bbc3652836Alan Wright } else {
29bd28862cfb8abbd3a0f0a4b17e08bbc3652836Alan Wright (void) ddi_pathname(dip, device_path);
29bd28862cfb8abbd3a0f0a4b17e08bbc3652836Alan Wright }
29bd28862cfb8abbd3a0f0a4b17e08bbc3652836Alan Wright
29bd28862cfb8abbd3a0f0a4b17e08bbc3652836Alan Wright if (ddi_prop_lookup_string(DDI_DEV_T_NONE, dip,
29bd28862cfb8abbd3a0f0a4b17e08bbc3652836Alan Wright DDI_PROP_DONTPASS, DEVID_PROP_NAME, &devid) == DDI_SUCCESS) {
29bd28862cfb8abbd3a0f0a4b17e08bbc3652836Alan Wright fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL,
29bd28862cfb8abbd3a0f0a4b17e08bbc3652836Alan Wright device_path, devid);
7f667e74610492ddbce8ce60f52ece95d2401949jose borrego ddi_prop_free(devid);
29bd28862cfb8abbd3a0f0a4b17e08bbc3652836Alan Wright } else {
7f667e74610492ddbce8ce60f52ece95d2401949jose borrego fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL,
7f667e74610492ddbce8ce60f52ece95d2401949jose borrego device_path, NULL);
7f667e74610492ddbce8ce60f52ece95d2401949jose borrego }
7f667e74610492ddbce8ce60f52ece95d2401949jose borrego
29bd28862cfb8abbd3a0f0a4b17e08bbc3652836Alan Wright if (ena == 0)
7f667e74610492ddbce8ce60f52ece95d2401949jose borrego ena = fm_ena_generate(0, FM_ENA_FMT1);
7f667e74610492ddbce8ce60f52ece95d2401949jose borrego
7f667e74610492ddbce8ce60f52ece95d2401949jose borrego (void) snprintf(ddi_error_class, ERPT_CLASS_SZ, "%s.%s",
7f667e74610492ddbce8ce60f52ece95d2401949jose borrego DDI_IO_CLASS, error_class);
7f667e74610492ddbce8ce60f52ece95d2401949jose borrego
7f667e74610492ddbce8ce60f52ece95d2401949jose borrego fm_ereport_set(ereport, version, ddi_error_class,
29bd28862cfb8abbd3a0f0a4b17e08bbc3652836Alan Wright ena, detector, NULL);
29bd28862cfb8abbd3a0f0a4b17e08bbc3652836Alan Wright
29bd28862cfb8abbd3a0f0a4b17e08bbc3652836Alan Wright name = va_arg(ap, char *);
29bd28862cfb8abbd3a0f0a4b17e08bbc3652836Alan Wright (void) i_fm_payload_set(ereport, name, ap);
29bd28862cfb8abbd3a0f0a4b17e08bbc3652836Alan Wright
29bd28862cfb8abbd3a0f0a4b17e08bbc3652836Alan Wright fm_ereport_post(ereport, EVCH_SLEEP);
29bd28862cfb8abbd3a0f0a4b17e08bbc3652836Alan Wright fm_nvlist_destroy(ereport, FM_NVA_FREE);
29bd28862cfb8abbd3a0f0a4b17e08bbc3652836Alan Wright fm_nvlist_destroy(detector, FM_NVA_FREE);
29bd28862cfb8abbd3a0f0a4b17e08bbc3652836Alan Wright
29bd28862cfb8abbd3a0f0a4b17e08bbc3652836Alan Wright return (0);
29bd28862cfb8abbd3a0f0a4b17e08bbc3652836Alan Wright}
29bd28862cfb8abbd3a0f0a4b17e08bbc3652836Alan Wright
29bd28862cfb8abbd3a0f0a4b17e08bbc3652836Alan Wrightstatic int
29bd28862cfb8abbd3a0f0a4b17e08bbc3652836Alan Wrighterpt_post_nosleep(dev_info_t *dip, struct i_ddi_fmhdl *fmhdl,
29bd28862cfb8abbd3a0f0a4b17e08bbc3652836Alan Wright const char *error_class, uint64_t ena, uint8_t version, va_list ap)
29bd28862cfb8abbd3a0f0a4b17e08bbc3652836Alan Wright{
29bd28862cfb8abbd3a0f0a4b17e08bbc3652836Alan Wright char *name;
29bd28862cfb8abbd3a0f0a4b17e08bbc3652836Alan Wright char device_path[MAXPATHLEN];
29bd28862cfb8abbd3a0f0a4b17e08bbc3652836Alan Wright char ddi_error_class[ERPT_CLASS_SZ];
29bd28862cfb8abbd3a0f0a4b17e08bbc3652836Alan Wright nvlist_t *ereport, *detector;
29bd28862cfb8abbd3a0f0a4b17e08bbc3652836Alan Wright nv_alloc_t *nva;
29bd28862cfb8abbd3a0f0a4b17e08bbc3652836Alan Wright errorq_elem_t *eqep;
7f667e74610492ddbce8ce60f52ece95d2401949jose borrego
7f667e74610492ddbce8ce60f52ece95d2401949jose borrego eqep = errorq_reserve(fmhdl->fh_errorq);
7f667e74610492ddbce8ce60f52ece95d2401949jose borrego if (eqep == NULL)
29bd28862cfb8abbd3a0f0a4b17e08bbc3652836Alan Wright return (1);
7f667e74610492ddbce8ce60f52ece95d2401949jose borrego
7f667e74610492ddbce8ce60f52ece95d2401949jose borrego ereport = errorq_elem_nvl(fmhdl->fh_errorq, eqep);
7f667e74610492ddbce8ce60f52ece95d2401949jose borrego nva = errorq_elem_nva(fmhdl->fh_errorq, eqep);
7f667e74610492ddbce8ce60f52ece95d2401949jose borrego
7f667e74610492ddbce8ce60f52ece95d2401949jose borrego ASSERT(ereport);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw ASSERT(nva);
9fb67ea305c66b6a297583b9b0db6796b0dfe497afshin salek ardakani - Sun Microsystems - Irvine United States
9fb67ea305c66b6a297583b9b0db6796b0dfe497afshin salek ardakani - Sun Microsystems - Irvine United States /*
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * Use the dev_path/devid for this device instance.
9fb67ea305c66b6a297583b9b0db6796b0dfe497afshin salek ardakani - Sun Microsystems - Irvine United States */
6537f381d2d9e7b4e2f7b29c3e7a3f13be036f2eas detector = fm_nvlist_create(nva);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw if (dip == ddi_root_node()) {
9fb67ea305c66b6a297583b9b0db6796b0dfe497afshin salek ardakani - Sun Microsystems - Irvine United States device_path[0] = '/';
9fb67ea305c66b6a297583b9b0db6796b0dfe497afshin salek ardakani - Sun Microsystems - Irvine United States device_path[1] = '\0';
9fb67ea305c66b6a297583b9b0db6796b0dfe497afshin salek ardakani - Sun Microsystems - Irvine United States } else {
9fb67ea305c66b6a297583b9b0db6796b0dfe497afshin salek ardakani - Sun Microsystems - Irvine United States (void) ddi_pathname(dip, device_path);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw }
9fb67ea305c66b6a297583b9b0db6796b0dfe497afshin salek ardakani - Sun Microsystems - Irvine United States
9fb67ea305c66b6a297583b9b0db6796b0dfe497afshin salek ardakani - Sun Microsystems - Irvine United States fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL,
9fb67ea305c66b6a297583b9b0db6796b0dfe497afshin salek ardakani - Sun Microsystems - Irvine United States device_path, NULL);
9fb67ea305c66b6a297583b9b0db6796b0dfe497afshin salek ardakani - Sun Microsystems - Irvine United States
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw if (ena == 0)
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw ena = fm_ena_generate(0, FM_ENA_FMT1);
6537f381d2d9e7b4e2f7b29c3e7a3f13be036f2eas
6537f381d2d9e7b4e2f7b29c3e7a3f13be036f2eas (void) snprintf(ddi_error_class, ERPT_CLASS_SZ, "%s.%s",
9fb67ea305c66b6a297583b9b0db6796b0dfe497afshin salek ardakani - Sun Microsystems - Irvine United States DDI_IO_CLASS, error_class);
6537f381d2d9e7b4e2f7b29c3e7a3f13be036f2eas
6537f381d2d9e7b4e2f7b29c3e7a3f13be036f2eas fm_ereport_set(ereport, version, ddi_error_class,
6537f381d2d9e7b4e2f7b29c3e7a3f13be036f2eas ena, detector, NULL);
9fb67ea305c66b6a297583b9b0db6796b0dfe497afshin salek ardakani - Sun Microsystems - Irvine United States
9fb67ea305c66b6a297583b9b0db6796b0dfe497afshin salek ardakani - Sun Microsystems - Irvine United States name = va_arg(ap, char *);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw (void) i_fm_payload_set(ereport, name, ap);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw errorq_commit(fmhdl->fh_errorq, eqep, ERRORQ_ASYNC);
9fb67ea305c66b6a297583b9b0db6796b0dfe497afshin salek ardakani - Sun Microsystems - Irvine United States
9fb67ea305c66b6a297583b9b0db6796b0dfe497afshin salek ardakani - Sun Microsystems - Irvine United States return (0);
9fb67ea305c66b6a297583b9b0db6796b0dfe497afshin salek ardakani - Sun Microsystems - Irvine United States}
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwvoid
9fb67ea305c66b6a297583b9b0db6796b0dfe497afshin salek ardakani - Sun Microsystems - Irvine United Statesi_ddi_drv_ereport_post(dev_info_t *dip, const char *error_class,
9fb67ea305c66b6a297583b9b0db6796b0dfe497afshin salek ardakani - Sun Microsystems - Irvine United States nvlist_t *errp, int sflag)
9fb67ea305c66b6a297583b9b0db6796b0dfe497afshin salek ardakani - Sun Microsystems - Irvine United States{
9fb67ea305c66b6a297583b9b0db6796b0dfe497afshin salek ardakani - Sun Microsystems - Irvine United States int i;
6537f381d2d9e7b4e2f7b29c3e7a3f13be036f2eas int depth;
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw char classp[DDI_DVR_MAX_CLASS];
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw caddr_t stkp;
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw char *buf;
6537f381d2d9e7b4e2f7b29c3e7a3f13be036f2eas char **stkpp;
6537f381d2d9e7b4e2f7b29c3e7a3f13be036f2eas char *sym;
6537f381d2d9e7b4e2f7b29c3e7a3f13be036f2eas pc_t stack[DDI_FM_STKDEPTH];
6537f381d2d9e7b4e2f7b29c3e7a3f13be036f2eas ulong_t off;
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw dev_info_t *root_dip = ddi_root_node();
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw if (!DDI_FM_EREPORT_CAP(ddi_fm_capable(root_dip)))
return;
(void) snprintf(classp, DDI_DVR_MAX_CLASS, "%s%s", DVR_ERPT,
error_class);
if (sflag == DDI_SLEEP) {
depth = getpcstack(stack, DDI_FM_STKDEPTH);
/* Allocate array of char * for nvlist payload */
stkpp = (char **)kmem_alloc(depth * sizeof (char *), KM_SLEEP);
/*
* Allocate temporary 64-bit aligned buffer for stack
* symbol strings
*/
buf = kmem_alloc(depth * DDI_FM_SYM_SZ, KM_SLEEP);
stkp = buf;
for (i = 0; i < depth; ++i) {
sym = kobj_getsymname(stack[i], &off);
(void) snprintf(stkp, DDI_FM_SYM_SZ,
"\t%s+%lx\n", sym ? sym : "?", off);
stkpp[i] = stkp;
stkp += DDI_FM_SYM_SZ;
}
if (errp)
ddi_fm_ereport_post(root_dip,
classp, fm_ena_generate(0, FM_ENA_FMT1), sflag,
FM_VERSION, DATA_TYPE_UINT8, 0,
DVR_NAME, DATA_TYPE_STRING, ddi_driver_name(dip),
DVR_STACK_DEPTH, DATA_TYPE_UINT32, depth,
DVR_STACK, DATA_TYPE_STRING_ARRAY, depth, stkpp,
DVR_ERR_SPECIFIC, DATA_TYPE_NVLIST, errp, NULL);
else
ddi_fm_ereport_post(root_dip,
classp, fm_ena_generate(0, FM_ENA_FMT1), sflag,
FM_VERSION, DATA_TYPE_UINT8, 0,
DVR_NAME, DATA_TYPE_STRING, ddi_driver_name(dip),
DVR_STACK_DEPTH, DATA_TYPE_UINT32, depth,
DVR_STACK, DATA_TYPE_STRING_ARRAY, depth, stkpp,
NULL);
kmem_free(stkpp, depth * sizeof (char *));
kmem_free(buf, depth * DDI_FM_SYM_SZ);
} else {
if (errp)
ddi_fm_ereport_post(root_dip,
classp, fm_ena_generate(0, FM_ENA_FMT1), sflag,
FM_VERSION, DATA_TYPE_UINT8, 0,
DVR_NAME, DATA_TYPE_STRING, ddi_driver_name(dip),
DVR_ERR_SPECIFIC, DATA_TYPE_NVLIST, errp, NULL);
else
ddi_fm_ereport_post(root_dip,
classp, fm_ena_generate(0, FM_ENA_FMT1), sflag,
FM_VERSION, DATA_TYPE_UINT8, 0,
DVR_NAME, DATA_TYPE_STRING, ddi_driver_name(dip),
NULL);
}
}
/*
* 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.
*/
void
ddi_fm_ereport_post(dev_info_t *dip, const char *error_class, uint64_t ena,
int sflag, ...)
{
int ret;
char *name;
data_type_t type;
uint8_t version;
va_list ap;
struct i_ddi_fmhdl *fmhdl = DEVI(dip)->devi_fmhdl;
if (!DDI_FM_EREPORT_CAP(ddi_fm_capable(dip)))
return;
ASSERT(fmhdl);
va_start(ap, sflag);
/* First payload tuple should 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);
i_ddi_drv_ereport_post(dip, DVR_EVER, NULL, sflag);
return;
}
if (sflag == DDI_SLEEP)
ret = erpt_post_sleep(dip, error_class, ena, version, ap);
else
ret = erpt_post_nosleep(dip, fmhdl, error_class, ena, version,
ap);
va_end(ap);
if (ret != 0)
atomic_add_64(&fmhdl->fh_kstat.fek_erpt_dropped.value.ui64, 1);
}
/*
* Driver error handling entry. Prevents multiple simultaneous calls into
* driver error handling callback.
*
* May be called from a context consistent with the iblock_cookie returned
* in ddi_fm_init().
*/
void
i_ddi_fm_handler_enter(dev_info_t *dip)
{
struct i_ddi_fmhdl *hdl = DEVI(dip)->devi_fmhdl;
mutex_enter(&hdl->fh_lock);
hdl->fh_lock_owner = curthread;
}
/*
* Driver error handling exit.
*
* May be called from a context consistent with the iblock_cookie returned
* in ddi_fm_init().
*/
void
i_ddi_fm_handler_exit(dev_info_t *dip)
{
struct i_ddi_fmhdl *hdl = DEVI(dip)->devi_fmhdl;
hdl->fh_lock_owner = NULL;
mutex_exit(&hdl->fh_lock);
}
boolean_t
i_ddi_fm_handler_owned(dev_info_t *dip)
{
struct i_ddi_fmhdl *hdl = DEVI(dip)->devi_fmhdl;
return (hdl->fh_lock_owner == curthread);
}
/*
* Register a fault manager error handler for this device instance
*
* This function must be called from a driver's attach(9E) routine.
*/
void
ddi_fm_handler_register(dev_info_t *dip, ddi_err_func_t handler,
void *impl_data)
{
dev_info_t *pdip;
struct i_ddi_fmhdl *pfmhdl;
struct i_ddi_errhdl *new_eh;
struct i_ddi_fmtgt *tgt;
/*
* Check for proper calling context.
* The DDI configuration framework does not support
* DR states to allow checking for proper invocation
* from a DDI_ATTACH or DDI_RESUME. This limits context checking
* to interrupt only.
*/
if (servicing_interrupt()) {
i_ddi_drv_ereport_post(dip, DVR_ECONTEXT, NULL, DDI_NOSLEEP);
return;
}
if (dip == ddi_root_node())
pdip = dip;
else
pdip = (dev_info_t *)DEVI(dip)->devi_parent;
ASSERT(pdip);
if (!(DDI_FM_ERRCB_CAP(ddi_fm_capable(dip)) &&
DDI_FM_ERRCB_CAP(ddi_fm_capable(pdip)))) {
i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_SLEEP);
return;
}
new_eh = kmem_zalloc(sizeof (struct i_ddi_errhdl), KM_SLEEP);
new_eh->eh_func = handler;
new_eh->eh_impl = impl_data;
/* Add dip to parent's target list of registered error handlers */
tgt = kmem_alloc(sizeof (struct i_ddi_fmtgt), KM_SLEEP);
tgt->ft_dip = dip;
tgt->ft_errhdl = new_eh;
i_ddi_fm_handler_enter(pdip);
pfmhdl = DEVI(pdip)->devi_fmhdl;
ASSERT(pfmhdl);
tgt->ft_next = pfmhdl->fh_tgts;
pfmhdl->fh_tgts = tgt;
i_ddi_fm_handler_exit(pdip);
}
/*
* Unregister a fault manager error handler for this device instance
*
* This function must be called from a drivers attach(9E) or detach(9E)
* routine.
*/
void
ddi_fm_handler_unregister(dev_info_t *dip)
{
dev_info_t *pdip;
struct i_ddi_fmhdl *pfmhdl;
struct i_ddi_fmtgt *tgt, **ptgt;
/*
* Check for proper calling context.
* The DDI configuration framework does not support
* DR states to allow checking for proper invocation
* from a DDI_DETACH or DDI_SUSPEND. This limits context checking
* to interrupt only.
*/
if (servicing_interrupt()) {
i_ddi_drv_ereport_post(dip, DVR_ECONTEXT, NULL, DDI_NOSLEEP);
return;
}
if (dip == ddi_root_node())
pdip = dip;
else
pdip = (dev_info_t *)DEVI(dip)->devi_parent;
ASSERT(pdip);
if (!(DDI_FM_ERRCB_CAP(ddi_fm_capable(dip)) &&
DDI_FM_ERRCB_CAP(ddi_fm_capable(pdip)))) {
i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_SLEEP);
return;
}
i_ddi_fm_handler_enter(pdip);
pfmhdl = DEVI(pdip)->devi_fmhdl;
ASSERT(pfmhdl);
ptgt = &pfmhdl->fh_tgts;
for (tgt = pfmhdl->fh_tgts; tgt != NULL; tgt = tgt->ft_next) {
if (dip == tgt->ft_dip) {
*ptgt = tgt->ft_next;
kmem_free(tgt->ft_errhdl, sizeof (struct i_ddi_errhdl));
kmem_free(tgt, sizeof (struct i_ddi_fmtgt));
break;
}
ptgt = &tgt->ft_next;
}
i_ddi_fm_handler_exit(pdip);
}
/*
* Initialize Fault Management capabilities for this device instance (dip).
* When called with the following capabilities, data structures neccessary
* for fault management activities are allocated and initialized.
*
* DDI_FM_EREPORT_CAPABLE - initialize ereport errorq and ereport
* capable driver property.
*
* DDI_FM_ERRCB_CAPABLE - check with parent for ability to register
* an error handler.
*
* DDI_FM_ACCCHK_CAPABLE - initialize access handle cache and acc-chk
* driver property
*
* DDI_FM_DMACHK_CAPABLE - initialize dma handle cache and dma-chk
* driver property
*
* A driver's FM capability level may not exceed that of its parent or
* system-wide FM capability. The available capability level for this
* device instance is returned in *fmcap.
*
* This function must be called from a driver's attach(9E) entry point.
*/
void
ddi_fm_init(dev_info_t *dip, int *fmcap, ddi_iblock_cookie_t *ibcp)
{
struct dev_info *devi = DEVI(dip);
struct i_ddi_fmhdl *fmhdl;
ddi_iblock_cookie_t ibc;
int pcap, newcap = DDI_FM_NOT_CAPABLE;
if (!DEVI_IS_ATTACHING(dip)) {
i_ddi_drv_ereport_post(dip, DVR_ECONTEXT, NULL, DDI_NOSLEEP);
*fmcap = DDI_FM_NOT_CAPABLE;
return;
}
if (DDI_FM_DEFAULT_CAP(*fmcap))
return;
/*
* Check parent for supported FM level
* and correct error handling PIL
*/
if (dip != ddi_root_node()) {
/*
* Initialize the default ibc. The parent may change it
* depending upon its capabilities.
*/
ibc = (ddi_iblock_cookie_t)ipltospl(FM_ERR_PIL);
pcap = i_ndi_busop_fm_init(dip, *fmcap, &ibc);
} else {
pcap = *fmcap;
ibc = *ibcp;
}
/* Initialize the per-device instance FM handle */
fmhdl = kmem_zalloc(sizeof (struct i_ddi_fmhdl), KM_SLEEP);
if ((fmhdl->fh_ksp = kstat_create((char *)ddi_driver_name(dip),
ddi_get_instance(dip), "fm", "misc",
KSTAT_TYPE_NAMED, sizeof (struct i_ddi_fmkstat) /
sizeof (kstat_named_t), KSTAT_FLAG_VIRTUAL)) == NULL) {
mutex_destroy(&fmhdl->fh_lock);
kmem_free(fmhdl, sizeof (struct i_ddi_fmhdl));
*fmcap = DDI_FM_NOT_CAPABLE;
return;
}
bcopy(&ddifm_kstat_template, &fmhdl->fh_kstat,
sizeof (struct i_ddi_fmkstat));
fmhdl->fh_ksp->ks_data = &fmhdl->fh_kstat;
fmhdl->fh_ksp->ks_private = fmhdl;
kstat_install(fmhdl->fh_ksp);
fmhdl->fh_dma_cache = NULL;
fmhdl->fh_acc_cache = NULL;
fmhdl->fh_tgts = NULL;
fmhdl->fh_dip = dip;
fmhdl->fh_ibc = ibc;
mutex_init(&fmhdl->fh_lock, NULL, MUTEX_DRIVER, fmhdl->fh_ibc);
devi->devi_fmhdl = fmhdl;
/*
* Initialize support for ereport generation
*/
if (DDI_FM_EREPORT_CAP(*fmcap) && DDI_FM_EREPORT_CAP(pcap)) {
fmhdl->fh_errorq = ereport_errorq;
if (ddi_getprop(DDI_DEV_T_NONE, dip, DDI_PROP_DONTPASS,
"fm-ereport-capable", 0) == 0)
(void) ddi_prop_create(DDI_DEV_T_NONE, dip,
DDI_PROP_CANSLEEP, "fm-ereport-capable", NULL, 0);
newcap |= DDI_FM_EREPORT_CAPABLE;
}
/*
* Need cooperation of the parent for error handling
*/
if (DDI_FM_ERRCB_CAP(*fmcap) && DDI_FM_ERRCB_CAP(pcap)) {
if (ddi_getprop(DDI_DEV_T_NONE, dip, DDI_PROP_DONTPASS,
"fm-errcb-capable", 0) == 0)
(void) ddi_prop_create(DDI_DEV_T_NONE, dip,
DDI_PROP_CANSLEEP, "fm-errcb-capable", NULL, 0);
newcap |= DDI_FM_ERRCB_CAPABLE;
}
/*
* Support for DMA and Access error handling
*/
if (DDI_FM_DMA_ERR_CAP(*fmcap) && DDI_FM_DMA_ERR_CAP(pcap)) {
i_ndi_fmc_create(&fmhdl->fh_dma_cache, 2, ibc);
/* Set-up dma chk capability prop */
if (ddi_getprop(DDI_DEV_T_NONE, dip, DDI_PROP_DONTPASS,
"fm-dmachk-capable", 0) == 0)
(void) ddi_prop_create(DDI_DEV_T_NONE, dip,
DDI_PROP_CANSLEEP, "fm-dmachk-capable", NULL, 0);
newcap |= DDI_FM_DMACHK_CAPABLE;
}
if (DDI_FM_ACC_ERR_CAP(*fmcap) && DDI_FM_ACC_ERR_CAP(pcap)) {
i_ndi_fmc_create(&fmhdl->fh_acc_cache, 2, ibc);
/* Set-up dma chk capability prop */
if (ddi_getprop(DDI_DEV_T_NONE, dip, DDI_PROP_DONTPASS,
"fm-accchk-capable", 0) == 0)
(void) ddi_prop_create(DDI_DEV_T_NONE, dip,
DDI_PROP_CANSLEEP, "fm-accchk-capable", NULL, 0);
newcap |= DDI_FM_ACCCHK_CAPABLE;
}
/*
* Return the capability support available
* to this driver instance
*/
fmhdl->fh_cap = newcap;
*fmcap = newcap;
if (ibcp != NULL)
*ibcp = ibc;
}
/*
* Finalize Fault Management activities for this device instance.
* Outstanding IO transaction must be completed prior to calling
* this routine. All previously allocated resources and error handler
* registration are cleared and deallocated.
*
* This function must be called from a driver's detach(9E) entry point.
*/
void
ddi_fm_fini(dev_info_t *dip)
{
struct i_ddi_fmhdl *fmhdl = DEVI(dip)->devi_fmhdl;
ASSERT(fmhdl);
if (!(DEVI_IS_DETACHING(dip) || DEVI_IS_ATTACHING(dip))) {
i_ddi_drv_ereport_post(dip, DVR_ECONTEXT, NULL, DDI_NOSLEEP);
return;
}
kstat_delete(fmhdl->fh_ksp);
if (DDI_FM_EREPORT_CAP(fmhdl->fh_cap)) {
(void) ddi_prop_remove(DDI_DEV_T_NONE, dip,
"fm-ereport-capable");
}
if (dip != ddi_root_node()) {
if (DDI_FM_ERRCB_CAP(fmhdl->fh_cap)) {
ddi_fm_handler_unregister(dip);
(void) ddi_prop_remove(DDI_DEV_T_NONE, dip,
"fm-errcb-capable");
}
if (DDI_FM_DMA_ERR_CAP(fmhdl->fh_cap) ||
DDI_FM_ACC_ERR_CAP(fmhdl->fh_cap)) {
if (fmhdl->fh_dma_cache != NULL) {
i_ndi_fmc_destroy(fmhdl->fh_dma_cache);
(void) ddi_prop_remove(DDI_DEV_T_NONE, dip,
"fm-dmachk-capable");
}
if (fmhdl->fh_acc_cache != NULL) {
i_ndi_fmc_destroy(fmhdl->fh_acc_cache);
(void) ddi_prop_remove(DDI_DEV_T_NONE, dip,
"fm-accachk-capable");
}
}
i_ndi_busop_fm_fini(dip);
}
kmem_free(fmhdl, sizeof (struct i_ddi_fmhdl));
DEVI(dip)->devi_fmhdl = NULL;
}
/*
* Return the fault management capability level for this device instance.
*
* This function may be called from user, kernel, or interrupt context.
*/
int
ddi_fm_capable(dev_info_t *dip)
{
struct i_ddi_fmhdl *fmhdl = DEVI(dip)->devi_fmhdl;
if (fmhdl == NULL)
return (DDI_FM_NOT_CAPABLE);
return (fmhdl->fh_cap);
}
/*
* Routines to set and get error information for/from an access or dma handle
*
* These routines may be called from user, kernel, and interrupt contexts.
*/
void
ddi_fm_acc_err_get(ddi_acc_handle_t handle, ddi_fm_error_t *de, int version)
{
ndi_err_t *errp;
if (handle == NULL)
return;
if (version != DDI_FME_VER0 && version != DDI_FME_VER1) {
ddi_acc_hdl_t *hp = impl_acc_hdl_get(handle);
i_ddi_drv_ereport_post(hp->ah_dip, DVR_EVER, NULL, DDI_NOSLEEP);
cmn_err(CE_PANIC, "ddi_fm_acc_err_get: "
"Invalid driver version\n");
}
errp = ((ddi_acc_impl_t *)handle)->ahi_err;
de->fme_status = errp->err_status;
de->fme_ena = errp->err_ena;
de->fme_flag = errp->err_expected;
de->fme_acc_handle = handle;
}
void
ddi_fm_dma_err_get(ddi_dma_handle_t handle, ddi_fm_error_t *de, int version)
{
ndi_err_t *errp;
if (handle == NULL)
return;
if (version != DDI_FME_VER0 && version != DDI_FME_VER1) {
i_ddi_drv_ereport_post(((ddi_dma_impl_t *)handle)->dmai_rdip,
DVR_EVER, NULL, DDI_NOSLEEP);
cmn_err(CE_PANIC, "ddi_fm_dma_err_get: "
"Invalid driver version\n");
}
errp = &((ddi_dma_impl_t *)handle)->dmai_error;
de->fme_status = errp->err_status;
de->fme_ena = errp->err_ena;
de->fme_flag = errp->err_expected;
de->fme_dma_handle = handle;
}
void
ddi_fm_acc_err_clear(ddi_acc_handle_t handle, int version)
{
ndi_err_t *errp;
if (handle == NULL)
return;
if (version != DDI_FME_VER0 && version != DDI_FME_VER1) {
ddi_acc_hdl_t *hp = impl_acc_hdl_get(handle);
i_ddi_drv_ereport_post(hp->ah_dip, DVR_EVER, NULL, DDI_NOSLEEP);
cmn_err(CE_PANIC, "ddi_fm_acc_err_clear: "
"Invalid driver version\n");
}
errp = ((ddi_acc_impl_t *)handle)->ahi_err;
errp->err_status = DDI_FM_OK;
errp->err_ena = 0;
errp->err_expected = DDI_FM_ERR_UNEXPECTED;
}
void
ddi_fm_dma_err_clear(ddi_dma_handle_t handle, int version)
{
ndi_err_t *errp;
if (handle == NULL)
return;
if (version != DDI_FME_VER0 && version != DDI_FME_VER1) {
i_ddi_drv_ereport_post(((ddi_dma_impl_t *)handle)->dmai_rdip,
DVR_EVER, NULL, DDI_NOSLEEP);
cmn_err(CE_PANIC, "ddi_fm_dma_err_clear: "
"Invalid driver version\n");
}
errp = &((ddi_dma_impl_t *)handle)->dmai_error;
errp->err_status = DDI_FM_OK;
errp->err_ena = 0;
errp->err_expected = DDI_FM_ERR_UNEXPECTED;
}
void
i_ddi_fm_acc_err_set(ddi_acc_handle_t handle, uint64_t ena, int status,
int flag)
{
ddi_acc_hdl_t *hdlp = impl_acc_hdl_get(handle);
ddi_acc_impl_t *i_hdlp = (ddi_acc_impl_t *)handle;
struct i_ddi_fmhdl *fmhdl = DEVI(hdlp->ah_dip)->devi_fmhdl;
i_hdlp->ahi_err->err_ena = ena;
i_hdlp->ahi_err->err_status = status;
i_hdlp->ahi_err->err_expected = flag;
atomic_add_64(&fmhdl->fh_kstat.fek_acc_err.value.ui64, 1);
}
void
i_ddi_fm_dma_err_set(ddi_dma_handle_t handle, uint64_t ena, int status,
int flag)
{
ddi_dma_impl_t *hdlp = (ddi_dma_impl_t *)handle;
struct i_ddi_fmhdl *fmhdl = DEVI(hdlp->dmai_rdip)->devi_fmhdl;
hdlp->dmai_error.err_ena = ena;
hdlp->dmai_error.err_status = status;
hdlp->dmai_error.err_expected = flag;
atomic_add_64(&fmhdl->fh_kstat.fek_dma_err.value.ui64, 1);
}
ddi_fmcompare_t
i_ddi_fm_acc_err_cf_get(ddi_acc_handle_t handle)
{
ddi_acc_impl_t *i_hdlp = (ddi_acc_impl_t *)handle;
return (i_hdlp->ahi_err->err_cf);
}
ddi_fmcompare_t
i_ddi_fm_dma_err_cf_get(ddi_dma_handle_t handle)
{
ddi_dma_impl_t *hdlp = (ddi_dma_impl_t *)handle;
return (hdlp->dmai_error.err_cf);
}