10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 52951Selowe * Common Development and Distribution License (the "License"). 62951Selowe * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 22*5197Sstephh * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 270Sstevel@tonic-gate 280Sstevel@tonic-gate /* 290Sstevel@tonic-gate * Kernel Error Queues 300Sstevel@tonic-gate * 310Sstevel@tonic-gate * A common problem when handling hardware error traps and interrupts is that 320Sstevel@tonic-gate * these errors frequently must be handled at high interrupt level, where 330Sstevel@tonic-gate * reliably producing error messages and safely examining and manipulating 340Sstevel@tonic-gate * other kernel state may not be possible. The kernel error queue primitive is 350Sstevel@tonic-gate * a common set of routines that allow a subsystem to maintain a queue of 360Sstevel@tonic-gate * errors that can be processed by an explicit call from a safe context or by a 370Sstevel@tonic-gate * soft interrupt that fires at a specific lower interrupt level. The queue 380Sstevel@tonic-gate * management code also ensures that if the system panics, all in-transit 390Sstevel@tonic-gate * errors are logged prior to reset. Each queue has an associated kstat for 400Sstevel@tonic-gate * observing the number of errors dispatched and logged, and mdb(1) debugging 410Sstevel@tonic-gate * support is provided for live and post-mortem observability. 420Sstevel@tonic-gate * 430Sstevel@tonic-gate * Memory Allocation 440Sstevel@tonic-gate * 450Sstevel@tonic-gate * All of the queue data structures are allocated in advance as part of 460Sstevel@tonic-gate * the errorq_create() call. No additional memory allocations are 470Sstevel@tonic-gate * performed as part of errorq_dispatch(), errorq_reserve(), 480Sstevel@tonic-gate * errorq_commit() or errorq_drain(). This design 490Sstevel@tonic-gate * facilitates reliable error queue processing even when the system is low 500Sstevel@tonic-gate * on memory, and ensures that errorq_dispatch() can be called from any 510Sstevel@tonic-gate * context. When the queue is created, the maximum queue length is 520Sstevel@tonic-gate * specified as a parameter to errorq_create() errorq_nvcreate(). This 530Sstevel@tonic-gate * length should represent a reasonable upper bound on the number of 540Sstevel@tonic-gate * simultaneous errors. If errorq_dispatch() or errorq_reserve() is 550Sstevel@tonic-gate * invoked and no free queue elements are available, the error is 560Sstevel@tonic-gate * dropped and will not be logged. Typically, the queue will only be 570Sstevel@tonic-gate * exhausted by an error storm, and in this case 580Sstevel@tonic-gate * the earlier errors provide the most important data for analysis. 590Sstevel@tonic-gate * When a new error is dispatched, the error data is copied into the 600Sstevel@tonic-gate * preallocated queue element so that the caller's buffer can be reused. 610Sstevel@tonic-gate * 620Sstevel@tonic-gate * When a new error is reserved, an element is moved from the free list 630Sstevel@tonic-gate * and returned to the caller. The element buffer data, eqe_data, may be 640Sstevel@tonic-gate * managed by the caller and dispatched to the errorq by calling 650Sstevel@tonic-gate * errorq_commit(). This is useful for additions to errorq's 660Sstevel@tonic-gate * created with errorq_nvcreate() to handle name-value pair (nvpair) data. 670Sstevel@tonic-gate * See below for a discussion on nvlist errorq's. 680Sstevel@tonic-gate * 690Sstevel@tonic-gate * Queue Drain Callback 700Sstevel@tonic-gate * 710Sstevel@tonic-gate * When the error queue is drained, the caller's queue drain callback is 720Sstevel@tonic-gate * invoked with a pointer to the saved error data. This function may be 730Sstevel@tonic-gate * called from passive kernel context or soft interrupt context at or 740Sstevel@tonic-gate * below LOCK_LEVEL, or as part of panic(). As such, the callback should 750Sstevel@tonic-gate * basically only be calling cmn_err (but NOT with the CE_PANIC flag). 760Sstevel@tonic-gate * The callback must not call panic(), attempt to allocate memory, or wait 770Sstevel@tonic-gate * on a condition variable. The callback may not call errorq_destroy() 780Sstevel@tonic-gate * or errorq_drain() on the same error queue that called it. 790Sstevel@tonic-gate * 800Sstevel@tonic-gate * The queue drain callback will always be called for each pending error 810Sstevel@tonic-gate * in the order in which errors were enqueued (oldest to newest). The 820Sstevel@tonic-gate * queue drain callback is guaranteed to provide at *least* once semantics 830Sstevel@tonic-gate * for all errors that are successfully dispatched (i.e. for which 840Sstevel@tonic-gate * errorq_dispatch() has successfully completed). If an unrelated panic 850Sstevel@tonic-gate * occurs while the queue drain callback is running on a vital queue, the 860Sstevel@tonic-gate * panic subsystem will continue the queue drain and the callback may be 870Sstevel@tonic-gate * invoked again for the same error. Therefore, the callback should 880Sstevel@tonic-gate * restrict itself to logging messages and taking other actions that are 890Sstevel@tonic-gate * not destructive if repeated. 900Sstevel@tonic-gate * 910Sstevel@tonic-gate * Name-Value Pair Error Queues 920Sstevel@tonic-gate * 930Sstevel@tonic-gate * During error handling, it may be more convenient to store error 940Sstevel@tonic-gate * queue element data as a fixed buffer of name-value pairs. The 950Sstevel@tonic-gate * nvpair library allows construction and destruction of nvlists in 960Sstevel@tonic-gate * in pre-allocated memory buffers. 970Sstevel@tonic-gate * 980Sstevel@tonic-gate * Error queues created via errorq_nvcreate() store queue element 990Sstevel@tonic-gate * data as fixed buffer nvlists (ereports). errorq_reserve() 1000Sstevel@tonic-gate * allocates an errorq element from eqp->eq_free and returns a valid 1010Sstevel@tonic-gate * pointer to a errorq_elem_t (queue element) and a pre-allocated 1020Sstevel@tonic-gate * fixed buffer nvlist. errorq_elem_nvl() is used to gain access 1030Sstevel@tonic-gate * to the nvlist to add name-value ereport members prior to 1040Sstevel@tonic-gate * dispatching the error queue element in errorq_commit(). 1050Sstevel@tonic-gate * 1060Sstevel@tonic-gate * Once dispatched, the drain function will return the element to 1070Sstevel@tonic-gate * eqp->eq_free and reset the associated nv_alloc structure. 1080Sstevel@tonic-gate * error_cancel() may be called to cancel an element reservation 1090Sstevel@tonic-gate * element that was never dispatched (committed). This is useful in 1100Sstevel@tonic-gate * cases where a programming error prevents a queue element from being 1110Sstevel@tonic-gate * dispatched. 1120Sstevel@tonic-gate * 1130Sstevel@tonic-gate * Queue Management 1140Sstevel@tonic-gate * 1150Sstevel@tonic-gate * The queue element structures and error data buffers are allocated in 1160Sstevel@tonic-gate * two contiguous chunks as part of errorq_create() or errorq_nvcreate(). 1170Sstevel@tonic-gate * Each queue element structure contains a next pointer, 1180Sstevel@tonic-gate * a previous pointer, and a pointer to the corresponding error data 1190Sstevel@tonic-gate * buffer. The data buffer for a nvlist errorq is a shared buffer 1200Sstevel@tonic-gate * for the allocation of name-value pair lists. The elements are kept on 1210Sstevel@tonic-gate * one of three lists: 1220Sstevel@tonic-gate * 1230Sstevel@tonic-gate * Unused elements are kept on the free list, a singly-linked list pointed 1240Sstevel@tonic-gate * to by eqp->eq_free, and linked together using eqe_prev. The eqe_next 1250Sstevel@tonic-gate * pointer is not used by the free list and will be set to NULL. 1260Sstevel@tonic-gate * 1270Sstevel@tonic-gate * Pending errors are kept on the pending list, a singly-linked list 1280Sstevel@tonic-gate * pointed to by eqp->eq_pend, and linked together using eqe_prev. This 1290Sstevel@tonic-gate * list is maintained in order from newest error to oldest. The eqe_next 1300Sstevel@tonic-gate * pointer is not used by the pending list and will be set to NULL. 1310Sstevel@tonic-gate * 1320Sstevel@tonic-gate * The processing list is a doubly-linked list pointed to by eqp->eq_phead 1330Sstevel@tonic-gate * (the oldest element) and eqp->eq_ptail (the newest element). The 1340Sstevel@tonic-gate * eqe_next pointer is used to traverse from eq_phead to eq_ptail, and the 1350Sstevel@tonic-gate * eqe_prev pointer is used to traverse from eq_ptail to eq_phead. Once a 1360Sstevel@tonic-gate * queue drain operation begins, the current pending list is moved to the 1370Sstevel@tonic-gate * processing list in a two-phase commit fashion, allowing the panic code 1380Sstevel@tonic-gate * to always locate and process all pending errors in the event that a 1390Sstevel@tonic-gate * panic occurs in the middle of queue processing. 1400Sstevel@tonic-gate * 1410Sstevel@tonic-gate * A fourth list is maintained for nvlist errorqs. The dump list, 1420Sstevel@tonic-gate * eq_dump is used to link all errorq elements that should be stored 1430Sstevel@tonic-gate * in a crash dump file in the event of a system panic. During 1440Sstevel@tonic-gate * errorq_panic(), the list is created and subsequently traversed 1450Sstevel@tonic-gate * in errorq_dump() during the final phases of a crash dump. 1460Sstevel@tonic-gate * 1470Sstevel@tonic-gate * Platform Considerations 1480Sstevel@tonic-gate * 1490Sstevel@tonic-gate * In order to simplify their implementation, error queues make use of the 1500Sstevel@tonic-gate * C wrappers for compare-and-swap. If the platform itself does not 1510Sstevel@tonic-gate * support compare-and-swap in hardware and the kernel emulation routines 1520Sstevel@tonic-gate * are used instead, then the context in which errorq_dispatch() can be 1530Sstevel@tonic-gate * safely invoked is further constrained by the implementation of the 1540Sstevel@tonic-gate * compare-and-swap emulation. Specifically, if errorq_dispatch() is 1550Sstevel@tonic-gate * called from a code path that can be executed above ATOMIC_LEVEL on such 1560Sstevel@tonic-gate * a platform, the dispatch code could potentially deadlock unless the 1570Sstevel@tonic-gate * corresponding error interrupt is blocked or disabled prior to calling 1580Sstevel@tonic-gate * errorq_dispatch(). Error queues should therefore be deployed with 1590Sstevel@tonic-gate * caution on these platforms. 1600Sstevel@tonic-gate * 1610Sstevel@tonic-gate * Interfaces 1620Sstevel@tonic-gate * 1630Sstevel@tonic-gate * errorq_t *errorq_create(name, func, private, qlen, eltsize, ipl, flags); 1640Sstevel@tonic-gate * errorq_t *errorq_nvcreate(name, func, private, qlen, eltsize, ipl, flags); 1650Sstevel@tonic-gate * 1660Sstevel@tonic-gate * Create a new error queue with the specified name, callback, and 1670Sstevel@tonic-gate * properties. A pointer to the new error queue is returned upon success, 1680Sstevel@tonic-gate * or NULL is returned to indicate that the queue could not be created. 1690Sstevel@tonic-gate * This function must be called from passive kernel context with no locks 1700Sstevel@tonic-gate * held that can prevent a sleeping memory allocation from occurring. 1710Sstevel@tonic-gate * errorq_create() will return failure if the queue kstats cannot be 1720Sstevel@tonic-gate * created, or if a soft interrupt handler cannot be registered. 1730Sstevel@tonic-gate * 1740Sstevel@tonic-gate * The queue 'name' is a string that is recorded for live and post-mortem 1750Sstevel@tonic-gate * examination by a debugger. The queue callback 'func' will be invoked 1760Sstevel@tonic-gate * for each error drained from the queue, and will receive the 'private' 1770Sstevel@tonic-gate * pointer as its first argument. The callback must obey the rules for 1780Sstevel@tonic-gate * callbacks described above. The queue will have maximum length 'qlen' 1790Sstevel@tonic-gate * and each element will be able to record up to 'eltsize' bytes of data. 1800Sstevel@tonic-gate * The queue's soft interrupt (see errorq_dispatch(), below) will fire 1810Sstevel@tonic-gate * at 'ipl', which should not exceed LOCK_LEVEL. The queue 'flags' may 1820Sstevel@tonic-gate * include the following flag: 1830Sstevel@tonic-gate * 1840Sstevel@tonic-gate * ERRORQ_VITAL - This queue contains information that is considered 1850Sstevel@tonic-gate * vital to problem diagnosis. Error queues that are marked vital will 1860Sstevel@tonic-gate * be automatically drained by the panic subsystem prior to printing 1870Sstevel@tonic-gate * the panic messages to the console. 1880Sstevel@tonic-gate * 1890Sstevel@tonic-gate * void errorq_destroy(errorq); 1900Sstevel@tonic-gate * 1910Sstevel@tonic-gate * Destroy the specified error queue. The queue is drained of any 1920Sstevel@tonic-gate * pending elements and these are logged before errorq_destroy returns. 1930Sstevel@tonic-gate * Once errorq_destroy() begins draining the queue, any simultaneous 1940Sstevel@tonic-gate * calls to dispatch errors will result in the errors being dropped. 1950Sstevel@tonic-gate * The caller must invoke a higher-level abstraction (e.g. disabling 1960Sstevel@tonic-gate * an error interrupt) to ensure that error handling code does not 1970Sstevel@tonic-gate * attempt to dispatch errors to the queue while it is being freed. 1980Sstevel@tonic-gate * 1990Sstevel@tonic-gate * void errorq_dispatch(errorq, data, len, flag); 2000Sstevel@tonic-gate * 2010Sstevel@tonic-gate * Attempt to enqueue the specified error data. If a free queue element 2020Sstevel@tonic-gate * is available, the data is copied into a free element and placed on a 2030Sstevel@tonic-gate * pending list. If no free queue element is available, the error is 2040Sstevel@tonic-gate * dropped. The data length (len) is specified in bytes and should not 2050Sstevel@tonic-gate * exceed the queue's maximum element size. If the data length is less 2060Sstevel@tonic-gate * than the maximum element size, the remainder of the queue element is 2070Sstevel@tonic-gate * filled with zeroes. The flag parameter should be one of: 2080Sstevel@tonic-gate * 2090Sstevel@tonic-gate * ERRORQ_ASYNC - Schedule a soft interrupt at the previously specified 2100Sstevel@tonic-gate * IPL to asynchronously drain the queue on behalf of the caller. 2110Sstevel@tonic-gate * 2120Sstevel@tonic-gate * ERRORQ_SYNC - Do not schedule a soft interrupt to drain the queue. 2130Sstevel@tonic-gate * The caller is presumed to be calling errorq_drain() or panic() in 2140Sstevel@tonic-gate * the near future in order to drain the queue and log the error. 2150Sstevel@tonic-gate * 2160Sstevel@tonic-gate * The errorq_dispatch() function may be called from any context, subject 2170Sstevel@tonic-gate * to the Platform Considerations described above. 2180Sstevel@tonic-gate * 2190Sstevel@tonic-gate * void errorq_drain(errorq); 2200Sstevel@tonic-gate * 2210Sstevel@tonic-gate * Drain the error queue of all pending errors. The queue's callback 2220Sstevel@tonic-gate * function is invoked for each error in order from oldest to newest. 2230Sstevel@tonic-gate * This function may be used at or below LOCK_LEVEL or from panic context. 2240Sstevel@tonic-gate * 2250Sstevel@tonic-gate * errorq_elem_t *errorq_reserve(errorq); 2260Sstevel@tonic-gate * 2270Sstevel@tonic-gate * Reserve an error queue element for later processing and dispatching. 2280Sstevel@tonic-gate * The element is returned to the caller who may add error-specific data 2290Sstevel@tonic-gate * to element. The element is retured to the free list when either 2300Sstevel@tonic-gate * errorq_commit() is called and the element asynchronously processed 2310Sstevel@tonic-gate * or immediately when errorq_cancel() is called. 2320Sstevel@tonic-gate * 2330Sstevel@tonic-gate * void errorq_commit(errorq, errorq_elem, flag); 2340Sstevel@tonic-gate * 2350Sstevel@tonic-gate * Commit an errorq element (eqep) for dispatching, see 2360Sstevel@tonic-gate * errorq_dispatch(). 2370Sstevel@tonic-gate * 2380Sstevel@tonic-gate * void errorq_cancel(errorq, errorq_elem); 2390Sstevel@tonic-gate * 2400Sstevel@tonic-gate * Cancel a pending errorq element reservation. The errorq element is 2410Sstevel@tonic-gate * returned to the free list upon cancelation. 2420Sstevel@tonic-gate */ 2430Sstevel@tonic-gate 2440Sstevel@tonic-gate #include <sys/errorq_impl.h> 2450Sstevel@tonic-gate #include <sys/sysmacros.h> 2460Sstevel@tonic-gate #include <sys/machlock.h> 2470Sstevel@tonic-gate #include <sys/cmn_err.h> 2480Sstevel@tonic-gate #include <sys/atomic.h> 2490Sstevel@tonic-gate #include <sys/systm.h> 2500Sstevel@tonic-gate #include <sys/kmem.h> 2510Sstevel@tonic-gate #include <sys/conf.h> 2520Sstevel@tonic-gate #include <sys/ddi.h> 2530Sstevel@tonic-gate #include <sys/sunddi.h> 2540Sstevel@tonic-gate #include <sys/bootconf.h> 2550Sstevel@tonic-gate #include <sys/spl.h> 2560Sstevel@tonic-gate #include <sys/dumphdr.h> 2570Sstevel@tonic-gate #include <sys/compress.h> 2580Sstevel@tonic-gate #include <sys/time.h> 2590Sstevel@tonic-gate #include <sys/panic.h> 2600Sstevel@tonic-gate #include <sys/fm/protocol.h> 2610Sstevel@tonic-gate #include <sys/fm/util.h> 2620Sstevel@tonic-gate 2630Sstevel@tonic-gate static struct errorq_kstat errorq_kstat_template = { 2640Sstevel@tonic-gate { "dispatched", KSTAT_DATA_UINT64 }, 2650Sstevel@tonic-gate { "dropped", KSTAT_DATA_UINT64 }, 2660Sstevel@tonic-gate { "logged", KSTAT_DATA_UINT64 }, 2670Sstevel@tonic-gate { "reserved", KSTAT_DATA_UINT64 }, 2680Sstevel@tonic-gate { "reserve_fail", KSTAT_DATA_UINT64 }, 2690Sstevel@tonic-gate { "committed", KSTAT_DATA_UINT64 }, 2700Sstevel@tonic-gate { "commit_fail", KSTAT_DATA_UINT64 }, 2710Sstevel@tonic-gate { "cancelled", KSTAT_DATA_UINT64 } 2720Sstevel@tonic-gate }; 2730Sstevel@tonic-gate 2740Sstevel@tonic-gate static uint64_t errorq_lost = 0; 2750Sstevel@tonic-gate static errorq_t *errorq_list = NULL; 2760Sstevel@tonic-gate static kmutex_t errorq_lock; 2770Sstevel@tonic-gate static uint64_t errorq_vitalmin = 5; 2780Sstevel@tonic-gate 2790Sstevel@tonic-gate static uint_t 2800Sstevel@tonic-gate errorq_intr(caddr_t eqp) 2810Sstevel@tonic-gate { 2820Sstevel@tonic-gate errorq_drain((errorq_t *)eqp); 2830Sstevel@tonic-gate return (DDI_INTR_CLAIMED); 2840Sstevel@tonic-gate } 2850Sstevel@tonic-gate 2860Sstevel@tonic-gate /* 2870Sstevel@tonic-gate * Create a new error queue with the specified properties and add a software 2880Sstevel@tonic-gate * interrupt handler and kstat for it. This function must be called from 2890Sstevel@tonic-gate * passive kernel context with no locks held that can prevent a sleeping 2900Sstevel@tonic-gate * memory allocation from occurring. This function will return NULL if the 2910Sstevel@tonic-gate * softint or kstat for this queue cannot be created. 2920Sstevel@tonic-gate */ 2930Sstevel@tonic-gate errorq_t * 2940Sstevel@tonic-gate errorq_create(const char *name, errorq_func_t func, void *private, 2950Sstevel@tonic-gate ulong_t qlen, size_t size, uint_t ipl, uint_t flags) 2960Sstevel@tonic-gate { 2970Sstevel@tonic-gate errorq_t *eqp = kmem_alloc(sizeof (errorq_t), KM_SLEEP); 2980Sstevel@tonic-gate ddi_iblock_cookie_t ibc = (ddi_iblock_cookie_t)(uintptr_t)ipltospl(ipl); 2990Sstevel@tonic-gate dev_info_t *dip = ddi_root_node(); 3000Sstevel@tonic-gate 3010Sstevel@tonic-gate errorq_elem_t *eep; 3020Sstevel@tonic-gate ddi_softintr_t id = NULL; 3030Sstevel@tonic-gate caddr_t data; 3040Sstevel@tonic-gate 3050Sstevel@tonic-gate ASSERT(qlen != 0 && size != 0); 3060Sstevel@tonic-gate ASSERT(ipl > 0 && ipl <= LOCK_LEVEL); 3070Sstevel@tonic-gate 3080Sstevel@tonic-gate /* 3090Sstevel@tonic-gate * If a queue is created very early in boot before device tree services 3100Sstevel@tonic-gate * are available, the queue softint handler cannot be created. We 3110Sstevel@tonic-gate * manually drain these queues and create their softint handlers when 3120Sstevel@tonic-gate * it is safe to do so as part of errorq_init(), below. 3130Sstevel@tonic-gate */ 3140Sstevel@tonic-gate if (modrootloaded && ddi_add_softintr(dip, DDI_SOFTINT_FIXED, &id, 3150Sstevel@tonic-gate &ibc, NULL, errorq_intr, (caddr_t)eqp) != DDI_SUCCESS) { 3160Sstevel@tonic-gate cmn_err(CE_WARN, "errorq_create: failed to register " 3170Sstevel@tonic-gate "IPL %u softint for queue %s", ipl, name); 3180Sstevel@tonic-gate kmem_free(eqp, sizeof (errorq_t)); 3190Sstevel@tonic-gate return (NULL); 3200Sstevel@tonic-gate } 3210Sstevel@tonic-gate 3222951Selowe if ((eqp->eq_ksp = kstat_create("unix", 0, name, "errorq", 3230Sstevel@tonic-gate KSTAT_TYPE_NAMED, sizeof (struct errorq_kstat) / 3240Sstevel@tonic-gate sizeof (kstat_named_t), KSTAT_FLAG_VIRTUAL)) == NULL) { 3250Sstevel@tonic-gate cmn_err(CE_WARN, "errorq_create: failed to create kstat " 3260Sstevel@tonic-gate "for queue %s", name); 3270Sstevel@tonic-gate if (id != NULL) 3280Sstevel@tonic-gate ddi_remove_softintr(id); 3290Sstevel@tonic-gate kmem_free(eqp, sizeof (errorq_t)); 3300Sstevel@tonic-gate return (NULL); 3310Sstevel@tonic-gate } 3320Sstevel@tonic-gate 3330Sstevel@tonic-gate bcopy(&errorq_kstat_template, &eqp->eq_kstat, 3340Sstevel@tonic-gate sizeof (struct errorq_kstat)); 3350Sstevel@tonic-gate eqp->eq_ksp->ks_data = &eqp->eq_kstat; 3360Sstevel@tonic-gate eqp->eq_ksp->ks_private = eqp; 3370Sstevel@tonic-gate kstat_install(eqp->eq_ksp); 3380Sstevel@tonic-gate 3390Sstevel@tonic-gate (void) strncpy(eqp->eq_name, name, ERRORQ_NAMELEN); 3400Sstevel@tonic-gate eqp->eq_name[ERRORQ_NAMELEN] = '\0'; 3410Sstevel@tonic-gate eqp->eq_func = func; 3420Sstevel@tonic-gate eqp->eq_private = private; 3430Sstevel@tonic-gate eqp->eq_data = kmem_alloc(qlen * size, KM_SLEEP); 3440Sstevel@tonic-gate eqp->eq_qlen = qlen; 3450Sstevel@tonic-gate eqp->eq_size = size; 3460Sstevel@tonic-gate eqp->eq_ipl = ipl; 3470Sstevel@tonic-gate eqp->eq_flags = flags | ERRORQ_ACTIVE; 3480Sstevel@tonic-gate eqp->eq_id = id; 3490Sstevel@tonic-gate mutex_init(&eqp->eq_lock, NULL, MUTEX_DEFAULT, NULL); 3500Sstevel@tonic-gate eqp->eq_elems = kmem_alloc(qlen * sizeof (errorq_elem_t), KM_SLEEP); 3510Sstevel@tonic-gate eqp->eq_phead = NULL; 3520Sstevel@tonic-gate eqp->eq_ptail = NULL; 3530Sstevel@tonic-gate eqp->eq_pend = NULL; 3540Sstevel@tonic-gate eqp->eq_dump = NULL; 3550Sstevel@tonic-gate eqp->eq_free = eqp->eq_elems; 3560Sstevel@tonic-gate 3570Sstevel@tonic-gate /* 3580Sstevel@tonic-gate * Iterate over the array of errorq_elem_t structures and place each 3590Sstevel@tonic-gate * one on the free list and set its data pointer. 3600Sstevel@tonic-gate */ 3610Sstevel@tonic-gate for (eep = eqp->eq_free, data = eqp->eq_data; qlen > 1; qlen--) { 3620Sstevel@tonic-gate eep->eqe_next = NULL; 3630Sstevel@tonic-gate eep->eqe_dump = NULL; 3640Sstevel@tonic-gate eep->eqe_prev = eep + 1; 3650Sstevel@tonic-gate eep->eqe_data = data; 3660Sstevel@tonic-gate data += size; 3670Sstevel@tonic-gate eep++; 3680Sstevel@tonic-gate } 3690Sstevel@tonic-gate 3700Sstevel@tonic-gate eep->eqe_next = NULL; 3710Sstevel@tonic-gate eep->eqe_prev = NULL; 3720Sstevel@tonic-gate eep->eqe_data = data; 3730Sstevel@tonic-gate eep->eqe_dump = NULL; 3740Sstevel@tonic-gate 3750Sstevel@tonic-gate /* 3760Sstevel@tonic-gate * Once the errorq is initialized, add it to the global list of queues, 3770Sstevel@tonic-gate * and then return a pointer to the new queue to the caller. 3780Sstevel@tonic-gate */ 3790Sstevel@tonic-gate mutex_enter(&errorq_lock); 3800Sstevel@tonic-gate eqp->eq_next = errorq_list; 3810Sstevel@tonic-gate errorq_list = eqp; 3820Sstevel@tonic-gate mutex_exit(&errorq_lock); 3830Sstevel@tonic-gate 3840Sstevel@tonic-gate return (eqp); 3850Sstevel@tonic-gate } 3860Sstevel@tonic-gate 3870Sstevel@tonic-gate /* 3880Sstevel@tonic-gate * Create a new errorq as if by errorq_create(), but set the ERRORQ_NVLIST 3890Sstevel@tonic-gate * flag and initialize each element to have the start of its data region used 3900Sstevel@tonic-gate * as an errorq_nvelem_t with a nvlist allocator that consumes the data region. 3910Sstevel@tonic-gate */ 3920Sstevel@tonic-gate errorq_t * 3930Sstevel@tonic-gate errorq_nvcreate(const char *name, errorq_func_t func, void *private, 3940Sstevel@tonic-gate ulong_t qlen, size_t size, uint_t ipl, uint_t flags) 3950Sstevel@tonic-gate { 3960Sstevel@tonic-gate errorq_t *eqp; 3970Sstevel@tonic-gate errorq_elem_t *eep; 3980Sstevel@tonic-gate 3990Sstevel@tonic-gate eqp = errorq_create(name, func, private, qlen, 4000Sstevel@tonic-gate size + sizeof (errorq_nvelem_t), ipl, flags | ERRORQ_NVLIST); 4010Sstevel@tonic-gate 4020Sstevel@tonic-gate if (eqp == NULL) 4030Sstevel@tonic-gate return (NULL); 4040Sstevel@tonic-gate 4050Sstevel@tonic-gate mutex_enter(&eqp->eq_lock); 4060Sstevel@tonic-gate 4070Sstevel@tonic-gate for (eep = eqp->eq_elems; qlen != 0; eep++, qlen--) { 4080Sstevel@tonic-gate errorq_nvelem_t *eqnp = eep->eqe_data; 4090Sstevel@tonic-gate eqnp->eqn_buf = (char *)eqnp + sizeof (errorq_nvelem_t); 4100Sstevel@tonic-gate eqnp->eqn_nva = fm_nva_xcreate(eqnp->eqn_buf, size); 4110Sstevel@tonic-gate } 4120Sstevel@tonic-gate 4130Sstevel@tonic-gate mutex_exit(&eqp->eq_lock); 4140Sstevel@tonic-gate return (eqp); 4150Sstevel@tonic-gate } 4160Sstevel@tonic-gate 4170Sstevel@tonic-gate /* 4180Sstevel@tonic-gate * To destroy an error queue, we mark it as disabled and then explicitly drain 4190Sstevel@tonic-gate * all pending errors. Once the drain is complete, we can remove the queue 4200Sstevel@tonic-gate * from the global list of queues examined by errorq_panic(), and then free 4210Sstevel@tonic-gate * the various queue data structures. The caller must use some higher-level 4220Sstevel@tonic-gate * abstraction (e.g. disabling an error interrupt) to ensure that no one will 4230Sstevel@tonic-gate * attempt to enqueue new errors while we are freeing this queue. 4240Sstevel@tonic-gate */ 4250Sstevel@tonic-gate void 4260Sstevel@tonic-gate errorq_destroy(errorq_t *eqp) 4270Sstevel@tonic-gate { 4280Sstevel@tonic-gate errorq_t *p, **pp; 4290Sstevel@tonic-gate errorq_elem_t *eep; 4300Sstevel@tonic-gate ulong_t i; 4310Sstevel@tonic-gate 4320Sstevel@tonic-gate ASSERT(eqp != NULL); 4330Sstevel@tonic-gate eqp->eq_flags &= ~ERRORQ_ACTIVE; 4340Sstevel@tonic-gate errorq_drain(eqp); 4350Sstevel@tonic-gate 4360Sstevel@tonic-gate mutex_enter(&errorq_lock); 4370Sstevel@tonic-gate pp = &errorq_list; 4380Sstevel@tonic-gate 4390Sstevel@tonic-gate for (p = errorq_list; p != NULL; p = p->eq_next) { 4400Sstevel@tonic-gate if (p == eqp) { 4410Sstevel@tonic-gate *pp = p->eq_next; 4420Sstevel@tonic-gate break; 4430Sstevel@tonic-gate } 4440Sstevel@tonic-gate pp = &p->eq_next; 4450Sstevel@tonic-gate } 4460Sstevel@tonic-gate 4470Sstevel@tonic-gate mutex_exit(&errorq_lock); 4480Sstevel@tonic-gate ASSERT(p != NULL); 4490Sstevel@tonic-gate 4500Sstevel@tonic-gate if (eqp->eq_flags & ERRORQ_NVLIST) { 4510Sstevel@tonic-gate for (eep = eqp->eq_elems, i = 0; i < eqp->eq_qlen; i++, eep++) { 4520Sstevel@tonic-gate errorq_nvelem_t *eqnp = eep->eqe_data; 4530Sstevel@tonic-gate fm_nva_xdestroy(eqnp->eqn_nva); 4540Sstevel@tonic-gate } 4550Sstevel@tonic-gate } 4560Sstevel@tonic-gate 4570Sstevel@tonic-gate mutex_destroy(&eqp->eq_lock); 4580Sstevel@tonic-gate kstat_delete(eqp->eq_ksp); 4590Sstevel@tonic-gate 4600Sstevel@tonic-gate if (eqp->eq_id != NULL) 4610Sstevel@tonic-gate ddi_remove_softintr(eqp->eq_id); 4620Sstevel@tonic-gate 4630Sstevel@tonic-gate kmem_free(eqp->eq_elems, eqp->eq_qlen * sizeof (errorq_elem_t)); 4640Sstevel@tonic-gate kmem_free(eqp->eq_data, eqp->eq_qlen * eqp->eq_size); 4650Sstevel@tonic-gate 4660Sstevel@tonic-gate kmem_free(eqp, sizeof (errorq_t)); 4670Sstevel@tonic-gate } 4680Sstevel@tonic-gate 4690Sstevel@tonic-gate /* 4700Sstevel@tonic-gate * Dispatch a new error into the queue for later processing. The specified 4710Sstevel@tonic-gate * data buffer is copied into a preallocated queue element. If 'len' is 4720Sstevel@tonic-gate * smaller than the queue element size, the remainder of the queue element is 4730Sstevel@tonic-gate * filled with zeroes. This function may be called from any context subject 4740Sstevel@tonic-gate * to the Platform Considerations described above. 4750Sstevel@tonic-gate */ 4760Sstevel@tonic-gate void 4770Sstevel@tonic-gate errorq_dispatch(errorq_t *eqp, const void *data, size_t len, uint_t flag) 4780Sstevel@tonic-gate { 4790Sstevel@tonic-gate errorq_elem_t *eep, *old; 4800Sstevel@tonic-gate 4810Sstevel@tonic-gate if (eqp == NULL || !(eqp->eq_flags & ERRORQ_ACTIVE)) { 4820Sstevel@tonic-gate atomic_add_64(&errorq_lost, 1); 4830Sstevel@tonic-gate return; /* drop error if queue is uninitialized or disabled */ 4840Sstevel@tonic-gate } 4850Sstevel@tonic-gate 4860Sstevel@tonic-gate while ((eep = eqp->eq_free) != NULL) { 4870Sstevel@tonic-gate if (casptr(&eqp->eq_free, eep, eep->eqe_prev) == eep) 4880Sstevel@tonic-gate break; 4890Sstevel@tonic-gate } 4900Sstevel@tonic-gate 4910Sstevel@tonic-gate if (eep == NULL) { 4920Sstevel@tonic-gate atomic_add_64(&eqp->eq_kstat.eqk_dropped.value.ui64, 1); 4930Sstevel@tonic-gate return; 4940Sstevel@tonic-gate } 4950Sstevel@tonic-gate 4960Sstevel@tonic-gate ASSERT(len <= eqp->eq_size); 4970Sstevel@tonic-gate bcopy(data, eep->eqe_data, MIN(eqp->eq_size, len)); 4980Sstevel@tonic-gate 4990Sstevel@tonic-gate if (len < eqp->eq_size) 5000Sstevel@tonic-gate bzero((caddr_t)eep->eqe_data + len, eqp->eq_size - len); 5010Sstevel@tonic-gate 5020Sstevel@tonic-gate for (;;) { 5030Sstevel@tonic-gate old = eqp->eq_pend; 5040Sstevel@tonic-gate eep->eqe_prev = old; 5050Sstevel@tonic-gate membar_producer(); 5060Sstevel@tonic-gate 5070Sstevel@tonic-gate if (casptr(&eqp->eq_pend, old, eep) == old) 5080Sstevel@tonic-gate break; 5090Sstevel@tonic-gate } 5100Sstevel@tonic-gate 5110Sstevel@tonic-gate atomic_add_64(&eqp->eq_kstat.eqk_dispatched.value.ui64, 1); 5120Sstevel@tonic-gate 5130Sstevel@tonic-gate if (flag == ERRORQ_ASYNC && eqp->eq_id != NULL) 5140Sstevel@tonic-gate ddi_trigger_softintr(eqp->eq_id); 5150Sstevel@tonic-gate } 5160Sstevel@tonic-gate 5170Sstevel@tonic-gate /* 5180Sstevel@tonic-gate * Drain the specified error queue by calling eq_func() for each pending error. 5190Sstevel@tonic-gate * This function must be called at or below LOCK_LEVEL or from panic context. 5200Sstevel@tonic-gate * In order to synchronize with other attempts to drain the queue, we acquire 5210Sstevel@tonic-gate * the adaptive eq_lock, blocking other consumers. Once this lock is held, 5220Sstevel@tonic-gate * we must use compare-and-swap to move the pending list to the processing 5230Sstevel@tonic-gate * list and to return elements to the free list in order to synchronize 5240Sstevel@tonic-gate * with producers, who do not acquire any locks and only use compare-and-swap. 5250Sstevel@tonic-gate * 5260Sstevel@tonic-gate * An additional constraint on this function is that if the system panics 5270Sstevel@tonic-gate * while this function is running, the panic code must be able to detect and 5280Sstevel@tonic-gate * handle all intermediate states and correctly dequeue all errors. The 5290Sstevel@tonic-gate * errorq_panic() function below will be used for detecting and handling 5300Sstevel@tonic-gate * these intermediate states. The comments in errorq_drain() below explain 5310Sstevel@tonic-gate * how we make sure each intermediate state is distinct and consistent. 5320Sstevel@tonic-gate */ 5330Sstevel@tonic-gate void 5340Sstevel@tonic-gate errorq_drain(errorq_t *eqp) 5350Sstevel@tonic-gate { 5360Sstevel@tonic-gate errorq_elem_t *eep, *fep, *dep; 5370Sstevel@tonic-gate 5380Sstevel@tonic-gate ASSERT(eqp != NULL); 5390Sstevel@tonic-gate mutex_enter(&eqp->eq_lock); 5400Sstevel@tonic-gate 5410Sstevel@tonic-gate /* 5420Sstevel@tonic-gate * If there are one or more pending errors, set eq_ptail to point to 5430Sstevel@tonic-gate * the first element on the pending list and then attempt to compare- 5440Sstevel@tonic-gate * and-swap NULL to the pending list. We use membar_producer() to 5450Sstevel@tonic-gate * make sure that eq_ptail will be visible to errorq_panic() below 5460Sstevel@tonic-gate * before the pending list is NULLed out. This section is labeled 5470Sstevel@tonic-gate * case (1) for errorq_panic, below. If eq_ptail is not yet set (1A) 5480Sstevel@tonic-gate * eq_pend has all the pending errors. If casptr fails or has not 5490Sstevel@tonic-gate * been called yet (1B), eq_pend still has all the pending errors. 5500Sstevel@tonic-gate * If casptr succeeds (1C), eq_ptail has all the pending errors. 5510Sstevel@tonic-gate */ 5520Sstevel@tonic-gate while ((eep = eqp->eq_pend) != NULL) { 5530Sstevel@tonic-gate eqp->eq_ptail = eep; 5540Sstevel@tonic-gate membar_producer(); 5550Sstevel@tonic-gate 5560Sstevel@tonic-gate if (casptr(&eqp->eq_pend, eep, NULL) == eep) 5570Sstevel@tonic-gate break; 5580Sstevel@tonic-gate } 5590Sstevel@tonic-gate 5600Sstevel@tonic-gate /* 5610Sstevel@tonic-gate * If no errors were pending, assert that eq_ptail is set to NULL, 5620Sstevel@tonic-gate * drop the consumer lock, and return without doing anything. 5630Sstevel@tonic-gate */ 5640Sstevel@tonic-gate if (eep == NULL) { 5650Sstevel@tonic-gate ASSERT(eqp->eq_ptail == NULL); 5660Sstevel@tonic-gate mutex_exit(&eqp->eq_lock); 5670Sstevel@tonic-gate return; 5680Sstevel@tonic-gate } 5690Sstevel@tonic-gate 5700Sstevel@tonic-gate /* 5710Sstevel@tonic-gate * Now iterate from eq_ptail (a.k.a. eep, the newest error) to the 5720Sstevel@tonic-gate * oldest error, setting the eqe_next pointer so that we can iterate 5730Sstevel@tonic-gate * over the errors from oldest to newest. We use membar_producer() 5740Sstevel@tonic-gate * to make sure that these stores are visible before we set eq_phead. 5750Sstevel@tonic-gate * If we panic before, during, or just after this loop (case 2), 5760Sstevel@tonic-gate * errorq_panic() will simply redo this work, as described below. 5770Sstevel@tonic-gate */ 5780Sstevel@tonic-gate for (eep->eqe_next = NULL; eep->eqe_prev != NULL; eep = eep->eqe_prev) 5790Sstevel@tonic-gate eep->eqe_prev->eqe_next = eep; 5800Sstevel@tonic-gate membar_producer(); 5810Sstevel@tonic-gate 5820Sstevel@tonic-gate /* 5830Sstevel@tonic-gate * Now set eq_phead to the head of the processing list (the oldest 5840Sstevel@tonic-gate * error) and issue another membar_producer() to make sure that 5850Sstevel@tonic-gate * eq_phead is seen as non-NULL before we clear eq_ptail. If we panic 5860Sstevel@tonic-gate * after eq_phead is set (case 3), we will detect and log these errors 5870Sstevel@tonic-gate * in errorq_panic(), as described below. 5880Sstevel@tonic-gate */ 5890Sstevel@tonic-gate eqp->eq_phead = eep; 5900Sstevel@tonic-gate membar_producer(); 5910Sstevel@tonic-gate 5920Sstevel@tonic-gate eqp->eq_ptail = NULL; 5930Sstevel@tonic-gate membar_producer(); 5940Sstevel@tonic-gate 5950Sstevel@tonic-gate /* 5960Sstevel@tonic-gate * If we enter from errorq_panic_drain(), we may already have 5970Sstevel@tonic-gate * errorq elements on the dump list. Find the tail of 5980Sstevel@tonic-gate * the list ready for append. 5990Sstevel@tonic-gate */ 6000Sstevel@tonic-gate if (panicstr && (dep = eqp->eq_dump) != NULL) { 6010Sstevel@tonic-gate while (dep->eqe_dump != NULL) 6020Sstevel@tonic-gate dep = dep->eqe_dump; 6030Sstevel@tonic-gate } 6040Sstevel@tonic-gate 6050Sstevel@tonic-gate /* 6060Sstevel@tonic-gate * Now iterate over the processing list from oldest (eq_phead) to 6070Sstevel@tonic-gate * newest and log each error. Once an error is logged, we use 6080Sstevel@tonic-gate * compare-and-swap to return it to the free list. If we panic before, 6090Sstevel@tonic-gate * during, or after calling eq_func() (case 4), the error will still be 6100Sstevel@tonic-gate * found on eq_phead and will be logged in errorq_panic below. 6110Sstevel@tonic-gate */ 6120Sstevel@tonic-gate 6130Sstevel@tonic-gate while ((eep = eqp->eq_phead) != NULL) { 6140Sstevel@tonic-gate eqp->eq_func(eqp->eq_private, eep->eqe_data, eep); 6150Sstevel@tonic-gate eqp->eq_kstat.eqk_logged.value.ui64++; 6160Sstevel@tonic-gate 6170Sstevel@tonic-gate eqp->eq_phead = eep->eqe_next; 6180Sstevel@tonic-gate membar_producer(); 6190Sstevel@tonic-gate 6200Sstevel@tonic-gate eep->eqe_next = NULL; 6210Sstevel@tonic-gate 622*5197Sstephh /* 623*5197Sstephh * On panic, we add the element to the dump list for each 624*5197Sstephh * nvlist errorq. Elements are stored oldest to newest. 625*5197Sstephh * Then continue, so we don't free and subsequently overwrite 626*5197Sstephh * any elements which we've put on the dump queue. 627*5197Sstephh */ 628*5197Sstephh if (panicstr && (eqp->eq_flags & ERRORQ_NVLIST)) { 629*5197Sstephh if (eqp->eq_dump == NULL) 630*5197Sstephh dep = eqp->eq_dump = eep; 631*5197Sstephh else 632*5197Sstephh dep = dep->eqe_dump = eep; 633*5197Sstephh membar_producer(); 634*5197Sstephh continue; 635*5197Sstephh } 636*5197Sstephh 6370Sstevel@tonic-gate for (;;) { 6380Sstevel@tonic-gate fep = eqp->eq_free; 6390Sstevel@tonic-gate eep->eqe_prev = fep; 6400Sstevel@tonic-gate membar_producer(); 6410Sstevel@tonic-gate 6420Sstevel@tonic-gate if (casptr(&eqp->eq_free, fep, eep) == fep) 6430Sstevel@tonic-gate break; 6440Sstevel@tonic-gate } 6450Sstevel@tonic-gate } 6460Sstevel@tonic-gate 6470Sstevel@tonic-gate mutex_exit(&eqp->eq_lock); 6480Sstevel@tonic-gate } 6490Sstevel@tonic-gate 6500Sstevel@tonic-gate /* 6510Sstevel@tonic-gate * Now that device tree services are available, set up the soft interrupt 6520Sstevel@tonic-gate * handlers for any queues that were created early in boot. We then 6530Sstevel@tonic-gate * manually drain these queues to report any pending early errors. 6540Sstevel@tonic-gate */ 6550Sstevel@tonic-gate void 6560Sstevel@tonic-gate errorq_init(void) 6570Sstevel@tonic-gate { 6580Sstevel@tonic-gate dev_info_t *dip = ddi_root_node(); 6590Sstevel@tonic-gate ddi_softintr_t id; 6600Sstevel@tonic-gate errorq_t *eqp; 6610Sstevel@tonic-gate 6620Sstevel@tonic-gate ASSERT(modrootloaded != 0); 6630Sstevel@tonic-gate ASSERT(dip != NULL); 6640Sstevel@tonic-gate 6650Sstevel@tonic-gate mutex_enter(&errorq_lock); 6660Sstevel@tonic-gate 6670Sstevel@tonic-gate for (eqp = errorq_list; eqp != NULL; eqp = eqp->eq_next) { 6680Sstevel@tonic-gate ddi_iblock_cookie_t ibc = 6690Sstevel@tonic-gate (ddi_iblock_cookie_t)(uintptr_t)ipltospl(eqp->eq_ipl); 6700Sstevel@tonic-gate 6710Sstevel@tonic-gate if (eqp->eq_id != NULL) 6720Sstevel@tonic-gate continue; /* softint already initialized */ 6730Sstevel@tonic-gate 6740Sstevel@tonic-gate if (ddi_add_softintr(dip, DDI_SOFTINT_FIXED, &id, &ibc, NULL, 6750Sstevel@tonic-gate errorq_intr, (caddr_t)eqp) != DDI_SUCCESS) { 6760Sstevel@tonic-gate panic("errorq_init: failed to register IPL %u softint " 6770Sstevel@tonic-gate "for queue %s", eqp->eq_ipl, eqp->eq_name); 6780Sstevel@tonic-gate } 6790Sstevel@tonic-gate 6800Sstevel@tonic-gate eqp->eq_id = id; 6810Sstevel@tonic-gate errorq_drain(eqp); 6820Sstevel@tonic-gate } 6830Sstevel@tonic-gate 6840Sstevel@tonic-gate mutex_exit(&errorq_lock); 6850Sstevel@tonic-gate } 6860Sstevel@tonic-gate 6870Sstevel@tonic-gate /* 6880Sstevel@tonic-gate * This function is designed to be called from panic context only, and 6890Sstevel@tonic-gate * therefore does not need to acquire errorq_lock when iterating over 6900Sstevel@tonic-gate * errorq_list. This function must be called no more than once for each 6910Sstevel@tonic-gate * 'what' value (if you change this then review the manipulation of 'dep'. 6920Sstevel@tonic-gate */ 6930Sstevel@tonic-gate static uint64_t 6940Sstevel@tonic-gate errorq_panic_drain(uint_t what) 6950Sstevel@tonic-gate { 6960Sstevel@tonic-gate errorq_elem_t *eep, *nep, *fep, *dep; 6970Sstevel@tonic-gate errorq_t *eqp; 6980Sstevel@tonic-gate uint64_t loggedtmp; 6990Sstevel@tonic-gate uint64_t logged = 0; 7000Sstevel@tonic-gate 7010Sstevel@tonic-gate for (eqp = errorq_list; eqp != NULL; eqp = eqp->eq_next) { 7020Sstevel@tonic-gate if ((eqp->eq_flags & (ERRORQ_VITAL | ERRORQ_NVLIST)) != what) 7030Sstevel@tonic-gate continue; /* do not drain this queue on this pass */ 7040Sstevel@tonic-gate 7050Sstevel@tonic-gate loggedtmp = eqp->eq_kstat.eqk_logged.value.ui64; 7060Sstevel@tonic-gate 7070Sstevel@tonic-gate /* 7080Sstevel@tonic-gate * In case (1B) above, eq_ptail may be set but the casptr may 7090Sstevel@tonic-gate * not have been executed yet or may have failed. Either way, 7100Sstevel@tonic-gate * we must log errors in chronological order. So we search 7110Sstevel@tonic-gate * the pending list for the error pointed to by eq_ptail. If 7120Sstevel@tonic-gate * it is found, we know that all subsequent errors are also 7130Sstevel@tonic-gate * still on the pending list, so just NULL out eq_ptail and let 7140Sstevel@tonic-gate * errorq_drain(), below, take care of the logging. 7150Sstevel@tonic-gate */ 7160Sstevel@tonic-gate for (eep = eqp->eq_pend; eep != NULL; eep = eep->eqe_prev) { 7170Sstevel@tonic-gate if (eep == eqp->eq_ptail) { 7180Sstevel@tonic-gate ASSERT(eqp->eq_phead == NULL); 7190Sstevel@tonic-gate eqp->eq_ptail = NULL; 7200Sstevel@tonic-gate break; 7210Sstevel@tonic-gate } 7220Sstevel@tonic-gate } 7230Sstevel@tonic-gate 7240Sstevel@tonic-gate /* 7250Sstevel@tonic-gate * In cases (1C) and (2) above, eq_ptail will be set to the 7260Sstevel@tonic-gate * newest error on the processing list but eq_phead will still 7270Sstevel@tonic-gate * be NULL. We set the eqe_next pointers so we can iterate 7280Sstevel@tonic-gate * over the processing list in order from oldest error to the 7290Sstevel@tonic-gate * newest error. We then set eq_phead to point to the oldest 7300Sstevel@tonic-gate * error and fall into the for-loop below. 7310Sstevel@tonic-gate */ 7320Sstevel@tonic-gate if (eqp->eq_phead == NULL && (eep = eqp->eq_ptail) != NULL) { 7330Sstevel@tonic-gate for (eep->eqe_next = NULL; eep->eqe_prev != NULL; 7340Sstevel@tonic-gate eep = eep->eqe_prev) 7350Sstevel@tonic-gate eep->eqe_prev->eqe_next = eep; 7360Sstevel@tonic-gate 7370Sstevel@tonic-gate eqp->eq_phead = eep; 7380Sstevel@tonic-gate eqp->eq_ptail = NULL; 7390Sstevel@tonic-gate } 7400Sstevel@tonic-gate 7410Sstevel@tonic-gate /* 7420Sstevel@tonic-gate * In cases (3) and (4) above (or after case (1C/2) handling), 7430Sstevel@tonic-gate * eq_phead will be set to the oldest error on the processing 7440Sstevel@tonic-gate * list. We log each error and return it to the free list. 7450Sstevel@tonic-gate * 7460Sstevel@tonic-gate * Unlike errorq_drain(), we don't need to worry about updating 7470Sstevel@tonic-gate * eq_phead because errorq_panic() will be called at most once. 7480Sstevel@tonic-gate * However, we must use casptr to update the freelist in case 7490Sstevel@tonic-gate * errors are still being enqueued during panic. 7500Sstevel@tonic-gate */ 7510Sstevel@tonic-gate for (eep = eqp->eq_phead; eep != NULL; eep = nep) { 7520Sstevel@tonic-gate eqp->eq_func(eqp->eq_private, eep->eqe_data, eep); 7530Sstevel@tonic-gate eqp->eq_kstat.eqk_logged.value.ui64++; 7540Sstevel@tonic-gate 7550Sstevel@tonic-gate nep = eep->eqe_next; 7560Sstevel@tonic-gate eep->eqe_next = NULL; 7570Sstevel@tonic-gate 758*5197Sstephh /* 759*5197Sstephh * On panic, we add the element to the dump list for 760*5197Sstephh * each nvlist errorq, stored oldest to newest. Then 761*5197Sstephh * continue, so we don't free and subsequently overwrite 762*5197Sstephh * any elements which we've put on the dump queue. 763*5197Sstephh */ 764*5197Sstephh if (eqp->eq_flags & ERRORQ_NVLIST) { 765*5197Sstephh if (eqp->eq_dump == NULL) 766*5197Sstephh dep = eqp->eq_dump = eep; 767*5197Sstephh else 768*5197Sstephh dep = dep->eqe_dump = eep; 769*5197Sstephh membar_producer(); 770*5197Sstephh continue; 771*5197Sstephh } 772*5197Sstephh 7730Sstevel@tonic-gate for (;;) { 7740Sstevel@tonic-gate fep = eqp->eq_free; 7750Sstevel@tonic-gate eep->eqe_prev = fep; 7760Sstevel@tonic-gate membar_producer(); 7770Sstevel@tonic-gate 7780Sstevel@tonic-gate if (casptr(&eqp->eq_free, fep, eep) == fep) 7790Sstevel@tonic-gate break; 7800Sstevel@tonic-gate } 7810Sstevel@tonic-gate } 7820Sstevel@tonic-gate 7830Sstevel@tonic-gate /* 7840Sstevel@tonic-gate * Now go ahead and drain any other errors on the pending list. 7850Sstevel@tonic-gate * This call transparently handles case (1A) above, as well as 7860Sstevel@tonic-gate * any other errors that were dispatched after errorq_drain() 7870Sstevel@tonic-gate * completed its first compare-and-swap. 7880Sstevel@tonic-gate */ 7890Sstevel@tonic-gate errorq_drain(eqp); 7900Sstevel@tonic-gate 7910Sstevel@tonic-gate logged += eqp->eq_kstat.eqk_logged.value.ui64 - loggedtmp; 7920Sstevel@tonic-gate } 7930Sstevel@tonic-gate return (logged); 7940Sstevel@tonic-gate } 7950Sstevel@tonic-gate 7960Sstevel@tonic-gate /* 7970Sstevel@tonic-gate * Drain all error queues - called only from panic context. Some drain 7980Sstevel@tonic-gate * functions may enqueue errors to ERRORQ_NVLIST error queues so that 7990Sstevel@tonic-gate * they may be written out in the panic dump - so ERRORQ_NVLIST queues 8000Sstevel@tonic-gate * must be drained last. Drain ERRORQ_VITAL queues before nonvital queues 8010Sstevel@tonic-gate * so that vital errors get to fill the ERRORQ_NVLIST queues first, and 8020Sstevel@tonic-gate * do not drain the nonvital queues if there are many vital errors. 8030Sstevel@tonic-gate */ 8040Sstevel@tonic-gate void 8050Sstevel@tonic-gate errorq_panic(void) 8060Sstevel@tonic-gate { 8070Sstevel@tonic-gate ASSERT(panicstr != NULL); 8080Sstevel@tonic-gate 8090Sstevel@tonic-gate if (errorq_panic_drain(ERRORQ_VITAL) <= errorq_vitalmin) 8100Sstevel@tonic-gate (void) errorq_panic_drain(0); 8110Sstevel@tonic-gate (void) errorq_panic_drain(ERRORQ_VITAL | ERRORQ_NVLIST); 8120Sstevel@tonic-gate (void) errorq_panic_drain(ERRORQ_NVLIST); 8130Sstevel@tonic-gate } 8140Sstevel@tonic-gate 8150Sstevel@tonic-gate /* 8160Sstevel@tonic-gate * Reserve an error queue element for later processing and dispatching. The 8170Sstevel@tonic-gate * element is returned to the caller who may add error-specific data to 8180Sstevel@tonic-gate * element. The element is retured to the free list when either 8190Sstevel@tonic-gate * errorq_commit() is called and the element asynchronously processed 8200Sstevel@tonic-gate * or immediately when errorq_cancel() is called. 8210Sstevel@tonic-gate */ 8220Sstevel@tonic-gate errorq_elem_t * 8230Sstevel@tonic-gate errorq_reserve(errorq_t *eqp) 8240Sstevel@tonic-gate { 8250Sstevel@tonic-gate errorq_elem_t *eqep; 8260Sstevel@tonic-gate 8270Sstevel@tonic-gate if (eqp == NULL || !(eqp->eq_flags & ERRORQ_ACTIVE)) { 8280Sstevel@tonic-gate atomic_add_64(&errorq_lost, 1); 8290Sstevel@tonic-gate return (NULL); 8300Sstevel@tonic-gate } 8310Sstevel@tonic-gate 8320Sstevel@tonic-gate while ((eqep = eqp->eq_free) != NULL) { 8330Sstevel@tonic-gate if (casptr(&eqp->eq_free, eqep, eqep->eqe_prev) == eqep) 8340Sstevel@tonic-gate break; 8350Sstevel@tonic-gate } 8360Sstevel@tonic-gate 8370Sstevel@tonic-gate if (eqep == NULL) { 8380Sstevel@tonic-gate atomic_add_64(&eqp->eq_kstat.eqk_dropped.value.ui64, 1); 8390Sstevel@tonic-gate return (NULL); 8400Sstevel@tonic-gate } 8410Sstevel@tonic-gate 8420Sstevel@tonic-gate if (eqp->eq_flags & ERRORQ_NVLIST) { 8430Sstevel@tonic-gate errorq_nvelem_t *eqnp = eqep->eqe_data; 8440Sstevel@tonic-gate nv_alloc_reset(eqnp->eqn_nva); 8450Sstevel@tonic-gate eqnp->eqn_nvl = fm_nvlist_create(eqnp->eqn_nva); 8460Sstevel@tonic-gate } 8470Sstevel@tonic-gate 8480Sstevel@tonic-gate atomic_add_64(&eqp->eq_kstat.eqk_reserved.value.ui64, 1); 8490Sstevel@tonic-gate return (eqep); 8500Sstevel@tonic-gate } 8510Sstevel@tonic-gate 8520Sstevel@tonic-gate /* 8530Sstevel@tonic-gate * Commit an errorq element (eqep) for dispatching. 8540Sstevel@tonic-gate * This function may be called from any context subject 8550Sstevel@tonic-gate * to the Platform Considerations described above. 8560Sstevel@tonic-gate */ 8570Sstevel@tonic-gate void 8580Sstevel@tonic-gate errorq_commit(errorq_t *eqp, errorq_elem_t *eqep, uint_t flag) 8590Sstevel@tonic-gate { 8600Sstevel@tonic-gate errorq_elem_t *old; 8610Sstevel@tonic-gate 8620Sstevel@tonic-gate if (eqep == NULL || !(eqp->eq_flags & ERRORQ_ACTIVE)) { 8630Sstevel@tonic-gate atomic_add_64(&eqp->eq_kstat.eqk_commit_fail.value.ui64, 1); 8640Sstevel@tonic-gate return; 8650Sstevel@tonic-gate } 8660Sstevel@tonic-gate 8670Sstevel@tonic-gate for (;;) { 8680Sstevel@tonic-gate old = eqp->eq_pend; 8690Sstevel@tonic-gate eqep->eqe_prev = old; 8700Sstevel@tonic-gate membar_producer(); 8710Sstevel@tonic-gate 8720Sstevel@tonic-gate if (casptr(&eqp->eq_pend, old, eqep) == old) 8730Sstevel@tonic-gate break; 8740Sstevel@tonic-gate } 8750Sstevel@tonic-gate 8760Sstevel@tonic-gate atomic_add_64(&eqp->eq_kstat.eqk_committed.value.ui64, 1); 8770Sstevel@tonic-gate 8780Sstevel@tonic-gate if (flag == ERRORQ_ASYNC && eqp->eq_id != NULL) 8790Sstevel@tonic-gate ddi_trigger_softintr(eqp->eq_id); 8800Sstevel@tonic-gate } 8810Sstevel@tonic-gate 8820Sstevel@tonic-gate /* 8830Sstevel@tonic-gate * Cancel an errorq element reservation by returning the specified element 8840Sstevel@tonic-gate * to the free list. Duplicate or invalid frees are not supported. 8850Sstevel@tonic-gate */ 8860Sstevel@tonic-gate void 8870Sstevel@tonic-gate errorq_cancel(errorq_t *eqp, errorq_elem_t *eqep) 8880Sstevel@tonic-gate { 8890Sstevel@tonic-gate errorq_elem_t *fep; 8900Sstevel@tonic-gate 8910Sstevel@tonic-gate if (eqep == NULL || !(eqp->eq_flags & ERRORQ_ACTIVE)) 8920Sstevel@tonic-gate return; 8930Sstevel@tonic-gate 8940Sstevel@tonic-gate for (;;) { 8950Sstevel@tonic-gate fep = eqp->eq_free; 8960Sstevel@tonic-gate eqep->eqe_prev = fep; 8970Sstevel@tonic-gate membar_producer(); 8980Sstevel@tonic-gate 8990Sstevel@tonic-gate if (casptr(&eqp->eq_free, fep, eqep) == fep) 9000Sstevel@tonic-gate break; 9010Sstevel@tonic-gate } 9020Sstevel@tonic-gate 9030Sstevel@tonic-gate atomic_add_64(&eqp->eq_kstat.eqk_cancelled.value.ui64, 1); 9040Sstevel@tonic-gate } 9050Sstevel@tonic-gate 9060Sstevel@tonic-gate /* 9070Sstevel@tonic-gate * Write elements on the dump list of each nvlist errorq to the dump device. 9080Sstevel@tonic-gate * Upon reboot, fmd(1M) will extract and replay them for diagnosis. 9090Sstevel@tonic-gate */ 9100Sstevel@tonic-gate void 9110Sstevel@tonic-gate errorq_dump(void) 9120Sstevel@tonic-gate { 9130Sstevel@tonic-gate errorq_elem_t *eep; 9140Sstevel@tonic-gate errorq_t *eqp; 9150Sstevel@tonic-gate 9160Sstevel@tonic-gate if (ereport_dumpbuf == NULL) 9170Sstevel@tonic-gate return; /* reboot or panic before errorq is even set up */ 9180Sstevel@tonic-gate 9190Sstevel@tonic-gate for (eqp = errorq_list; eqp != NULL; eqp = eqp->eq_next) { 9200Sstevel@tonic-gate if (!(eqp->eq_flags & ERRORQ_NVLIST) || 9210Sstevel@tonic-gate !(eqp->eq_flags & ERRORQ_ACTIVE)) 9220Sstevel@tonic-gate continue; /* do not dump this queue on panic */ 9230Sstevel@tonic-gate 9240Sstevel@tonic-gate for (eep = eqp->eq_dump; eep != NULL; eep = eep->eqe_dump) { 9250Sstevel@tonic-gate errorq_nvelem_t *eqnp = eep->eqe_data; 9260Sstevel@tonic-gate size_t len = 0; 9270Sstevel@tonic-gate erpt_dump_t ed; 9280Sstevel@tonic-gate int err; 9290Sstevel@tonic-gate 9300Sstevel@tonic-gate (void) nvlist_size(eqnp->eqn_nvl, 9310Sstevel@tonic-gate &len, NV_ENCODE_NATIVE); 9320Sstevel@tonic-gate 9330Sstevel@tonic-gate if (len > ereport_dumplen || len == 0) { 9340Sstevel@tonic-gate cmn_err(CE_WARN, "%s: unable to save error " 9350Sstevel@tonic-gate "report %p due to size %lu\n", 9360Sstevel@tonic-gate eqp->eq_name, (void *)eep, len); 9370Sstevel@tonic-gate continue; 9380Sstevel@tonic-gate } 9390Sstevel@tonic-gate 9400Sstevel@tonic-gate if ((err = nvlist_pack(eqnp->eqn_nvl, 9410Sstevel@tonic-gate (char **)&ereport_dumpbuf, &ereport_dumplen, 9420Sstevel@tonic-gate NV_ENCODE_NATIVE, KM_NOSLEEP)) != 0) { 9430Sstevel@tonic-gate cmn_err(CE_WARN, "%s: unable to save error " 9440Sstevel@tonic-gate "report %p due to pack error %d\n", 9450Sstevel@tonic-gate eqp->eq_name, (void *)eep, err); 9460Sstevel@tonic-gate continue; 9470Sstevel@tonic-gate } 9480Sstevel@tonic-gate 9490Sstevel@tonic-gate ed.ed_magic = ERPT_MAGIC; 9500Sstevel@tonic-gate ed.ed_chksum = checksum32(ereport_dumpbuf, len); 9510Sstevel@tonic-gate ed.ed_size = (uint32_t)len; 9520Sstevel@tonic-gate ed.ed_pad = 0; 9530Sstevel@tonic-gate ed.ed_hrt_nsec = 0; 9540Sstevel@tonic-gate ed.ed_hrt_base = panic_hrtime; 9550Sstevel@tonic-gate ed.ed_tod_base.sec = panic_hrestime.tv_sec; 9560Sstevel@tonic-gate ed.ed_tod_base.nsec = panic_hrestime.tv_nsec; 9570Sstevel@tonic-gate 9580Sstevel@tonic-gate dumpvp_write(&ed, sizeof (ed)); 9590Sstevel@tonic-gate dumpvp_write(ereport_dumpbuf, len); 9600Sstevel@tonic-gate } 9610Sstevel@tonic-gate } 9620Sstevel@tonic-gate } 9630Sstevel@tonic-gate 9640Sstevel@tonic-gate nvlist_t * 9650Sstevel@tonic-gate errorq_elem_nvl(errorq_t *eqp, const errorq_elem_t *eqep) 9660Sstevel@tonic-gate { 9670Sstevel@tonic-gate errorq_nvelem_t *eqnp = eqep->eqe_data; 9680Sstevel@tonic-gate 9690Sstevel@tonic-gate ASSERT(eqp->eq_flags & ERRORQ_ACTIVE && eqp->eq_flags & ERRORQ_NVLIST); 9700Sstevel@tonic-gate 9710Sstevel@tonic-gate return (eqnp->eqn_nvl); 9720Sstevel@tonic-gate } 9730Sstevel@tonic-gate 9740Sstevel@tonic-gate nv_alloc_t * 9750Sstevel@tonic-gate errorq_elem_nva(errorq_t *eqp, const errorq_elem_t *eqep) 9760Sstevel@tonic-gate { 9770Sstevel@tonic-gate errorq_nvelem_t *eqnp = eqep->eqe_data; 9780Sstevel@tonic-gate 9790Sstevel@tonic-gate ASSERT(eqp->eq_flags & ERRORQ_ACTIVE && eqp->eq_flags & ERRORQ_NVLIST); 9800Sstevel@tonic-gate 9810Sstevel@tonic-gate return (eqnp->eqn_nva); 9820Sstevel@tonic-gate } 9830Sstevel@tonic-gate 9840Sstevel@tonic-gate /* 9850Sstevel@tonic-gate * Reserve a new element and duplicate the data of the original into it. 9860Sstevel@tonic-gate */ 9870Sstevel@tonic-gate void * 9880Sstevel@tonic-gate errorq_elem_dup(errorq_t *eqp, const errorq_elem_t *eqep, errorq_elem_t **neqep) 9890Sstevel@tonic-gate { 9900Sstevel@tonic-gate ASSERT(eqp->eq_flags & ERRORQ_ACTIVE); 9910Sstevel@tonic-gate ASSERT(!(eqp->eq_flags & ERRORQ_NVLIST)); 9920Sstevel@tonic-gate 9930Sstevel@tonic-gate if ((*neqep = errorq_reserve(eqp)) == NULL) 9940Sstevel@tonic-gate return (NULL); 9950Sstevel@tonic-gate 9960Sstevel@tonic-gate bcopy(eqep->eqe_data, (*neqep)->eqe_data, eqp->eq_size); 9970Sstevel@tonic-gate return ((*neqep)->eqe_data); 9980Sstevel@tonic-gate } 999