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