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 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 /*
27 * Fault Management for Nexus Device Drivers
28 *
29 * In addition to implementing and supporting Fault Management for Device
30 * Drivers (ddifm.c), nexus drivers must support their children by
31 * reporting FM capabilities, intializing interrupt block cookies
32 * for error handling callbacks and caching mapped resources for lookup
33 * during the detection of an IO transaction error.
34 *
35 * It is typically the nexus driver that receives an error indication
36 * for a fault that may have occurred in the data path of an IO transaction.
37 * Errors may be detected or received via an interrupt, a callback from
38 * another subsystem (e.g. a cpu trap) or examination of control data.
39 *
40 * Upon detection of an error, the nexus has a responsibility to alert
41 * its children of the error and the transaction associated with that
42 * error. The actual implementation may vary depending upon the capabilities
43 * of the nexus, its underlying hardware and its children. In this file,
44 * we provide support for typical nexus driver fault management tasks.
45 *
46 * Fault Management Initialization
47 *
48 * Nexus drivers must implement two new busops, bus_fm_init() and
49 * bus_fm_fini(). bus_fm_init() is called from a child nexus or device
50 * driver and is expected to initialize any per-child state and return
51 * the FM and error interrupt priority levels of the nexus driver.
52 * Similarly, bus_fm_fini() is called by child drivers and should
53 * clean-up any resources allocated during bus_fm_init().
54 * These functions are called from passive kernel context, typically from
55 * driver attach(9F) and detach(9F) entry points.
56 *
57 * Error Handler Dispatching
58 *
59 * Nexus drivers implemented to support error handler capabilities
60 * should invoke registered error handler callbacks for child drivers
61 * thought to be involved in the error.
62 * ndi_fm_handler_dispatch() is used to invoke
63 * all error handlers and returns one of the following status
64 * indications:
65 *
66 * DDI_FM_OK - No errors found by any child
67 * DDI_FM_FATAL - one or more children have detected a fatal error
68 * DDI_FM_NONFATAL - no fatal errors, but one or more children have
69 * detected a non-fatal error
70 *
71 * ndi_fm_handler_dispatch() may be called in any context
72 * subject to the constraints specified by the interrupt iblock cookie
73 * returned during initialization.
74 *
75 * Protected Accesses
76 *
77 * When an access handle is mapped or a DMA handle is bound via the
78 * standard busops, bus_map() or bus_dma_bindhdl(), a child driver
79 * implemented to support DDI_FM_ACCCHK_CAPABLE or
80 * DDI_FM_DMACHK_CAPABLE capabilites
81 * expects the nexus to flag any errors detected for transactions
82 * associated with the mapped or bound handles.
83 *
84 * Children nexus or device drivers will set the following flags
85 * in their ddi_device_access or dma_attr_flags when requesting
86 * the an access or DMA handle mapping:
87 *
88 * DDI_DMA_FLAGERR - nexus should set error status for any errors
89 * detected for a failed DMA transaction.
90 * DDI_ACC_FLAGERR - nexus should set error status for any errors
91 * detected for a failed PIO transaction.
92 *
93 * A nexus is expected to provide additional error detection and
94 * handling for handles with these flags set.
95 *
96 * Exclusive Bus Access
97 *
98 * In cases where a driver requires a high level of fault tolerance
99 * for a programmed IO transaction, it is neccessary to grant exclusive
100 * access to the bus resource. Exclusivity guarantees that a fault
101 * resulting from a transaction on the bus can be easily traced and
102 * reported to the driver requesting the transaction.
103 *
104 * Nexus drivers must implement two new busops to support exclusive
105 * access, bus_fm_access_enter() and bus_fm_access_exit(). The IO
106 * framework will use these functions when it must set-up access
107 * handles that set devacc_attr_access to DDI_ACC_CAUTIOUS in
108 * their ddi_device_acc_attr_t request.
109 *
110 * Upon receipt of a bus_fm_access_enter() request, the nexus must prevent
111 * all other access requests until it receives bus_fm_access_exit()
112 * for the requested bus instance. bus_fm_access_enter() and
113 * bus_fm_access_exit() may be called from user, kernel or kernel
114 * interrupt context.
115 *
116 * Access and DMA Handle Caching
117 *
118 * To aid a nexus driver in associating access or DMA handles with
119 * a detected error, the nexus should cache all handles that are
120 * associated with DDI_ACC_FLAGERR, DDI_ACC_CAUTIOUS_ACC or
121 * DDI_DMA_FLAGERR requests from its children. ndi_fmc_insert() is
122 * called by a nexus to cache handles with the above protection flags
123 * and ndi_fmc_remove() is called when that handle is unmapped or
124 * unbound by the requesting child. ndi_fmc_insert() and
125 * ndi_fmc_remove() may be called from any user or kernel context.
126 *
127 * FM cache element is implemented by kmem_cache. The elements are
128 * stored in a doubly-linked searchable list. When a handle is created,
129 * ndi_fm_insert() allocates an entry from the kmem_cache and inserts
130 * the entry to the head of the list. When a handle is unmapped
131 * or unbound, ndi_fm_remove() removes its associated cache entry from
132 * the list.
133 *
134 * Upon detection of an error, the nexus may invoke ndi_fmc_error() to
135 * iterate over the handle cache of one or more of its FM compliant
136 * children. A comparison callback function is provided upon each
137 * invocation of ndi_fmc_error() to tell the IO framework if a
138 * handle is associated with an error. If so, the framework will
139 * set the error status for that handle before returning from
140 * ndi_fmc_error().
141 *
142 * ndi_fmc_error() may be called in any context
143 * subject to the constraints specified by the interrupt iblock cookie
144 * returned during initialization of the nexus and its children.
145 *
146 */
147
148 #include <sys/types.h>
149 #include <sys/param.h>
150 #include <sys/debug.h>
151 #include <sys/sunddi.h>
152 #include <sys/sunndi.h>
153 #include <sys/ddi.h>
154 #include <sys/ndi_impldefs.h>
155 #include <sys/devctl.h>
156 #include <sys/nvpair.h>
157 #include <sys/ddifm.h>
158 #include <sys/ndifm.h>
159 #include <sys/spl.h>
160 #include <sys/sysmacros.h>
161 #include <sys/devops.h>
162 #include <sys/atomic.h>
163 #include <sys/kmem.h>
164 #include <sys/fm/io/ddi.h>
165
166 kmem_cache_t *ndi_fm_entry_cache;
167
168 void
ndi_fm_init(void)169 ndi_fm_init(void)
170 {
171 ndi_fm_entry_cache = kmem_cache_create("ndi_fm_entry_cache",
172 sizeof (ndi_fmcentry_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
173 }
174
175 /*
176 * Allocate and initialize a fault management resource cache
177 * A fault management cache consists of a set of cache elements that
178 * are allocated from "ndi_fm_entry_cache".
179 */
180 /* ARGSUSED */
181 void
i_ndi_fmc_create(ndi_fmc_t ** fcpp,int qlen,ddi_iblock_cookie_t ibc)182 i_ndi_fmc_create(ndi_fmc_t **fcpp, int qlen, ddi_iblock_cookie_t ibc)
183 {
184 ndi_fmc_t *fcp;
185
186 fcp = kmem_zalloc(sizeof (ndi_fmc_t), KM_SLEEP);
187 mutex_init(&fcp->fc_lock, NULL, MUTEX_DRIVER, ibc);
188
189 *fcpp = fcp;
190 }
191
192 /*
193 * Destroy and resources associated with the given fault management cache.
194 */
195 void
i_ndi_fmc_destroy(ndi_fmc_t * fcp)196 i_ndi_fmc_destroy(ndi_fmc_t *fcp)
197 {
198 ndi_fmcentry_t *fep, *pp;
199
200 if (fcp == NULL)
201 return;
202
203 /* Free all the cached entries, this should not happen though */
204 mutex_enter(&fcp->fc_lock);
205 for (fep = fcp->fc_head; fep != NULL; fep = pp) {
206 pp = fep->fce_next;
207 kmem_cache_free(ndi_fm_entry_cache, fep);
208 }
209 mutex_exit(&fcp->fc_lock);
210 mutex_destroy(&fcp->fc_lock);
211 kmem_free(fcp, sizeof (ndi_fmc_t));
212 }
213
214 /*
215 * ndi_fmc_insert -
216 * Add a new entry to the specified cache.
217 *
218 * This function must be called at or below LOCK_LEVEL
219 */
220 void
ndi_fmc_insert(dev_info_t * dip,int flag,void * resource,void * bus_specific)221 ndi_fmc_insert(dev_info_t *dip, int flag, void *resource, void *bus_specific)
222 {
223 struct dev_info *devi = DEVI(dip);
224 ndi_fmc_t *fcp;
225 ndi_fmcentry_t *fep, **fpp;
226 struct i_ddi_fmhdl *fmhdl;
227
228 ASSERT(devi);
229
230 fmhdl = devi->devi_fmhdl;
231 if (fmhdl == NULL) {
232 return;
233 }
234
235 switch (flag) {
236 case DMA_HANDLE:
237 if (!DDI_FM_DMA_ERR_CAP(fmhdl->fh_cap)) {
238 return;
239 }
240 fcp = fmhdl->fh_dma_cache;
241 fpp = &((ddi_dma_impl_t *)resource)->dmai_error.err_fep;
242 break;
243 case ACC_HANDLE:
244 if (!DDI_FM_ACC_ERR_CAP(fmhdl->fh_cap)) {
245 i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL,
246 DDI_NOSLEEP);
247 return;
248 }
249 fcp = fmhdl->fh_acc_cache;
250 fpp = &((ddi_acc_impl_t *)resource)->ahi_err->err_fep;
251 break;
252 default:
253 ASSERT(0);
254 return;
255 }
256
257 fep = kmem_cache_alloc(ndi_fm_entry_cache, KM_NOSLEEP);
258 if (fep == NULL) {
259 atomic_inc_64(&fmhdl->fh_kstat.fek_fmc_full.value.ui64);
260 return;
261 }
262
263 /*
264 * Set-up the handle resource and bus_specific information.
265 * Also remember the pointer back to the cache for quick removal.
266 */
267 fep->fce_bus_specific = bus_specific;
268 fep->fce_resource = resource;
269 fep->fce_next = NULL;
270
271 /* Add entry to the end of the active list */
272 mutex_enter(&fcp->fc_lock);
273 ASSERT(*fpp == NULL);
274 *fpp = fep;
275 fep->fce_prev = fcp->fc_tail;
276 if (fcp->fc_tail != NULL)
277 fcp->fc_tail->fce_next = fep;
278 else
279 fcp->fc_head = fep;
280 fcp->fc_tail = fep;
281 mutex_exit(&fcp->fc_lock);
282 }
283
284 /*
285 * Remove an entry from the specified cache of access or dma mappings
286 *
287 * This function must be called at or below LOCK_LEVEL.
288 */
289 void
ndi_fmc_remove(dev_info_t * dip,int flag,const void * resource)290 ndi_fmc_remove(dev_info_t *dip, int flag, const void *resource)
291 {
292 ndi_fmc_t *fcp;
293 ndi_fmcentry_t *fep;
294 struct dev_info *devi = DEVI(dip);
295 struct i_ddi_fmhdl *fmhdl;
296
297 ASSERT(devi);
298 ASSERT(flag == DMA_HANDLE || flag == ACC_HANDLE);
299
300 fmhdl = devi->devi_fmhdl;
301 if (fmhdl == NULL) {
302 return;
303 }
304
305 /* Find cache entry pointer for this resource */
306 if (flag == DMA_HANDLE) {
307 if (!DDI_FM_DMA_ERR_CAP(fmhdl->fh_cap)) {
308 return;
309 }
310 fcp = fmhdl->fh_dma_cache;
311
312 ASSERT(fcp);
313
314 mutex_enter(&fcp->fc_lock);
315 fep = ((ddi_dma_impl_t *)resource)->dmai_error.err_fep;
316 ((ddi_dma_impl_t *)resource)->dmai_error.err_fep = NULL;
317 } else if (flag == ACC_HANDLE) {
318 if (!DDI_FM_ACC_ERR_CAP(fmhdl->fh_cap)) {
319 i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL,
320 DDI_NOSLEEP);
321 return;
322 }
323 fcp = fmhdl->fh_acc_cache;
324
325 ASSERT(fcp);
326
327 mutex_enter(&fcp->fc_lock);
328 fep = ((ddi_acc_impl_t *)resource)->ahi_err->err_fep;
329 ((ddi_acc_impl_t *)resource)->ahi_err->err_fep = NULL;
330 } else {
331 return;
332 }
333
334 /*
335 * Resource not in cache, return
336 */
337 if (fep == NULL) {
338 mutex_exit(&fcp->fc_lock);
339 atomic_inc_64(&fmhdl->fh_kstat.fek_fmc_miss.value.ui64);
340 return;
341 }
342
343 /*
344 * Updates to FM cache pointers require us to grab fmc_lock
345 * to synchronize access to the cache for ndi_fmc_insert()
346 * and ndi_fmc_error()
347 */
348 if (fep == fcp->fc_head)
349 fcp->fc_head = fep->fce_next;
350 else
351 fep->fce_prev->fce_next = fep->fce_next;
352 if (fep == fcp->fc_tail)
353 fcp->fc_tail = fep->fce_prev;
354 else
355 fep->fce_next->fce_prev = fep->fce_prev;
356 mutex_exit(&fcp->fc_lock);
357
358 kmem_cache_free(ndi_fm_entry_cache, fep);
359 }
360
361 int
ndi_fmc_entry_error(dev_info_t * dip,int flag,ddi_fm_error_t * derr,const void * bus_err_state)362 ndi_fmc_entry_error(dev_info_t *dip, int flag, ddi_fm_error_t *derr,
363 const void *bus_err_state)
364 {
365 int status, fatal = 0, nonfatal = 0;
366 ndi_fmc_t *fcp = NULL;
367 ndi_fmcentry_t *fep;
368 struct i_ddi_fmhdl *fmhdl;
369
370 ASSERT(flag == DMA_HANDLE || flag == ACC_HANDLE);
371
372 fmhdl = DEVI(dip)->devi_fmhdl;
373 ASSERT(fmhdl);
374 status = DDI_FM_UNKNOWN;
375
376 if (flag == DMA_HANDLE && DDI_FM_DMA_ERR_CAP(fmhdl->fh_cap)) {
377 fcp = fmhdl->fh_dma_cache;
378 ASSERT(fcp);
379 } else if (flag == ACC_HANDLE && DDI_FM_ACC_ERR_CAP(fmhdl->fh_cap)) {
380 fcp = fmhdl->fh_acc_cache;
381 ASSERT(fcp);
382 }
383
384 if (fcp != NULL) {
385
386 /*
387 * Check active resource entries
388 */
389 mutex_enter(&fcp->fc_lock);
390 for (fep = fcp->fc_head; fep != NULL; fep = fep->fce_next) {
391 ddi_fmcompare_t compare_func;
392
393 /*
394 * Compare captured error state with handle
395 * resources. During the comparison and
396 * subsequent error handling, we block
397 * attempts to free the cache entry.
398 */
399 compare_func = (flag == ACC_HANDLE) ?
400 i_ddi_fm_acc_err_cf_get((ddi_acc_handle_t)
401 fep->fce_resource) :
402 i_ddi_fm_dma_err_cf_get((ddi_dma_handle_t)
403 fep->fce_resource);
404
405 if (compare_func == NULL) /* unbound or not FLAGERR */
406 continue;
407
408 status = compare_func(dip, fep->fce_resource,
409 bus_err_state, fep->fce_bus_specific);
410 if (status == DDI_FM_UNKNOWN || status == DDI_FM_OK)
411 continue;
412
413 if (status == DDI_FM_FATAL)
414 ++fatal;
415 else if (status == DDI_FM_NONFATAL)
416 ++nonfatal;
417
418 /* Set the error for this resource handle */
419 if (flag == ACC_HANDLE) {
420 ddi_acc_handle_t ap = fep->fce_resource;
421
422 i_ddi_fm_acc_err_set(ap, derr->fme_ena, status,
423 DDI_FM_ERR_UNEXPECTED);
424 ddi_fm_acc_err_get(ap, derr, DDI_FME_VERSION);
425 derr->fme_acc_handle = ap;
426 } else {
427 ddi_dma_handle_t dp = fep->fce_resource;
428
429 i_ddi_fm_dma_err_set(dp, derr->fme_ena, status,
430 DDI_FM_ERR_UNEXPECTED);
431 ddi_fm_dma_err_get(dp, derr, DDI_FME_VERSION);
432 derr->fme_dma_handle = dp;
433 }
434 }
435 mutex_exit(&fcp->fc_lock);
436 }
437 return (fatal ? DDI_FM_FATAL : nonfatal ? DDI_FM_NONFATAL :
438 DDI_FM_UNKNOWN);
439 }
440
441 /*
442 * Check error state against the handle resource stored in the specified
443 * FM cache. If tdip != NULL, we check only the cache entries for tdip.
444 * The caller must ensure that tdip is valid throughout the call and
445 * all FM data structures can be safely accesses.
446 *
447 * If tdip == NULL, we check all children that have registered their
448 * FM_DMA_CHK or FM_ACC_CHK capabilities.
449 *
450 * The following status values may be returned:
451 *
452 * DDI_FM_FATAL - if at least one cache entry comparison yields a
453 * fatal error.
454 *
455 * DDI_FM_NONFATAL - if at least one cache entry comparison yields a
456 * non-fatal error and no comparison yields a fatal error.
457 *
458 * DDI_FM_UNKNOWN - cache entry comparisons did not yield fatal or
459 * non-fatal errors.
460 *
461 */
462 int
ndi_fmc_error(dev_info_t * dip,dev_info_t * tdip,int flag,uint64_t ena,const void * bus_err_state)463 ndi_fmc_error(dev_info_t *dip, dev_info_t *tdip, int flag, uint64_t ena,
464 const void *bus_err_state)
465 {
466 int status, fatal = 0, nonfatal = 0;
467 ddi_fm_error_t derr;
468 struct i_ddi_fmhdl *fmhdl;
469 struct i_ddi_fmtgt *tgt;
470
471 ASSERT(flag == DMA_HANDLE || flag == ACC_HANDLE);
472
473 i_ddi_fm_handler_enter(dip);
474 fmhdl = DEVI(dip)->devi_fmhdl;
475 ASSERT(fmhdl);
476
477 bzero(&derr, sizeof (ddi_fm_error_t));
478 derr.fme_version = DDI_FME_VERSION;
479 derr.fme_flag = DDI_FM_ERR_UNEXPECTED;
480 derr.fme_ena = ena;
481
482 for (tgt = fmhdl->fh_tgts; tgt != NULL; tgt = tgt->ft_next) {
483
484 if (tdip != NULL && tdip != tgt->ft_dip)
485 continue;
486
487 /*
488 * Attempt to find the entry in this childs handle cache
489 */
490 status = ndi_fmc_entry_error(tgt->ft_dip, flag, &derr,
491 bus_err_state);
492
493 if (status == DDI_FM_FATAL)
494 ++fatal;
495 else if (status == DDI_FM_NONFATAL)
496 ++nonfatal;
497 else
498 continue;
499
500 /*
501 * Call our child to process this error.
502 */
503 status = tgt->ft_errhdl->eh_func(tgt->ft_dip, &derr,
504 tgt->ft_errhdl->eh_impl);
505
506 if (status == DDI_FM_FATAL)
507 ++fatal;
508 else if (status == DDI_FM_NONFATAL)
509 ++nonfatal;
510 }
511
512 i_ddi_fm_handler_exit(dip);
513
514 if (fatal)
515 return (DDI_FM_FATAL);
516 else if (nonfatal)
517 return (DDI_FM_NONFATAL);
518
519 return (DDI_FM_UNKNOWN);
520 }
521
522 int
ndi_fmc_entry_error_all(dev_info_t * dip,int flag,ddi_fm_error_t * derr)523 ndi_fmc_entry_error_all(dev_info_t *dip, int flag, ddi_fm_error_t *derr)
524 {
525 ndi_fmc_t *fcp = NULL;
526 ndi_fmcentry_t *fep;
527 struct i_ddi_fmhdl *fmhdl;
528 int nonfatal = 0;
529
530 ASSERT(flag == DMA_HANDLE || flag == ACC_HANDLE);
531
532 fmhdl = DEVI(dip)->devi_fmhdl;
533 ASSERT(fmhdl);
534
535 if (flag == DMA_HANDLE && DDI_FM_DMA_ERR_CAP(fmhdl->fh_cap)) {
536 fcp = fmhdl->fh_dma_cache;
537 ASSERT(fcp);
538 } else if (flag == ACC_HANDLE && DDI_FM_ACC_ERR_CAP(fmhdl->fh_cap)) {
539 fcp = fmhdl->fh_acc_cache;
540 ASSERT(fcp);
541 }
542
543 if (fcp != NULL) {
544 /*
545 * Check active resource entries
546 */
547 mutex_enter(&fcp->fc_lock);
548 for (fep = fcp->fc_head; fep != NULL; fep = fep->fce_next) {
549 ddi_fmcompare_t compare_func;
550
551 compare_func = (flag == ACC_HANDLE) ?
552 i_ddi_fm_acc_err_cf_get((ddi_acc_handle_t)
553 fep->fce_resource) :
554 i_ddi_fm_dma_err_cf_get((ddi_dma_handle_t)
555 fep->fce_resource);
556
557 if (compare_func == NULL) /* unbound or not FLAGERR */
558 continue;
559
560 /* Set the error for this resource handle */
561 nonfatal++;
562
563 if (flag == ACC_HANDLE) {
564 ddi_acc_handle_t ap = fep->fce_resource;
565
566 i_ddi_fm_acc_err_set(ap, derr->fme_ena,
567 DDI_FM_NONFATAL, DDI_FM_ERR_UNEXPECTED);
568 ddi_fm_acc_err_get(ap, derr, DDI_FME_VERSION);
569 derr->fme_acc_handle = ap;
570 } else {
571 ddi_dma_handle_t dp = fep->fce_resource;
572
573 i_ddi_fm_dma_err_set(dp, derr->fme_ena,
574 DDI_FM_NONFATAL, DDI_FM_ERR_UNEXPECTED);
575 ddi_fm_dma_err_get(dp, derr, DDI_FME_VERSION);
576 derr->fme_dma_handle = dp;
577 }
578 }
579 mutex_exit(&fcp->fc_lock);
580 }
581 return (nonfatal ? DDI_FM_NONFATAL : DDI_FM_UNKNOWN);
582 }
583
584 /*
585 * Dispatch registered error handlers for dip. If tdip != NULL, only
586 * the error handler (if available) for tdip is invoked. Otherwise,
587 * all registered error handlers are invoked.
588 *
589 * The following status values may be returned:
590 *
591 * DDI_FM_FATAL - if at least one error handler returns a
592 * fatal error.
593 *
594 * DDI_FM_NONFATAL - if at least one error handler returns a
595 * non-fatal error and none returned a fatal error.
596 *
597 * DDI_FM_UNKNOWN - if at least one error handler returns
598 * unknown status and none return fatal or non-fatal.
599 *
600 * DDI_FM_OK - if all error handlers return DDI_FM_OK
601 */
602 int
ndi_fm_handler_dispatch(dev_info_t * dip,dev_info_t * tdip,const ddi_fm_error_t * nerr)603 ndi_fm_handler_dispatch(dev_info_t *dip, dev_info_t *tdip,
604 const ddi_fm_error_t *nerr)
605 {
606 int status;
607 int unknown = 0, fatal = 0, nonfatal = 0;
608 struct i_ddi_fmhdl *hdl;
609 struct i_ddi_fmtgt *tgt;
610
611 status = DDI_FM_UNKNOWN;
612
613 i_ddi_fm_handler_enter(dip);
614 hdl = DEVI(dip)->devi_fmhdl;
615 tgt = hdl->fh_tgts;
616 while (tgt != NULL) {
617 if (tdip == NULL || tdip == tgt->ft_dip) {
618 struct i_ddi_errhdl *errhdl;
619
620 errhdl = tgt->ft_errhdl;
621 status = errhdl->eh_func(tgt->ft_dip, nerr,
622 errhdl->eh_impl);
623
624 if (status == DDI_FM_FATAL)
625 ++fatal;
626 else if (status == DDI_FM_NONFATAL)
627 ++nonfatal;
628 else if (status == DDI_FM_UNKNOWN)
629 ++unknown;
630
631 /* Only interested in one target */
632 if (tdip != NULL)
633 break;
634 }
635 tgt = tgt->ft_next;
636 }
637 i_ddi_fm_handler_exit(dip);
638
639 if (fatal)
640 return (DDI_FM_FATAL);
641 else if (nonfatal)
642 return (DDI_FM_NONFATAL);
643 else if (unknown)
644 return (DDI_FM_UNKNOWN);
645 else
646 return (DDI_FM_OK);
647 }
648
649 /*
650 * Set error status for specified access or DMA handle
651 *
652 * May be called in any context but caller must insure validity of
653 * handle.
654 */
655 void
ndi_fm_acc_err_set(ddi_acc_handle_t handle,ddi_fm_error_t * dfe)656 ndi_fm_acc_err_set(ddi_acc_handle_t handle, ddi_fm_error_t *dfe)
657 {
658 i_ddi_fm_acc_err_set(handle, dfe->fme_ena, dfe->fme_status,
659 dfe->fme_flag);
660 }
661
662 void
ndi_fm_dma_err_set(ddi_dma_handle_t handle,ddi_fm_error_t * dfe)663 ndi_fm_dma_err_set(ddi_dma_handle_t handle, ddi_fm_error_t *dfe)
664 {
665 i_ddi_fm_dma_err_set(handle, dfe->fme_ena, dfe->fme_status,
666 dfe->fme_flag);
667 }
668
669 /*
670 * Call parent busop fm initialization routine.
671 *
672 * Called during driver attach(9E)
673 */
674 int
i_ndi_busop_fm_init(dev_info_t * dip,int tcap,ddi_iblock_cookie_t * ibc)675 i_ndi_busop_fm_init(dev_info_t *dip, int tcap, ddi_iblock_cookie_t *ibc)
676 {
677 int pcap;
678 dev_info_t *pdip = (dev_info_t *)DEVI(dip)->devi_parent;
679
680 if (dip == ddi_root_node())
681 return (ddi_system_fmcap | DDI_FM_EREPORT_CAPABLE);
682
683 /* Valid operation for BUSO_REV_6 and above */
684 if (DEVI(pdip)->devi_ops->devo_bus_ops->busops_rev < BUSO_REV_6)
685 return (DDI_FM_NOT_CAPABLE);
686
687 if (DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_init == NULL)
688 return (DDI_FM_NOT_CAPABLE);
689
690 pcap = (*DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_init)
691 (pdip, dip, tcap, ibc);
692
693 return (pcap);
694 }
695
696 /*
697 * Call parent busop fm clean-up routine.
698 *
699 * Called during driver detach(9E)
700 */
701 void
i_ndi_busop_fm_fini(dev_info_t * dip)702 i_ndi_busop_fm_fini(dev_info_t *dip)
703 {
704 dev_info_t *pdip = (dev_info_t *)DEVI(dip)->devi_parent;
705
706 if (dip == ddi_root_node())
707 return;
708
709 /* Valid operation for BUSO_REV_6 and above */
710 if (DEVI(pdip)->devi_ops->devo_bus_ops->busops_rev < BUSO_REV_6)
711 return;
712
713 if (DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_fini == NULL)
714 return;
715
716 (*DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_fini)(pdip, dip);
717 }
718
719 /*
720 * The following routines provide exclusive access to a nexus resource
721 *
722 * These busops may be called in user or kernel driver context.
723 */
724 void
i_ndi_busop_access_enter(dev_info_t * dip,ddi_acc_handle_t handle)725 i_ndi_busop_access_enter(dev_info_t *dip, ddi_acc_handle_t handle)
726 {
727 dev_info_t *pdip = (dev_info_t *)DEVI(dip)->devi_parent;
728
729 /* Valid operation for BUSO_REV_6 and above */
730 if (DEVI(pdip)->devi_ops->devo_bus_ops->busops_rev < BUSO_REV_6)
731 return;
732
733 if (DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_access_enter == NULL)
734 return;
735
736 (*DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_access_enter)
737 (pdip, handle);
738 }
739
740 void
i_ndi_busop_access_exit(dev_info_t * dip,ddi_acc_handle_t handle)741 i_ndi_busop_access_exit(dev_info_t *dip, ddi_acc_handle_t handle)
742 {
743 dev_info_t *pdip = (dev_info_t *)DEVI(dip)->devi_parent;
744
745 /* Valid operation for BUSO_REV_6 and above */
746 if (DEVI(pdip)->devi_ops->devo_bus_ops->busops_rev < BUSO_REV_6)
747 return;
748
749 if (DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_access_exit == NULL)
750 return;
751
752 (*DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_access_exit)(pdip, handle);
753 }
754