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