Home | History | Annotate | Download | only in os
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 /*
     27  * Fault Management for Device Drivers
     28  *
     29  * Device drivers wishing to participate in fault management may do so by
     30  * first initializing their fault management state and capabilties via
     31  * ddi_fm_init(). If the system supports the requested FM capabilities,
     32  * the IO framework will intialize FM state and return a bit mask of the
     33  * requested capabilities.
     34  *
     35  * If the system does not support the requested FM capabilities,
     36  * the device driver must behave in accordance with the programming semantics
     37  * defined below for the capabilities returned from ddi_fm_init().
     38  * ddi_fm_init() must be called at attach(9E) time and ddi_fm_fini() must be
     39  * called from detach(9E) to perform FM clean-up.
     40  *
     41  * Driver Fault Management Capabilities
     42  *
     43  * DDI_FM_NOT_CAPABLE
     44  *
     45  *	This is the default fault management capability for drivers.  Drivers
     46  *	that implement no fault management capabilites or do not participate
     47  *	in fault management activities have their FM capability bitmask set
     48  *	to 0.
     49  *
     50  * DDI_FM_EREPORT_CAPABLE
     51  *
     52  *	When this capability bit is set, drivers are expected to generate error
     53  *	report events via ddi_ereport_post() for the associated faults
     54  *	that are diagnosed by the IO fault manager DE.  ddi_ereport_post()
     55  *	may be called in any context subject to the constraints specified
     56  *	by the interrupt iblock cookie	returned during initialization.
     57  *
     58  *	Error reports resulting from hardware component specific and common IO
     59  *	fault and driver defects must be accompanied by an Eversholt fault
     60  *	tree (.eft) by the Solaris fault manager (fmd(1M)) for
     61  *	diagnosis.
     62  *
     63  * DDI_FM_ERRCB_CAPABLE
     64  *
     65  *	Device drivers are expected to implement and register an error
     66  *	handler callback function.  ddi_fm_handler_register() and
     67  *	ddi_fm_handler_unregister() must be
     68  *	called in passive kernel context, typically during an attach(9E)
     69  *	or detach(9E) operation.  When called by the FM IO framework,
     70  *	the callback function should check for error conditions for the
     71  *	hardware and software under its control.  All detected errors
     72  *	should have ereport events generated for them.
     73  *
     74  *	Upon completion of the error handler callback, the driver should
     75  *	return one of the following values:
     76  *
     77  *	#define DDI_FM_OK - no error was detected
     78  *	#define DDI_FM_FATAL - a fatal error was detected
     79  *	#define DDI_FM_NONFATAL - a non-fatal error was detected
     80  *	#define DDI_FM_UNKNOWN - the error status is unknown
     81  *
     82  *	To insure single threaded access to error handling callbacks,
     83  *	the device driver may use i_ddi_fm_handler_enter() and
     84  *	i_ddi_fm_handler_exit() when entering and exiting the callback.
     85  *
     86  * DDI_FM_ACCCHK_CAPABLE/DDI_FM_DMACHK_CAPABLE
     87  *
     88  *	Device drivers are expected to set-up access and DMA handles
     89  *	with FM-specific attributes designed to allow nexus parent
     90  *	drivers to flag any errors seen during subsequent IO transactions.
     91  *	Drivers must set the devacc_attr_acc_flag member of their
     92  *	ddi_device_acc_attr_t structures to DDI_FLAGERR_ACC or DDI_CAUTIOUS_ACC.
     93  *	For DMA transactions, driver must set the dma_attr_flags of
     94  *	their ddi_dma_attr_t structures to DDI_DMA_FLAGERR.
     95  *
     96  *	Upon completion of an IO transaction, device drivers are expected
     97  *	to check the status of host-side hardware access and device-side
     98  *	dma completions by calling ddi_acc_err_check() or ddi_dma_err_check()
     99  *	respectively. If the handle is associated with an error detected by
    100  *	the nexus parent or FM IO framework, ddi_fm_error_t data (status, ena
    101  *	and error expectation) is returned.  If status of DDI_FM_NONFATAL or
    102  *	DDI_FM_FATAL is returned, the ena is valid and the expectation flag
    103  *	will be set to 1 if the error was unexpected (i.e. not the result
    104  *	of a peek or poke type operation).
    105  *
    106  *	ddi_acc_err_check() and ddi_dma_err_check() may be called in any
    107  *	context	subject to the constraints specified by the interrupt
    108  *	iblock cookie returned during initialization.
    109  *
    110  *	Device drivers should generate an access (DDI_FM_IO_ACC) or dma
    111  *	(DDI_FM_IO_DMA) data path error report if DDI_FM_NONFATAL or
    112  *	DDI_FM_FATAL is returned.
    113  *
    114  */
    115 
    116 #include <sys/types.h>
    117 #include <sys/sunddi.h>
    118 #include <sys/sunndi.h>
    119 #include <sys/kmem.h>
    120 #include <sys/nvpair.h>
    121 #include <sys/fm/protocol.h>
    122 #include <sys/ndifm.h>
    123 #include <sys/ddifm.h>
    124 #include <sys/ddi_impldefs.h>
    125 #include <sys/ddi_isa.h>
    126 #include <sys/spl.h>
    127 #include <sys/varargs.h>
    128 #include <sys/systm.h>
    129 #include <sys/disp.h>
    130 #include <sys/atomic.h>
    131 #include <sys/errorq_impl.h>
    132 #include <sys/kobj.h>
    133 #include <sys/fm/util.h>
    134 #include <sys/fm/io/ddi.h>
    135 
    136 #define	ERPT_CLASS_SZ	sizeof (DDI_IO_CLASS) + sizeof (FM_EREPORT_CLASS) + \
    137 			    DDI_MAX_ERPT_CLASS + 2
    138 /* Globals */
    139 int default_dmacache_sz = DEFAULT_DMACACHE_SZ;
    140 int default_acccache_sz = DEFAULT_ACCCACHE_SZ;
    141 int ddi_system_fmcap = 0;
    142 
    143 static struct i_ddi_fmkstat ddifm_kstat_template = {
    144 	{"erpt_dropped", KSTAT_DATA_UINT64 },
    145 	{"fm_cache_miss", KSTAT_DATA_UINT64 },
    146 	{"fm_cache_full", KSTAT_DATA_UINT64 },
    147 	{"acc_err", KSTAT_DATA_UINT64 },
    148 	{"dma_err", KSTAT_DATA_UINT64 }
    149 };
    150 
    151 /*
    152  * Update the service state following the detection of an
    153  * error.
    154  */
    155 void
    156 ddi_fm_service_impact(dev_info_t *dip, int svc_impact)
    157 {
    158 	uint64_t ena;
    159 	char buf[FM_MAX_CLASS];
    160 
    161 	ena = fm_ena_generate(0, FM_ENA_FMT1);
    162 	mutex_enter(&(DEVI(dip)->devi_lock));
    163 	if (!DEVI_IS_DEVICE_OFFLINE(dip)) {
    164 		switch (svc_impact) {
    165 		case DDI_SERVICE_LOST:
    166 			DEVI_SET_DEVICE_DOWN(dip);
    167 			(void) snprintf(buf, FM_MAX_CLASS, "%s.%s",
    168 			    DDI_FM_SERVICE_IMPACT, DDI_FM_SERVICE_LOST);
    169 			ddi_fm_ereport_post(dip, buf, ena, DDI_NOSLEEP,
    170 			    FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0,
    171 			    NULL);
    172 			break;
    173 		case DDI_SERVICE_DEGRADED:
    174 			DEVI_SET_DEVICE_DEGRADED(dip);
    175 			if (DEVI_IS_DEVICE_DEGRADED(dip)) {
    176 				(void) snprintf(buf, FM_MAX_CLASS, "%s.%s",
    177 				    DDI_FM_SERVICE_IMPACT,
    178 				    DDI_FM_SERVICE_DEGRADED);
    179 				ddi_fm_ereport_post(dip, buf, ena, DDI_NOSLEEP,
    180 				    FM_VERSION, DATA_TYPE_UINT8,
    181 				    FM_EREPORT_VERS0, NULL);
    182 			} else if (DEVI_IS_DEVICE_DOWN(dip)) {
    183 				(void) snprintf(buf, FM_MAX_CLASS, "%s.%s",
    184 				    DDI_FM_SERVICE_IMPACT,
    185 				    DDI_FM_SERVICE_LOST);
    186 				ddi_fm_ereport_post(dip, buf, ena, DDI_NOSLEEP,
    187 				    FM_VERSION, DATA_TYPE_UINT8,
    188 				    FM_EREPORT_VERS0, NULL);
    189 			}
    190 			break;
    191 		case DDI_SERVICE_RESTORED:
    192 			DEVI_SET_DEVICE_UP(dip);
    193 			(void) snprintf(buf, FM_MAX_CLASS, "%s.%s",
    194 			    DDI_FM_SERVICE_IMPACT, DDI_FM_SERVICE_RESTORED);
    195 			ddi_fm_ereport_post(dip, buf, ena, DDI_NOSLEEP,
    196 			    FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0,
    197 			    NULL);
    198 			break;
    199 		case DDI_SERVICE_UNAFFECTED:
    200 			(void) snprintf(buf, FM_MAX_CLASS, "%s.%s",
    201 			    DDI_FM_SERVICE_IMPACT, DDI_FM_SERVICE_UNAFFECTED);
    202 			ddi_fm_ereport_post(dip, buf, ena, DDI_NOSLEEP,
    203 			    FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0,
    204 			    NULL);
    205 			break;
    206 		default:
    207 			break;
    208 		}
    209 	}
    210 	mutex_exit(&(DEVI(dip)->devi_lock));
    211 }
    212 
    213 void
    214 i_ddi_drv_ereport_post(dev_info_t *dip, const char *error_class,
    215     nvlist_t *errp, int sflag)
    216 {
    217 	int i;
    218 	int depth;
    219 	char classp[DDI_DVR_MAX_CLASS];
    220 	caddr_t stkp;
    221 	char *buf;
    222 	char **stkpp;
    223 	char *sym;
    224 	pc_t stack[DDI_FM_STKDEPTH];
    225 	ulong_t off;
    226 	dev_info_t *root_dip = ddi_root_node();
    227 
    228 	if (!DDI_FM_EREPORT_CAP(ddi_fm_capable(root_dip)))
    229 		return;
    230 
    231 	(void) snprintf(classp, DDI_DVR_MAX_CLASS, "%s%s", DVR_ERPT,
    232 	    error_class);
    233 
    234 	if (sflag == DDI_SLEEP) {
    235 		depth = getpcstack(stack, DDI_FM_STKDEPTH);
    236 
    237 		/* Allocate array of char * for nvlist payload */
    238 		stkpp = (char **)kmem_alloc(depth * sizeof (char *), KM_SLEEP);
    239 
    240 		/*
    241 		 * Allocate temporary 64-bit aligned buffer for stack
    242 		 * symbol strings
    243 		 */
    244 		buf = kmem_alloc(depth * DDI_FM_SYM_SZ, KM_SLEEP);
    245 
    246 		stkp = buf;
    247 		for (i = 0; i < depth; ++i) {
    248 			sym = kobj_getsymname(stack[i], &off);
    249 			(void) snprintf(stkp, DDI_FM_SYM_SZ,
    250 			    "\t%s+%lx\n", sym ? sym : "?", off);
    251 			stkpp[i] = stkp;
    252 			stkp += DDI_FM_SYM_SZ;
    253 		}
    254 
    255 		if (errp)
    256 			ddi_fm_ereport_post(root_dip,
    257 			    classp, fm_ena_generate(0, FM_ENA_FMT1), sflag,
    258 			    FM_VERSION, DATA_TYPE_UINT8, 0,
    259 			    DVR_NAME, DATA_TYPE_STRING, ddi_driver_name(dip),
    260 			    DVR_STACK_DEPTH, DATA_TYPE_UINT32, depth,
    261 			    DVR_STACK, DATA_TYPE_STRING_ARRAY, depth, stkpp,
    262 			    DVR_ERR_SPECIFIC, DATA_TYPE_NVLIST, errp, NULL);
    263 		else
    264 			ddi_fm_ereport_post(root_dip,
    265 			    classp, fm_ena_generate(0, FM_ENA_FMT1), sflag,
    266 			    FM_VERSION, DATA_TYPE_UINT8, 0,
    267 			    DVR_NAME, DATA_TYPE_STRING, ddi_driver_name(dip),
    268 			    DVR_STACK_DEPTH, DATA_TYPE_UINT32, depth,
    269 			    DVR_STACK, DATA_TYPE_STRING_ARRAY, depth, stkpp,
    270 			    NULL);
    271 
    272 		kmem_free(stkpp, depth * sizeof (char *));
    273 		kmem_free(buf, depth * DDI_FM_SYM_SZ);
    274 
    275 	} else {
    276 		if (errp)
    277 			ddi_fm_ereport_post(root_dip,
    278 			    classp, fm_ena_generate(0, FM_ENA_FMT1), sflag,
    279 			    FM_VERSION, DATA_TYPE_UINT8, 0,
    280 			    DVR_NAME, DATA_TYPE_STRING, ddi_driver_name(dip),
    281 			    DVR_ERR_SPECIFIC, DATA_TYPE_NVLIST, errp, NULL);
    282 		else
    283 			ddi_fm_ereport_post(root_dip,
    284 			    classp, fm_ena_generate(0, FM_ENA_FMT1), sflag,
    285 			    FM_VERSION, DATA_TYPE_UINT8, 0,
    286 			    DVR_NAME, DATA_TYPE_STRING, ddi_driver_name(dip),
    287 			    NULL);
    288 	}
    289 }
    290 
    291 /*
    292  * fm_dev_ereport_postv: Common consolidation private interface to
    293  * post a device tree oriented dev_scheme ereport. The device tree is
    294  * composed of the following entities: devinfo nodes, minor nodes, and
    295  * pathinfo nodes. All entities are associated with some devinfo node,
    296  * either directly or indirectly. The intended devinfo node association
    297  * for the ereport is communicated by the 'dip' argument. A minor node,
    298  * an entity below 'dip', is represented by a non-null 'minor_name'
    299  * argument. An application specific caller, like scsi_fm_ereport_post,
    300  * can override the devinfo path with a pathinfo path via a non-null
    301  * 'devpath' argument - in this case 'dip' is the MPXIO client node and
    302  * devpath should be the path through the pHCI devinfo node to the
    303  * pathinfo node.
    304  *
    305  * This interface also allows the caller to decide if the error being
    306  * reported is know to be associated with a specific device identity
    307  * via the 'devid' argument. The caller needs to control wether the
    308  * devid appears as an authority in the FMRI because for some types of
    309  * errors, like transport errors, the identity of the device on the
    310  * other end of the transport is not guaranteed to be the current
    311  * identity of the dip. For transport errors the caller should specify
    312  * a NULL devid, even when there is a valid devid associated with the dip.
    313  *
    314  * The ddi_fm_ereport_post() implementation calls this interface with
    315  * just a dip: devpath, minor_name, and devid are all NULL. The
    316  * scsi_fm_ereport_post() implementation may call this interface with
    317  * non-null devpath, minor_name, and devid arguments depending on
    318  * wether MPXIO is enabled, and wether a transport or non-transport
    319  * error is being posted.
    320  */
    321 void
    322 fm_dev_ereport_postv(dev_info_t *dip, dev_info_t *eqdip,
    323     const char *devpath, const char *minor_name, const char *devid,
    324     const char *error_class, uint64_t ena, int sflag, va_list ap)
    325 {
    326 	nv_alloc_t		*nva = NULL;
    327 	struct i_ddi_fmhdl	*fmhdl = NULL;
    328 	errorq_elem_t		*eqep;
    329 	nvlist_t		*ereport = NULL;
    330 	nvlist_t		*detector = NULL;
    331 	char			*name;
    332 	data_type_t		type;
    333 	uint8_t			version;
    334 	char			class[ERPT_CLASS_SZ];
    335 	char			path[MAXPATHLEN];
    336 
    337 	ASSERT(dip && eqdip && error_class);
    338 
    339 	/*
    340 	 * This interface should be called with a fm_capable eqdip. The
    341 	 * ddi_fm_ereport_post* interfaces call with eqdip == dip,
    342 	 * ndi_fm_ereport_post* interfaces call with eqdip == ddi_parent(dip).
    343 	 */
    344 	if (!DDI_FM_EREPORT_CAP(ddi_fm_capable(eqdip)))
    345 		goto err;
    346 
    347 	/* get ereport nvlist handle */
    348 	if ((sflag == DDI_SLEEP) && !panicstr) {
    349 		/*
    350 		 * Driver defect - should not call with DDI_SLEEP while in
    351 		 * interrupt context.
    352 		 */
    353 		if (servicing_interrupt()) {
    354 			i_ddi_drv_ereport_post(dip, DVR_ECONTEXT, NULL, sflag);
    355 			goto err;
    356 		}
    357 
    358 		/* Use normal interfaces to allocate memory. */
    359 		if ((ereport = fm_nvlist_create(NULL)) == NULL)
    360 			goto err;
    361 		ASSERT(nva == NULL);
    362 	} else {
    363 		/* Use errorq interfaces to avoid memory allocation. */
    364 		fmhdl = DEVI(eqdip)->devi_fmhdl;
    365 		ASSERT(fmhdl);
    366 		eqep = errorq_reserve(fmhdl->fh_errorq);
    367 		if (eqep == NULL)
    368 			goto err;
    369 
    370 		ereport = errorq_elem_nvl(fmhdl->fh_errorq, eqep);
    371 		nva = errorq_elem_nva(fmhdl->fh_errorq, eqep);
    372 		ASSERT(nva);
    373 	}
    374 	ASSERT(ereport);
    375 
    376 	/*
    377 	 * Form parts of an ereport:
    378 	 *	A: version
    379 	 * 	B: error_class
    380 	 *	C: ena
    381 	 *	D: detector	(path and optional devid authority)
    382 	 *	E: payload
    383 	 *
    384 	 * A: ereport version: first payload tuple must be the version.
    385 	 */
    386 	name = va_arg(ap, char *);
    387 	type = va_arg(ap, data_type_t);
    388 	version = va_arg(ap, uint_t);
    389 	if ((strcmp(name, FM_VERSION) != 0) || (type != DATA_TYPE_UINT8)) {
    390 		i_ddi_drv_ereport_post(dip, DVR_EVER, NULL, sflag);
    391 		goto err;
    392 	}
    393 
    394 	/* B: ereport error_class: add "io." prefix to class. */
    395 	(void) snprintf(class, ERPT_CLASS_SZ, "%s.%s",
    396 	    DDI_IO_CLASS, error_class);
    397 
    398 	/* C: ereport ena: if not passed in, generate new ena. */
    399 	if (ena == 0)
    400 		ena = fm_ena_generate(0, FM_ENA_FMT1);
    401 
    402 	/* D: detector: form dev scheme fmri with path and devid. */
    403 	if (devpath) {
    404 		(void) strlcpy(path, devpath, sizeof (path));
    405 	} else {
    406 		/* derive devpath from dip */
    407 		if (dip == ddi_root_node())
    408 			(void) strcpy(path, "/");
    409 		else
    410 			(void) ddi_pathname(dip, path);
    411 	}
    412 	if (minor_name) {
    413 		(void) strlcat(path, ":", sizeof (path));
    414 		(void) strlcat(path, minor_name, sizeof (path));
    415 	}
    416 	detector = fm_nvlist_create(nva);
    417 	fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL, path, devid);
    418 
    419 	/* Pull parts of ereport together into ereport. */
    420 	fm_ereport_set(ereport, version, class, ena, detector, NULL);
    421 
    422 	/* Add the payload to ereport. */
    423 	name = va_arg(ap, char *);
    424 	(void) i_fm_payload_set(ereport, name, ap);
    425 
    426 	/* Post the ereport. */
    427 	if (nva)
    428 		errorq_commit(fmhdl->fh_errorq, eqep, ERRORQ_ASYNC);
    429 	else
    430 		fm_ereport_post(ereport, EVCH_SLEEP);
    431 	goto out;
    432 
    433 	/* Count errors as drops. */
    434 err:	if (fmhdl)
    435 		atomic_add_64(&fmhdl->fh_kstat.fek_erpt_dropped.value.ui64, 1);
    436 
    437 	/* Free up nvlists if normal interfaces were used to allocate memory */
    438 out:	if (ereport && (nva == NULL))
    439 		fm_nvlist_destroy(ereport, FM_NVA_FREE);
    440 	if (detector && (nva == NULL))
    441 		fm_nvlist_destroy(detector, FM_NVA_FREE);
    442 }
    443 
    444 /*
    445  * Generate an error report for consumption by the Solaris Fault Manager,
    446  * fmd(1M).  Valid ereport classes are defined in /usr/include/sys/fm/io.
    447  *
    448  * The ENA should be set if this error is a result of an error status
    449  * returned from ddi_dma_err_check() or ddi_acc_err_check().  Otherwise,
    450  * an ENA value of 0 is appropriate.
    451  *
    452  * If sflag == DDI_NOSLEEP, ddi_fm_ereport_post () may be called
    453  * from user, kernel, interrupt or high-interrupt context.  Otherwise,
    454  * ddi_fm_ereport_post() must be called from user or kernel context.
    455  *
    456  * The ndi_interfaces are provided for use by nexus drivers to post
    457  * ereports about children who may not themselves be fm_capable.
    458  *
    459  * All interfaces end up in the common fm_dev_ereport_postv code above.
    460  */
    461 void
    462 ddi_fm_ereport_post(dev_info_t *dip,
    463     const char *error_class, uint64_t ena, int sflag, ...)
    464 {
    465 	va_list ap;
    466 
    467 	ASSERT(dip && error_class);
    468 	va_start(ap, sflag);
    469 	fm_dev_ereport_postv(dip, dip, NULL, NULL, NULL,
    470 	    error_class, ena, sflag, ap);
    471 	va_end(ap);
    472 }
    473 
    474 void
    475 ndi_fm_ereport_post(dev_info_t *dip,
    476     const char *error_class, uint64_t ena, int sflag, ...)
    477 {
    478 	va_list ap;
    479 
    480 	ASSERT(dip && error_class && (sflag == DDI_SLEEP));
    481 	va_start(ap, sflag);
    482 	fm_dev_ereport_postv(dip, ddi_get_parent(dip), NULL, NULL, NULL,
    483 	    error_class, ena, sflag, ap);
    484 	va_end(ap);
    485 }
    486 
    487 /*
    488  * Driver error handling entry.  Prevents multiple simultaneous calls into
    489  * driver error handling callback.
    490  *
    491  * May be called from a context consistent with the iblock_cookie returned
    492  * in ddi_fm_init().
    493  */
    494 void
    495 i_ddi_fm_handler_enter(dev_info_t *dip)
    496 {
    497 	struct i_ddi_fmhdl *hdl = DEVI(dip)->devi_fmhdl;
    498 
    499 	mutex_enter(&hdl->fh_lock);
    500 	hdl->fh_lock_owner = curthread;
    501 }
    502 
    503 /*
    504  * Driver error handling exit.
    505  *
    506  * May be called from a context consistent with the iblock_cookie returned
    507  * in ddi_fm_init().
    508  */
    509 void
    510 i_ddi_fm_handler_exit(dev_info_t *dip)
    511 {
    512 	struct i_ddi_fmhdl *hdl = DEVI(dip)->devi_fmhdl;
    513 
    514 	hdl->fh_lock_owner = NULL;
    515 	mutex_exit(&hdl->fh_lock);
    516 }
    517 
    518 boolean_t
    519 i_ddi_fm_handler_owned(dev_info_t *dip)
    520 {
    521 	struct i_ddi_fmhdl *hdl = DEVI(dip)->devi_fmhdl;
    522 
    523 	return (hdl->fh_lock_owner == curthread);
    524 }
    525 
    526 /*
    527  * Register a fault manager error handler for this device instance
    528  *
    529  * This function must be called from a driver's attach(9E) routine.
    530  */
    531 void
    532 ddi_fm_handler_register(dev_info_t *dip, ddi_err_func_t handler,
    533     void *impl_data)
    534 {
    535 	dev_info_t *pdip;
    536 	struct i_ddi_fmhdl *pfmhdl;
    537 	struct i_ddi_errhdl *new_eh;
    538 	struct i_ddi_fmtgt *tgt;
    539 
    540 	/*
    541 	 * Check for proper calling context.
    542 	 * The DDI configuration framework does not support
    543 	 * DR states to allow checking for proper invocation
    544 	 * from a DDI_ATTACH or DDI_RESUME.  This limits context checking
    545 	 * to interrupt only.
    546 	 */
    547 	if (servicing_interrupt()) {
    548 		i_ddi_drv_ereport_post(dip, DVR_ECONTEXT, NULL, DDI_NOSLEEP);
    549 		return;
    550 	}
    551 
    552 	if (dip == ddi_root_node())
    553 		pdip = dip;
    554 	else
    555 		pdip = (dev_info_t *)DEVI(dip)->devi_parent;
    556 
    557 	ASSERT(pdip);
    558 
    559 	if (!(DDI_FM_ERRCB_CAP(ddi_fm_capable(dip)) &&
    560 	    DDI_FM_ERRCB_CAP(ddi_fm_capable(pdip)))) {
    561 		i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_SLEEP);
    562 		return;
    563 	}
    564 
    565 	new_eh = kmem_zalloc(sizeof (struct i_ddi_errhdl), KM_SLEEP);
    566 	new_eh->eh_func = handler;
    567 	new_eh->eh_impl = impl_data;
    568 
    569 	/* Add dip to parent's target list of registered error handlers */
    570 	tgt = kmem_alloc(sizeof (struct i_ddi_fmtgt), KM_SLEEP);
    571 	tgt->ft_dip = dip;
    572 	tgt->ft_errhdl = new_eh;
    573 
    574 	i_ddi_fm_handler_enter(pdip);
    575 	pfmhdl = DEVI(pdip)->devi_fmhdl;
    576 	ASSERT(pfmhdl);
    577 	tgt->ft_next = pfmhdl->fh_tgts;
    578 	pfmhdl->fh_tgts = tgt;
    579 	i_ddi_fm_handler_exit(pdip);
    580 }
    581 
    582 /*
    583  * Unregister a fault manager error handler for this device instance
    584  *
    585  * This function must be called from a drivers attach(9E) or detach(9E)
    586  * routine.
    587  */
    588 void
    589 ddi_fm_handler_unregister(dev_info_t *dip)
    590 {
    591 	dev_info_t *pdip;
    592 	struct i_ddi_fmhdl *pfmhdl;
    593 	struct i_ddi_fmtgt *tgt, **ptgt;
    594 
    595 	/*
    596 	 * Check for proper calling context.
    597 	 * The DDI configuration framework does not support
    598 	 * DR states to allow checking for proper invocation
    599 	 * from a DDI_DETACH or DDI_SUSPEND.  This limits context checking
    600 	 * to interrupt only.
    601 	 */
    602 	if (servicing_interrupt()) {
    603 		i_ddi_drv_ereport_post(dip, DVR_ECONTEXT, NULL, DDI_NOSLEEP);
    604 		return;
    605 	}
    606 
    607 	if (dip == ddi_root_node())
    608 		pdip = dip;
    609 	else
    610 		pdip = (dev_info_t *)DEVI(dip)->devi_parent;
    611 
    612 	ASSERT(pdip);
    613 
    614 	if (!(DDI_FM_ERRCB_CAP(ddi_fm_capable(dip)) &&
    615 	    DDI_FM_ERRCB_CAP(ddi_fm_capable(pdip)))) {
    616 		i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_SLEEP);
    617 		return;
    618 	}
    619 
    620 	i_ddi_fm_handler_enter(pdip);
    621 	pfmhdl = DEVI(pdip)->devi_fmhdl;
    622 	ASSERT(pfmhdl);
    623 	ptgt = &pfmhdl->fh_tgts;
    624 	for (tgt = pfmhdl->fh_tgts; tgt != NULL; tgt = tgt->ft_next) {
    625 		if (dip == tgt->ft_dip) {
    626 			*ptgt = tgt->ft_next;
    627 			kmem_free(tgt->ft_errhdl, sizeof (struct i_ddi_errhdl));
    628 			kmem_free(tgt, sizeof (struct i_ddi_fmtgt));
    629 			break;
    630 		}
    631 		ptgt = &tgt->ft_next;
    632 	}
    633 	i_ddi_fm_handler_exit(pdip);
    634 
    635 
    636 }
    637 
    638 /*
    639  * Initialize Fault Management capabilities for this device instance (dip).
    640  * When called with the following capabilities, data structures neccessary
    641  * for fault management activities are allocated and initialized.
    642  *
    643  *	DDI_FM_EREPORT_CAPABLE - initialize ereport errorq and ereport
    644  *				capable driver property.
    645  *
    646  *	DDI_FM_ERRCB_CAPABLE - check with parent for ability to register
    647  *				an error handler.
    648  *
    649  *	DDI_FM_ACCCHK_CAPABLE - initialize access handle cache and acc-chk
    650  *				driver property
    651  *
    652  *	DDI_FM_DMACHK_CAPABLE - initialize dma handle cache and dma-chk
    653  *				driver property
    654  *
    655  * A driver's FM capability level may not exceed that of its parent or
    656  * system-wide FM capability.  The available capability level for this
    657  * device instance is returned in *fmcap.
    658  *
    659  * This function must be called from a driver's attach(9E) entry point.
    660  */
    661 void
    662 ddi_fm_init(dev_info_t *dip, int *fmcap, ddi_iblock_cookie_t *ibcp)
    663 {
    664 	struct dev_info *devi = DEVI(dip);
    665 	struct i_ddi_fmhdl *fmhdl;
    666 	ddi_iblock_cookie_t ibc;
    667 	int pcap, newcap = DDI_FM_NOT_CAPABLE;
    668 
    669 	if (!DEVI_IS_ATTACHING(dip)) {
    670 		i_ddi_drv_ereport_post(dip, DVR_ECONTEXT, NULL, DDI_NOSLEEP);
    671 		*fmcap = DDI_FM_NOT_CAPABLE;
    672 		return;
    673 	}
    674 
    675 	if (DDI_FM_DEFAULT_CAP(*fmcap))
    676 		return;
    677 
    678 	/*
    679 	 * Check parent for supported FM level
    680 	 * and correct error handling PIL
    681 	 */
    682 	if (dip != ddi_root_node()) {
    683 
    684 		/*
    685 		 * Initialize the default ibc.  The parent may change it
    686 		 * depending upon its capabilities.
    687 		 */
    688 		ibc = (ddi_iblock_cookie_t)ipltospl(FM_ERR_PIL);
    689 
    690 		pcap = i_ndi_busop_fm_init(dip, *fmcap, &ibc);
    691 	} else {
    692 		pcap = *fmcap;
    693 		ibc = *ibcp;
    694 	}
    695 
    696 	/* Initialize the per-device instance FM handle */
    697 	fmhdl = kmem_zalloc(sizeof (struct i_ddi_fmhdl), KM_SLEEP);
    698 
    699 	if ((fmhdl->fh_ksp = kstat_create((char *)ddi_driver_name(dip),
    700 	    ddi_get_instance(dip), "fm", "misc",
    701 	    KSTAT_TYPE_NAMED, sizeof (struct i_ddi_fmkstat) /
    702 	    sizeof (kstat_named_t), KSTAT_FLAG_VIRTUAL)) == NULL) {
    703 		mutex_destroy(&fmhdl->fh_lock);
    704 		kmem_free(fmhdl, sizeof (struct i_ddi_fmhdl));
    705 		*fmcap = DDI_FM_NOT_CAPABLE;
    706 		return;
    707 	}
    708 
    709 	bcopy(&ddifm_kstat_template, &fmhdl->fh_kstat,
    710 	    sizeof (struct i_ddi_fmkstat));
    711 	fmhdl->fh_ksp->ks_data = &fmhdl->fh_kstat;
    712 	fmhdl->fh_ksp->ks_private = fmhdl;
    713 	kstat_install(fmhdl->fh_ksp);
    714 
    715 	fmhdl->fh_dma_cache = NULL;
    716 	fmhdl->fh_acc_cache = NULL;
    717 	fmhdl->fh_tgts = NULL;
    718 	fmhdl->fh_dip = dip;
    719 	fmhdl->fh_ibc = ibc;
    720 	mutex_init(&fmhdl->fh_lock, NULL, MUTEX_DRIVER, fmhdl->fh_ibc);
    721 	devi->devi_fmhdl = fmhdl;
    722 
    723 	/*
    724 	 * Initialize support for ereport generation
    725 	 */
    726 	if (DDI_FM_EREPORT_CAP(*fmcap) && DDI_FM_EREPORT_CAP(pcap)) {
    727 		fmhdl->fh_errorq = ereport_errorq;
    728 		if (ddi_getprop(DDI_DEV_T_NONE, dip, DDI_PROP_DONTPASS,
    729 		    "fm-ereport-capable", 0) == 0)
    730 			(void) ddi_prop_create(DDI_DEV_T_NONE, dip,
    731 			    DDI_PROP_CANSLEEP, "fm-ereport-capable", NULL, 0);
    732 
    733 		newcap |= DDI_FM_EREPORT_CAPABLE;
    734 	}
    735 
    736 	/*
    737 	 * Need cooperation of the parent for error handling
    738 	 */
    739 
    740 	if (DDI_FM_ERRCB_CAP(*fmcap) && DDI_FM_ERRCB_CAP(pcap)) {
    741 		if (ddi_getprop(DDI_DEV_T_NONE, dip, DDI_PROP_DONTPASS,
    742 		    "fm-errcb-capable", 0) == 0)
    743 			(void) ddi_prop_create(DDI_DEV_T_NONE, dip,
    744 			    DDI_PROP_CANSLEEP, "fm-errcb-capable", NULL, 0);
    745 
    746 		newcap |= DDI_FM_ERRCB_CAPABLE;
    747 	}
    748 
    749 	/*
    750 	 * Support for DMA and Access error handling
    751 	 */
    752 
    753 	if (DDI_FM_DMA_ERR_CAP(*fmcap) && DDI_FM_DMA_ERR_CAP(pcap)) {
    754 		i_ndi_fmc_create(&fmhdl->fh_dma_cache, 2, ibc);
    755 
    756 		/* Set-up dma chk capability prop */
    757 		if (ddi_getprop(DDI_DEV_T_NONE, dip, DDI_PROP_DONTPASS,
    758 		    "fm-dmachk-capable", 0) == 0)
    759 			(void) ddi_prop_create(DDI_DEV_T_NONE, dip,
    760 			    DDI_PROP_CANSLEEP, "fm-dmachk-capable", NULL, 0);
    761 
    762 		newcap |= DDI_FM_DMACHK_CAPABLE;
    763 	}
    764 
    765 	if (DDI_FM_ACC_ERR_CAP(*fmcap) && DDI_FM_ACC_ERR_CAP(pcap)) {
    766 		i_ndi_fmc_create(&fmhdl->fh_acc_cache, 2, ibc);
    767 		/* Set-up dma chk capability prop */
    768 		if (ddi_getprop(DDI_DEV_T_NONE, dip, DDI_PROP_DONTPASS,
    769 		    "fm-accchk-capable", 0) == 0)
    770 			(void) ddi_prop_create(DDI_DEV_T_NONE, dip,
    771 			    DDI_PROP_CANSLEEP, "fm-accchk-capable", NULL, 0);
    772 
    773 		newcap |= DDI_FM_ACCCHK_CAPABLE;
    774 	}
    775 
    776 	/*
    777 	 * Return the capability support available
    778 	 * to this driver instance
    779 	 */
    780 	fmhdl->fh_cap = newcap;
    781 	*fmcap = newcap;
    782 
    783 	if (ibcp != NULL)
    784 		*ibcp = ibc;
    785 }
    786 
    787 /*
    788  * Finalize Fault Management activities for this device instance.
    789  * Outstanding IO transaction must be completed prior to calling
    790  * this routine.  All previously allocated resources and error handler
    791  * registration are cleared and deallocated.
    792  *
    793  * This function must be called from a driver's detach(9E) entry point.
    794  */
    795 void
    796 ddi_fm_fini(dev_info_t *dip)
    797 {
    798 	struct i_ddi_fmhdl *fmhdl = DEVI(dip)->devi_fmhdl;
    799 
    800 	ASSERT(fmhdl);
    801 
    802 	if (!(DEVI_IS_DETACHING(dip) || DEVI_IS_ATTACHING(dip))) {
    803 		i_ddi_drv_ereport_post(dip, DVR_ECONTEXT, NULL, DDI_NOSLEEP);
    804 		return;
    805 	}
    806 
    807 	kstat_delete(fmhdl->fh_ksp);
    808 
    809 	if (DDI_FM_EREPORT_CAP(fmhdl->fh_cap)) {
    810 		(void) ddi_prop_remove(DDI_DEV_T_NONE, dip,
    811 		    "fm-ereport-capable");
    812 	}
    813 
    814 	if (dip != ddi_root_node()) {
    815 		if (DDI_FM_ERRCB_CAP(fmhdl->fh_cap)) {
    816 			ddi_fm_handler_unregister(dip);
    817 			(void) ddi_prop_remove(DDI_DEV_T_NONE, dip,
    818 			    "fm-errcb-capable");
    819 		}
    820 
    821 		if (DDI_FM_DMA_ERR_CAP(fmhdl->fh_cap) ||
    822 		    DDI_FM_ACC_ERR_CAP(fmhdl->fh_cap)) {
    823 			if (fmhdl->fh_dma_cache != NULL) {
    824 				i_ndi_fmc_destroy(fmhdl->fh_dma_cache);
    825 				(void) ddi_prop_remove(DDI_DEV_T_NONE, dip,
    826 				    "fm-dmachk-capable");
    827 			}
    828 			if (fmhdl->fh_acc_cache != NULL) {
    829 				i_ndi_fmc_destroy(fmhdl->fh_acc_cache);
    830 				(void) ddi_prop_remove(DDI_DEV_T_NONE, dip,
    831 				    "fm-accachk-capable");
    832 			}
    833 		}
    834 
    835 		i_ndi_busop_fm_fini(dip);
    836 	}
    837 
    838 	kmem_free(fmhdl, sizeof (struct i_ddi_fmhdl));
    839 	DEVI(dip)->devi_fmhdl = NULL;
    840 }
    841 
    842 /*
    843  * Return the fault management capability level for this device instance.
    844  *
    845  * This function may be called from user, kernel, or interrupt context.
    846  */
    847 int
    848 ddi_fm_capable(dev_info_t *dip)
    849 {
    850 	struct i_ddi_fmhdl *fmhdl = DEVI(dip)->devi_fmhdl;
    851 
    852 	if (fmhdl == NULL)
    853 		return (DDI_FM_NOT_CAPABLE);
    854 
    855 	return (fmhdl->fh_cap);
    856 }
    857 
    858 /*
    859  * Routines to set and get error information for/from an access or dma handle
    860  *
    861  * These routines may be called from user, kernel, and interrupt contexts.
    862  */
    863 void
    864 ddi_fm_acc_err_get(ddi_acc_handle_t handle, ddi_fm_error_t *de, int version)
    865 {
    866 	ndi_err_t *errp;
    867 
    868 	if (handle == NULL)
    869 		return;
    870 
    871 	if (version != DDI_FME_VER0 && version != DDI_FME_VER1) {
    872 		ddi_acc_hdl_t *hp = impl_acc_hdl_get(handle);
    873 
    874 		i_ddi_drv_ereport_post(hp->ah_dip, DVR_EVER, NULL, DDI_NOSLEEP);
    875 		cmn_err(CE_PANIC, "ddi_fm_acc_err_get: "
    876 		    "Invalid driver version\n");
    877 	}
    878 
    879 	errp = ((ddi_acc_impl_t *)handle)->ahi_err;
    880 	de->fme_status = errp->err_status;
    881 	de->fme_ena = errp->err_ena;
    882 	de->fme_flag = errp->err_expected;
    883 	de->fme_acc_handle = handle;
    884 }
    885 
    886 void
    887 ddi_fm_dma_err_get(ddi_dma_handle_t handle, ddi_fm_error_t *de, int version)
    888 {
    889 	ndi_err_t *errp;
    890 
    891 	if (handle == NULL)
    892 		return;
    893 
    894 	if (version != DDI_FME_VER0 && version != DDI_FME_VER1) {
    895 		i_ddi_drv_ereport_post(((ddi_dma_impl_t *)handle)->dmai_rdip,
    896 		    DVR_EVER, NULL, DDI_NOSLEEP);
    897 		cmn_err(CE_PANIC, "ddi_fm_dma_err_get: "
    898 		    "Invalid driver version\n");
    899 	}
    900 
    901 	errp = &((ddi_dma_impl_t *)handle)->dmai_error;
    902 
    903 	de->fme_status = errp->err_status;
    904 	de->fme_ena = errp->err_ena;
    905 	de->fme_flag = errp->err_expected;
    906 	de->fme_dma_handle = handle;
    907 }
    908 
    909 void
    910 ddi_fm_acc_err_clear(ddi_acc_handle_t handle, int version)
    911 {
    912 	ndi_err_t *errp;
    913 
    914 	if (handle == NULL)
    915 		return;
    916 
    917 	if (version != DDI_FME_VER0 && version != DDI_FME_VER1) {
    918 		ddi_acc_hdl_t *hp = impl_acc_hdl_get(handle);
    919 
    920 		i_ddi_drv_ereport_post(hp->ah_dip, DVR_EVER, NULL, DDI_NOSLEEP);
    921 		cmn_err(CE_PANIC, "ddi_fm_acc_err_clear: "
    922 		    "Invalid driver version\n");
    923 	}
    924 
    925 	errp = ((ddi_acc_impl_t *)handle)->ahi_err;
    926 	errp->err_status = DDI_FM_OK;
    927 	errp->err_ena = 0;
    928 	errp->err_expected = DDI_FM_ERR_UNEXPECTED;
    929 }
    930 
    931 void
    932 ddi_fm_dma_err_clear(ddi_dma_handle_t handle, int version)
    933 {
    934 	ndi_err_t *errp;
    935 
    936 	if (handle == NULL)
    937 		return;
    938 
    939 	if (version != DDI_FME_VER0 && version != DDI_FME_VER1) {
    940 		i_ddi_drv_ereport_post(((ddi_dma_impl_t *)handle)->dmai_rdip,
    941 		    DVR_EVER, NULL, DDI_NOSLEEP);
    942 		cmn_err(CE_PANIC, "ddi_fm_dma_err_clear: "
    943 		    "Invalid driver version\n");
    944 	}
    945 
    946 	errp = &((ddi_dma_impl_t *)handle)->dmai_error;
    947 
    948 	errp->err_status = DDI_FM_OK;
    949 	errp->err_ena = 0;
    950 	errp->err_expected = DDI_FM_ERR_UNEXPECTED;
    951 }
    952 
    953 void
    954 i_ddi_fm_acc_err_set(ddi_acc_handle_t handle, uint64_t ena, int status,
    955     int flag)
    956 {
    957 	ddi_acc_hdl_t *hdlp = impl_acc_hdl_get(handle);
    958 	ddi_acc_impl_t *i_hdlp = (ddi_acc_impl_t *)handle;
    959 	struct i_ddi_fmhdl *fmhdl = DEVI(hdlp->ah_dip)->devi_fmhdl;
    960 
    961 	i_hdlp->ahi_err->err_ena = ena;
    962 	i_hdlp->ahi_err->err_status = status;
    963 	i_hdlp->ahi_err->err_expected = flag;
    964 	atomic_add_64(&fmhdl->fh_kstat.fek_acc_err.value.ui64, 1);
    965 }
    966 
    967 void
    968 i_ddi_fm_dma_err_set(ddi_dma_handle_t handle, uint64_t ena, int status,
    969     int flag)
    970 {
    971 	ddi_dma_impl_t *hdlp = (ddi_dma_impl_t *)handle;
    972 	struct i_ddi_fmhdl *fmhdl = DEVI(hdlp->dmai_rdip)->devi_fmhdl;
    973 
    974 	hdlp->dmai_error.err_ena = ena;
    975 	hdlp->dmai_error.err_status = status;
    976 	hdlp->dmai_error.err_expected = flag;
    977 	atomic_add_64(&fmhdl->fh_kstat.fek_dma_err.value.ui64, 1);
    978 }
    979 
    980 ddi_fmcompare_t
    981 i_ddi_fm_acc_err_cf_get(ddi_acc_handle_t handle)
    982 {
    983 	ddi_acc_impl_t *i_hdlp = (ddi_acc_impl_t *)handle;
    984 
    985 	return (i_hdlp->ahi_err->err_cf);
    986 }
    987 
    988 ddi_fmcompare_t
    989 i_ddi_fm_dma_err_cf_get(ddi_dma_handle_t handle)
    990 {
    991 	ddi_dma_impl_t *hdlp = (ddi_dma_impl_t *)handle;
    992 
    993 	return (hdlp->dmai_error.err_cf);
    994 }
    995