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