/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
/*
* Fault Management for Device Drivers
*
* Device drivers wishing to participate in fault management may do so by
* first initializing their fault management state and capabilties via
* ddi_fm_init(). If the system supports the requested FM capabilities,
* the IO framework will intialize FM state and return a bit mask of the
* requested capabilities.
*
* If the system does not support the requested FM capabilities,
* the device driver must behave in accordance with the programming semantics
* defined below for the capabilities returned from ddi_fm_init().
* ddi_fm_init() must be called at attach(9E) time and ddi_fm_fini() must be
* called from detach(9E) to perform FM clean-up.
*
* Driver Fault Management Capabilities
*
* DDI_FM_NOT_CAPABLE
*
* This is the default fault management capability for drivers. Drivers
* that implement no fault management capabilites or do not participate
* in fault management activities have their FM capability bitmask set
* to 0.
*
* DDI_FM_EREPORT_CAPABLE
*
* When this capability bit is set, drivers are expected to generate error
* report events via ddi_ereport_post() for the associated faults
* that are diagnosed by the IO fault manager DE. ddi_ereport_post()
* may be called in any context subject to the constraints specified
* by the interrupt iblock cookie returned during initialization.
*
* Error reports resulting from hardware component specific and common IO
* fault and driver defects must be accompanied by an Eversholt fault
* tree (.eft) by the Solaris fault manager (fmd(1M)) for
* diagnosis.
*
* DDI_FM_ERRCB_CAPABLE
*
* Device drivers are expected to implement and register an error
* handler callback function. ddi_fm_handler_register() and
* ddi_fm_handler_unregister() must be
* called in passive kernel context, typically during an attach(9E)
* or detach(9E) operation. When called by the FM IO framework,
* the callback function should check for error conditions for the
* hardware and software under its control. All detected errors
* should have ereport events generated for them.
*
* Upon completion of the error handler callback, the driver should
* return one of the following values:
*
* #define DDI_FM_OK - no error was detected
* #define DDI_FM_FATAL - a fatal error was detected
* #define DDI_FM_NONFATAL - a non-fatal error was detected
* #define DDI_FM_UNKNOWN - the error status is unknown
*
* To insure single threaded access to error handling callbacks,
* the device driver may use i_ddi_fm_handler_enter() and
* i_ddi_fm_handler_exit() when entering and exiting the callback.
*
*
* Device drivers are expected to set-up access and DMA handles
* with FM-specific attributes designed to allow nexus parent
* drivers to flag any errors seen during subsequent IO transactions.
* Drivers must set the devacc_attr_acc_flag member of their
* ddi_device_acc_attr_t structures to DDI_FLAGERR_ACC or DDI_CAUTIOUS_ACC.
* For DMA transactions, driver must set the dma_attr_flags of
* their ddi_dma_attr_t structures to DDI_DMA_FLAGERR.
*
* Upon completion of an IO transaction, device drivers are expected
* to check the status of host-side hardware access and device-side
* dma completions by calling ddi_acc_err_check() or ddi_dma_err_check()
* respectively. If the handle is associated with an error detected by
* the nexus parent or FM IO framework, ddi_fm_error_t data (status, ena
* and error expectation) is returned. If status of DDI_FM_NONFATAL or
* DDI_FM_FATAL is returned, the ena is valid and the expectation flag
* will be set to 1 if the error was unexpected (i.e. not the result
* of a peek or poke type operation).
*
* ddi_acc_err_check() and ddi_dma_err_check() may be called in any
* context subject to the constraints specified by the interrupt
* iblock cookie returned during initialization.
*
* Device drivers should generate an access (DDI_FM_IO_ACC) or dma
* (DDI_FM_IO_DMA) data path error report if DDI_FM_NONFATAL or
* DDI_FM_FATAL is returned.
*
*/
#include <sys/ddi_impldefs.h>
#include <sys/errorq_impl.h>
/* Globals */
int ddi_system_fmcap = 0;
{"erpt_dropped", KSTAT_DATA_UINT64 },
{"fm_cache_miss", KSTAT_DATA_UINT64 },
{"fm_cache_full", KSTAT_DATA_UINT64 },
{"acc_err", KSTAT_DATA_UINT64 },
{"dma_err", KSTAT_DATA_UINT64 }
};
/*
* Update the service state following the detection of an
* error.
*/
void
{
if (!DEVI_IS_DEVICE_OFFLINE(dip)) {
switch (svc_impact) {
case DDI_SERVICE_LOST:
NULL);
break;
case DDI_SERVICE_DEGRADED:
if (DEVI_IS_DEVICE_DEGRADED(dip)) {
} else if (DEVI_IS_DEVICE_DOWN(dip)) {
}
break;
case DDI_SERVICE_RESTORED:
NULL);
break;
case DDI_SERVICE_UNAFFECTED:
NULL);
break;
default:
break;
}
}
}
void
{
int i;
int depth;
char *buf;
char **stkpp;
char *sym;
return;
/* Allocate array of char * for nvlist payload */
/*
* Allocate temporary 64-bit aligned buffer for stack
* symbol strings
*/
for (i = 0; i < depth; ++i) {
stkp += DDI_FM_SYM_SZ;
}
if (errp)
else
NULL);
} else {
if (errp)
else
NULL);
}
}
/*
* fm_dev_ereport_postv: Common consolidation private interface to
* post a device tree oriented dev_scheme ereport. The device tree is
* composed of the following entities: devinfo nodes, minor nodes, and
* pathinfo nodes. All entities are associated with some devinfo node,
* either directly or indirectly. The intended devinfo node association
* for the ereport is communicated by the 'dip' argument. A minor node,
* an entity below 'dip', is represented by a non-null 'minor_name'
* argument. An application specific caller, like scsi_fm_ereport_post,
* can override the devinfo path with a pathinfo path via a non-null
* 'devpath' argument - in this case 'dip' is the MPXIO client node and
* devpath should be the path through the pHCI devinfo node to the
* pathinfo node.
*
* This interface also allows the caller to decide if the error being
* reported is know to be associated with a specific device identity
* via the 'devid' argument. The caller needs to control wether the
* devid appears as an authority in the FMRI because for some types of
* errors, like transport errors, the identity of the device on the
* other end of the transport is not guaranteed to be the current
* identity of the dip. For transport errors the caller should specify
* a NULL devid, even when there is a valid devid associated with the dip.
*
* The ddi_fm_ereport_post() implementation calls this interface with
* just a dip: devpath, minor_name, and devid are all NULL. The
* scsi_fm_ereport_post() implementation may call this interface with
* non-null devpath, minor_name, and devid arguments depending on
* wether MPXIO is enabled, and wether a transport or non-transport
* error is being posted.
*
* Additional event payload is specified via the varargs plist and, if
* not NULL, the nvlist passed in (such an nvlist will be merged into
* the payload; the caller is responsible for freeing this nvlist).
* Do not specify any high-level protocol event member names as part of the
* payload - eg no payload to be named "class", "version", "detector" etc
* or they will replace the members we construct here.
*
* The 'target-port-l0id' argument is SCSI specific. It is used
* by SCSI enumeration code when a devid is unavailable. If non-NULL
* the property-value becomes part of the ereport detector. The value
* specified might match one of the target-port-l0ids values of a
* libtopo disk chassis node. When libtopo finds a disk with a guaranteed
* unique wWWN target-port of a single-lun 'real' disk, it can add
* the target-port value to the libtopo disk chassis node target-port-l0ids
* string array property. Kernel code has no idea if this type of
* libtopo chassis node exists, or if matching will in fact occur.
*/
void
{
char *name;
/*
* This interface should be called with a fm_capable eqdip. The
* ddi_fm_ereport_post* interfaces call with eqdip == dip,
* ndi_fm_ereport_post* interfaces call with eqdip == ddi_parent(dip).
*/
goto err;
/* get ereport nvlist handle */
/*
* Driver defect - should not call with DDI_SLEEP while in
* interrupt context.
*/
if (servicing_interrupt()) {
goto err;
}
/* Use normal interfaces to allocate memory. */
goto err;
} else {
/* Use errorq interfaces to avoid memory allocation. */
goto err;
}
/*
* Form parts of an ereport:
* A: version
* B: error_class
* C: ena
* D: detector (path and optional devid authority)
* E: payload
*
* A: ereport version: first payload tuple must be the version.
*/
goto err;
}
/* B: ereport error_class: add "io." prefix to class. */
/* C: ereport ena: if not passed in, generate new ena. */
if (ena == 0)
/* D: detector: form dev scheme fmri with path and devid. */
if (devpath) {
} else {
/* derive devpath from dip */
if (dip == ddi_root_node())
else
}
if (minor_name) {
}
/* Pull parts of ereport together into ereport. */
/* Merge any preconstructed payload into the event. */
if (pl)
/* Add any remaining (after version) varargs payload to ereport. */
/* Post the ereport. */
if (nva)
else
goto out;
/* Count errors as drops. */
/* Free up nvlists if normal interfaces were used to allocate memory */
}
/*
* Generate an error report for consumption by the Solaris Fault Manager,
*
* The ENA should be set if this error is a result of an error status
* returned from ddi_dma_err_check() or ddi_acc_err_check(). Otherwise,
* an ENA value of 0 is appropriate.
*
* If sflag == DDI_NOSLEEP, ddi_fm_ereport_post () may be called
* from user, kernel, interrupt or high-interrupt context. Otherwise,
* ddi_fm_ereport_post() must be called from user or kernel context.
*
* The ndi_interfaces are provided for use by nexus drivers to post
* ereports about children who may not themselves be fm_capable.
*
* All interfaces end up in the common fm_dev_ereport_postv code above.
*/
void
{
}
void
{
}
/*
* Driver error handling entry. Prevents multiple simultaneous calls into
* driver error handling callback.
*
* May be called from a context consistent with the iblock_cookie returned
* in ddi_fm_init().
*/
void
{
}
/*
* Driver error handling exit.
*
* May be called from a context consistent with the iblock_cookie returned
* in ddi_fm_init().
*/
void
{
}
{
}
/*
* Register a fault manager error handler for this device instance
*
* This function must be called from a driver's attach(9E) routine.
*/
void
void *impl_data)
{
/*
* Check for proper calling context.
* The DDI configuration framework does not support
* DR states to allow checking for proper invocation
* from a DDI_ATTACH or DDI_RESUME. This limits context checking
* to interrupt only.
*/
if (servicing_interrupt()) {
return;
}
if (dip == ddi_root_node())
else
return;
}
/* Add dip to parent's target list of registered error handlers */
}
/*
* Unregister a fault manager error handler for this device instance
*
* This function must be called from a drivers attach(9E) or detach(9E)
* routine.
*/
void
{
/*
* Check for proper calling context.
* The DDI configuration framework does not support
* DR states to allow checking for proper invocation
* from a DDI_DETACH or DDI_SUSPEND. This limits context checking
* to interrupt only.
*/
if (servicing_interrupt()) {
return;
}
if (dip == ddi_root_node())
else
return;
}
break;
}
}
}
/*
* Initialize Fault Management capabilities for this device instance (dip).
* When called with the following capabilities, data structures neccessary
* for fault management activities are allocated and initialized.
*
* DDI_FM_EREPORT_CAPABLE - initialize ereport errorq and ereport
* capable driver property.
*
* DDI_FM_ERRCB_CAPABLE - check with parent for ability to register
* an error handler.
*
* DDI_FM_ACCCHK_CAPABLE - initialize access handle cache and acc-chk
* driver property
*
* DDI_FM_DMACHK_CAPABLE - initialize dma handle cache and dma-chk
* driver property
*
* A driver's FM capability level may not exceed that of its parent or
* system-wide FM capability. The available capability level for this
* device instance is returned in *fmcap.
*
* This function must be called from a driver's attach(9E) entry point.
*/
void
{
if (!DEVI_IS_ATTACHING(dip)) {
return;
}
if (DDI_FM_DEFAULT_CAP(*fmcap))
return;
/*
* Check parent for supported FM level
* and correct error handling PIL
*/
if (dip != ddi_root_node()) {
/*
* Initialize the default ibc. The parent may change it
* depending upon its capabilities.
*/
} else {
}
/* Initialize the per-device instance FM handle */
KSTAT_TYPE_NAMED, sizeof (struct i_ddi_fmkstat) /
return;
}
sizeof (struct i_ddi_fmkstat));
/*
* Initialize support for ereport generation
*/
"fm-ereport-capable", 0) == 0)
}
/*
* Need cooperation of the parent for error handling
*/
"fm-errcb-capable", 0) == 0)
}
/*
* Support for DMA and Access error handling
*/
/* Set-up dma chk capability prop */
"fm-dmachk-capable", 0) == 0)
}
/* Set-up dma chk capability prop */
"fm-accchk-capable", 0) == 0)
}
/*
* Return the capability support available
* to this driver instance
*/
}
/*
* Finalize Fault Management activities for this device instance.
* Outstanding IO transaction must be completed prior to calling
* this routine. All previously allocated resources and error handler
* registration are cleared and deallocated.
*
* This function must be called from a driver's detach(9E) entry point.
*/
void
{
return;
}
"fm-ereport-capable");
}
if (dip != ddi_root_node()) {
"fm-errcb-capable");
}
"fm-dmachk-capable");
}
"fm-accachk-capable");
}
}
}
}
/*
* Return the fault management capability level for this device instance.
*
* This function may be called from user, kernel, or interrupt context.
*/
int
{
return (DDI_FM_NOT_CAPABLE);
}
/*
*
* These routines may be called from user, kernel, and interrupt contexts.
*/
static void
{
}
void
{
return;
return;
}
return;
}
}
void
{
}
void
{
return;
return;
}
return;
}
}
void
{
}
void
{
return;
return;
}
}
void
{
}
void
{
return;
return;
}
}
void
int flag)
{
}
void
int flag)
{
}
{
}
{
}