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.
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.
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]
22 * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
26 * Fault Management for Device Drivers
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.
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.
40 * Driver Fault Management Capabilities
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
49 * DDI_FM_EREPORT_CAPABLE
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.
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(1M)) for
62 * DDI_FM_ERRCB_CAPABLE
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.
73 * Upon completion of the error handler callback, the driver should
74 * return one of the following values:
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
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.
85 * DDI_FM_ACCCHK_CAPABLE/DDI_FM_DMACHK_CAPABLE
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.
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).
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.
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.
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>
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>
135 #define ERPT_CLASS_SZ sizeof (DDI_IO_CLASS) + sizeof (FM_EREPORT_CLASS) + \
136 DDI_MAX_ERPT_CLASS + 2
138 int default_dmacache_sz
= DEFAULT_DMACACHE_SZ
;
139 int default_acccache_sz
= DEFAULT_ACCCACHE_SZ
;
140 int ddi_system_fmcap
= 0;
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
}
151 * Update the service state following the detection of an
155 ddi_fm_service_impact(dev_info_t
*dip
, int svc_impact
)
158 char buf
[FM_MAX_CLASS
];
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
,
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
);
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
,
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
,
209 mutex_exit(&(DEVI(dip
)->devi_lock
));
213 i_ddi_drv_ereport_post(dev_info_t
*dip
, const char *error_class
,
214 nvlist_t
*errp
, int sflag
)
218 char classp
[DDI_DVR_MAX_CLASS
];
223 pc_t stack
[DDI_FM_STKDEPTH
];
225 dev_info_t
*root_dip
= ddi_root_node();
227 if (!DDI_FM_EREPORT_CAP(ddi_fm_capable(root_dip
)))
230 (void) snprintf(classp
, DDI_DVR_MAX_CLASS
, "%s%s", DVR_ERPT
,
233 if (sflag
== DDI_SLEEP
) {
234 depth
= getpcstack(stack
, DDI_FM_STKDEPTH
);
236 /* Allocate array of char * for nvlist payload */
237 stkpp
= (char **)kmem_alloc(depth
* sizeof (char *), KM_SLEEP
);
240 * Allocate temporary 64-bit aligned buffer for stack
243 buf
= kmem_alloc(depth
* DDI_FM_SYM_SZ
, KM_SLEEP
);
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
);
251 stkp
+= DDI_FM_SYM_SZ
;
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
);
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
,
271 kmem_free(stkpp
, depth
* sizeof (char *));
272 kmem_free(buf
, depth
* DDI_FM_SYM_SZ
);
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
);
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
),
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
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.
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.
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.
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.
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
)
343 nv_alloc_t
*nva
= NULL
;
344 struct i_ddi_fmhdl
*fmhdl
= NULL
;
346 nvlist_t
*ereport
= NULL
;
347 nvlist_t
*detector
= NULL
;
351 char class[ERPT_CLASS_SZ
];
352 char path
[MAXPATHLEN
];
354 ASSERT(ap
!= NULL
); /* must supply at least ereport version */
355 ASSERT(dip
&& eqdip
&& error_class
);
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).
362 if (!DDI_FM_EREPORT_CAP(ddi_fm_capable(eqdip
)))
365 /* get ereport nvlist handle */
366 if ((sflag
== DDI_SLEEP
) && !panicstr
) {
368 * Driver defect - should not call with DDI_SLEEP while in
371 if (servicing_interrupt()) {
372 i_ddi_drv_ereport_post(dip
, DVR_ECONTEXT
, NULL
, sflag
);
376 /* Use normal interfaces to allocate memory. */
377 if ((ereport
= fm_nvlist_create(NULL
)) == NULL
)
381 /* Use errorq interfaces to avoid memory allocation. */
382 fmhdl
= DEVI(eqdip
)->devi_fmhdl
;
384 eqep
= errorq_reserve(fmhdl
->fh_errorq
);
388 ereport
= errorq_elem_nvl(fmhdl
->fh_errorq
, eqep
);
389 nva
= errorq_elem_nva(fmhdl
->fh_errorq
, eqep
);
395 * Form parts of an ereport:
399 * D: detector (path and optional devid authority)
402 * A: ereport version: first payload tuple must be the version.
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
);
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
);
416 /* C: ereport ena: if not passed in, generate new ena. */
418 ena
= fm_ena_generate(0, FM_ENA_FMT1
);
420 /* D: detector: form dev scheme fmri with path and devid. */
422 (void) strlcpy(path
, devpath
, sizeof (path
));
424 /* derive devpath from dip */
425 if (dip
== ddi_root_node())
426 (void) strcpy(path
, "/");
428 (void) ddi_pathname(dip
, path
);
431 (void) strlcat(path
, ":", sizeof (path
));
432 (void) strlcat(path
, minor_name
, sizeof (path
));
434 detector
= fm_nvlist_create(nva
);
435 fm_fmri_dev_set(detector
, FM_DEV_SCHEME_VERSION
, NULL
, path
,
438 /* Pull parts of ereport together into ereport. */
439 fm_ereport_set(ereport
, version
, class, ena
, detector
, NULL
);
441 /* Merge any preconstructed payload into the event. */
443 (void) nvlist_merge(ereport
, pl
, 0);
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
);
449 /* Post the ereport. */
451 errorq_commit(fmhdl
->fh_errorq
, eqep
, ERRORQ_ASYNC
);
453 fm_ereport_post(ereport
, EVCH_SLEEP
);
456 /* Count errors as drops. */
458 atomic_inc_64(&fmhdl
->fh_kstat
.fek_erpt_dropped
.value
.ui64
);
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
);
468 * Generate an error report for consumption by the Solaris Fault Manager,
469 * fmd(1M). Valid ereport classes are defined in /usr/include/sys/fm/io.
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.
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.
479 * The ndi_interfaces are provided for use by nexus drivers to post
480 * ereports about children who may not themselves be fm_capable.
482 * All interfaces end up in the common fm_dev_ereport_postv code above.
485 ddi_fm_ereport_post(dev_info_t
*dip
,
486 const char *error_class
, uint64_t ena
, int sflag
, ...)
490 ASSERT(dip
&& error_class
);
492 fm_dev_ereport_postv(dip
, dip
, NULL
, NULL
, NULL
, NULL
,
493 error_class
, ena
, sflag
, NULL
, ap
);
498 ndi_fm_ereport_post(dev_info_t
*dip
,
499 const char *error_class
, uint64_t ena
, int sflag
, ...)
503 ASSERT(dip
&& error_class
&& (sflag
== DDI_SLEEP
));
505 fm_dev_ereport_postv(dip
, ddi_get_parent(dip
), NULL
, NULL
, NULL
, NULL
,
506 error_class
, ena
, sflag
, NULL
, ap
);
511 * Driver error handling entry. Prevents multiple simultaneous calls into
512 * driver error handling callback.
514 * May be called from a context consistent with the iblock_cookie returned
518 i_ddi_fm_handler_enter(dev_info_t
*dip
)
520 struct i_ddi_fmhdl
*hdl
= DEVI(dip
)->devi_fmhdl
;
522 mutex_enter(&hdl
->fh_lock
);
523 hdl
->fh_lock_owner
= curthread
;
527 * Driver error handling exit.
529 * May be called from a context consistent with the iblock_cookie returned
533 i_ddi_fm_handler_exit(dev_info_t
*dip
)
535 struct i_ddi_fmhdl
*hdl
= DEVI(dip
)->devi_fmhdl
;
537 hdl
->fh_lock_owner
= NULL
;
538 mutex_exit(&hdl
->fh_lock
);
542 i_ddi_fm_handler_owned(dev_info_t
*dip
)
544 struct i_ddi_fmhdl
*hdl
= DEVI(dip
)->devi_fmhdl
;
546 return (hdl
->fh_lock_owner
== curthread
);
550 * Register a fault manager error handler for this device instance
552 * This function must be called from a driver's attach(9E) routine.
555 ddi_fm_handler_register(dev_info_t
*dip
, ddi_err_func_t handler
,
559 struct i_ddi_fmhdl
*pfmhdl
;
560 struct i_ddi_errhdl
*new_eh
;
561 struct i_ddi_fmtgt
*tgt
;
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
570 if (servicing_interrupt()) {
571 i_ddi_drv_ereport_post(dip
, DVR_ECONTEXT
, NULL
, DDI_NOSLEEP
);
575 if (dip
== ddi_root_node())
578 pdip
= (dev_info_t
*)DEVI(dip
)->devi_parent
;
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
);
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
;
592 /* Add dip to parent's target list of registered error handlers */
593 tgt
= kmem_alloc(sizeof (struct i_ddi_fmtgt
), KM_SLEEP
);
595 tgt
->ft_errhdl
= new_eh
;
597 i_ddi_fm_handler_enter(pdip
);
598 pfmhdl
= DEVI(pdip
)->devi_fmhdl
;
600 tgt
->ft_next
= pfmhdl
->fh_tgts
;
601 pfmhdl
->fh_tgts
= tgt
;
602 i_ddi_fm_handler_exit(pdip
);
606 * Unregister a fault manager error handler for this device instance
608 * This function must be called from a drivers attach(9E) or detach(9E)
612 ddi_fm_handler_unregister(dev_info_t
*dip
)
615 struct i_ddi_fmhdl
*pfmhdl
;
616 struct i_ddi_fmtgt
*tgt
, **ptgt
;
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
625 if (servicing_interrupt()) {
626 i_ddi_drv_ereport_post(dip
, DVR_ECONTEXT
, NULL
, DDI_NOSLEEP
);
630 if (dip
== ddi_root_node())
633 pdip
= (dev_info_t
*)DEVI(dip
)->devi_parent
;
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
);
643 i_ddi_fm_handler_enter(pdip
);
644 pfmhdl
= DEVI(pdip
)->devi_fmhdl
;
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
));
654 ptgt
= &tgt
->ft_next
;
656 i_ddi_fm_handler_exit(pdip
);
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.
666 * DDI_FM_EREPORT_CAPABLE - initialize ereport errorq and ereport
667 * capable driver property.
669 * DDI_FM_ERRCB_CAPABLE - check with parent for ability to register
672 * DDI_FM_ACCCHK_CAPABLE - initialize access handle cache and acc-chk
675 * DDI_FM_DMACHK_CAPABLE - initialize dma handle cache and dma-chk
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.
682 * This function must be called from a driver's attach(9E) entry point.
685 ddi_fm_init(dev_info_t
*dip
, int *fmcap
, ddi_iblock_cookie_t
*ibcp
)
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
;
692 if (!DEVI_IS_ATTACHING(dip
)) {
693 i_ddi_drv_ereport_post(dip
, DVR_ECONTEXT
, NULL
, DDI_NOSLEEP
);
694 *fmcap
= DDI_FM_NOT_CAPABLE
;
698 if (DDI_FM_DEFAULT_CAP(*fmcap
))
702 * Check parent for supported FM level
703 * and correct error handling PIL
705 if (dip
!= ddi_root_node()) {
708 * Initialize the default ibc. The parent may change it
709 * depending upon its capabilities.
711 ibc
= (ddi_iblock_cookie_t
)ipltospl(FM_ERR_PIL
);
713 pcap
= i_ndi_busop_fm_init(dip
, *fmcap
, &ibc
);
719 /* Initialize the per-device instance FM handle */
720 fmhdl
= kmem_zalloc(sizeof (struct i_ddi_fmhdl
), KM_SLEEP
);
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
;
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
);
738 fmhdl
->fh_dma_cache
= NULL
;
739 fmhdl
->fh_acc_cache
= NULL
;
740 fmhdl
->fh_tgts
= NULL
;
743 mutex_init(&fmhdl
->fh_lock
, NULL
, MUTEX_DRIVER
, fmhdl
->fh_ibc
);
744 devi
->devi_fmhdl
= fmhdl
;
747 * Initialize support for ereport generation
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);
756 newcap
|= DDI_FM_EREPORT_CAPABLE
;
760 * Need cooperation of the parent for error handling
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);
769 newcap
|= DDI_FM_ERRCB_CAPABLE
;
773 * Support for DMA and Access error handling
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
);
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);
785 newcap
|= DDI_FM_DMACHK_CAPABLE
;
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);
796 newcap
|= DDI_FM_ACCCHK_CAPABLE
;
800 * Return the capability support available
801 * to this driver instance
803 fmhdl
->fh_cap
= newcap
;
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.
816 * This function must be called from a driver's detach(9E) entry point.
819 ddi_fm_fini(dev_info_t
*dip
)
821 struct i_ddi_fmhdl
*fmhdl
= DEVI(dip
)->devi_fmhdl
;
825 if (!(DEVI_IS_DETACHING(dip
) || DEVI_IS_ATTACHING(dip
))) {
826 i_ddi_drv_ereport_post(dip
, DVR_ECONTEXT
, NULL
, DDI_NOSLEEP
);
830 kstat_delete(fmhdl
->fh_ksp
);
832 if (DDI_FM_EREPORT_CAP(fmhdl
->fh_cap
)) {
833 (void) ddi_prop_remove(DDI_DEV_T_NONE
, dip
,
834 "fm-ereport-capable");
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
,
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");
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");
858 i_ndi_busop_fm_fini(dip
);
861 kmem_free(fmhdl
, sizeof (struct i_ddi_fmhdl
));
862 DEVI(dip
)->devi_fmhdl
= NULL
;
866 * Return the fault management capability level for this device instance.
868 * This function may be called from user, kernel, or interrupt context.
871 ddi_fm_capable(dev_info_t
*dip
)
873 struct i_ddi_fmhdl
*fmhdl
= DEVI(dip
)->devi_fmhdl
;
876 return (DDI_FM_NOT_CAPABLE
);
878 return (fmhdl
->fh_cap
);
882 * Routines to set and get error information for/from an access or dma handle
884 * These routines may be called from user, kernel, and interrupt contexts.
888 ddi_fm_acc_err_get_fail(ddi_acc_handle_t handle
)
890 ddi_acc_hdl_t
*hp
= impl_acc_hdl_get(handle
);
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");
897 ddi_fm_acc_err_get(ddi_acc_handle_t handle
, ddi_fm_error_t
*de
, int version
)
904 if (version
!= DDI_FME_VER0
&& version
!= DDI_FME_VER1
) {
905 ddi_fm_acc_err_get_fail(handle
);
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
;
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
;
922 ddi_fm_dma_err_get_fail(ddi_dma_handle_t handle
)
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");
930 ddi_fm_dma_err_get(ddi_dma_handle_t handle
, ddi_fm_error_t
*de
, int version
)
937 if (version
!= DDI_FME_VER0
&& version
!= DDI_FME_VER1
) {
938 ddi_fm_dma_err_get_fail(handle
);
942 errp
= &((ddi_dma_impl_t
*)handle
)->dmai_error
;
944 if (errp
->err_status
== DDI_FM_OK
) {
945 if (de
->fme_status
!= DDI_FM_OK
)
946 de
->fme_status
= DDI_FM_OK
;
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
;
956 ddi_fm_acc_err_clear_fail(ddi_acc_handle_t handle
)
958 ddi_acc_hdl_t
*hp
= impl_acc_hdl_get(handle
);
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");
965 ddi_fm_acc_err_clear(ddi_acc_handle_t handle
, int version
)
972 if (version
!= DDI_FME_VER0
&& version
!= DDI_FME_VER1
) {
973 ddi_fm_acc_err_clear_fail(handle
);
977 errp
= ((ddi_acc_impl_t
*)handle
)->ahi_err
;
978 errp
->err_status
= DDI_FM_OK
;
980 errp
->err_expected
= DDI_FM_ERR_UNEXPECTED
;
984 ddi_fm_dma_err_clear_fail(ddi_dma_handle_t handle
)
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");
992 ddi_fm_dma_err_clear(ddi_dma_handle_t handle
, int version
)
999 if (version
!= DDI_FME_VER0
&& version
!= DDI_FME_VER1
) {
1000 ddi_fm_dma_err_clear_fail(handle
);
1004 errp
= &((ddi_dma_impl_t
*)handle
)->dmai_error
;
1006 errp
->err_status
= DDI_FM_OK
;
1008 errp
->err_expected
= DDI_FM_ERR_UNEXPECTED
;
1012 i_ddi_fm_acc_err_set(ddi_acc_handle_t handle
, uint64_t ena
, int status
,
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
;
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
);
1026 i_ddi_fm_dma_err_set(ddi_dma_handle_t handle
, uint64_t ena
, int status
,
1029 ddi_dma_impl_t
*hdlp
= (ddi_dma_impl_t
*)handle
;
1030 struct i_ddi_fmhdl
*fmhdl
= DEVI(hdlp
->dmai_rdip
)->devi_fmhdl
;
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
);
1039 i_ddi_fm_acc_err_cf_get(ddi_acc_handle_t handle
)
1041 ddi_acc_impl_t
*i_hdlp
= (ddi_acc_impl_t
*)handle
;
1043 return (i_hdlp
->ahi_err
->err_cf
);
1047 i_ddi_fm_dma_err_cf_get(ddi_dma_handle_t handle
)
1049 ddi_dma_impl_t
*hdlp
= (ddi_dma_impl_t
*)handle
;
1051 return (hdlp
->dmai_error
.err_cf
);