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