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