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