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