ndifm.c revision 8aec91825357bbeaf2ab5d30fc97fe5051a6b8dd
13faa91230bde46da937bf33010b9accc5bdeb59sd/*
13faa91230bde46da937bf33010b9accc5bdeb59sd * CDDL HEADER START
13faa91230bde46da937bf33010b9accc5bdeb59sd *
13faa91230bde46da937bf33010b9accc5bdeb59sd * The contents of this file are subject to the terms of the
13faa91230bde46da937bf33010b9accc5bdeb59sd * Common Development and Distribution License (the "License").
13faa91230bde46da937bf33010b9accc5bdeb59sd * You may not use this file except in compliance with the License.
13faa91230bde46da937bf33010b9accc5bdeb59sd *
13faa91230bde46da937bf33010b9accc5bdeb59sd * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
13faa91230bde46da937bf33010b9accc5bdeb59sd * or http://www.opensolaris.org/os/licensing.
13faa91230bde46da937bf33010b9accc5bdeb59sd * See the License for the specific language governing permissions
13faa91230bde46da937bf33010b9accc5bdeb59sd * and limitations under the License.
13faa91230bde46da937bf33010b9accc5bdeb59sd *
13faa91230bde46da937bf33010b9accc5bdeb59sd * When distributing Covered Code, include this CDDL HEADER in each
13faa91230bde46da937bf33010b9accc5bdeb59sd * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
13faa91230bde46da937bf33010b9accc5bdeb59sd * If applicable, add the following below this CDDL HEADER, with the
13faa91230bde46da937bf33010b9accc5bdeb59sd * fields enclosed by brackets "[]" replaced with your own identifying
13faa91230bde46da937bf33010b9accc5bdeb59sd * information: Portions Copyright [yyyy] [name of copyright owner]
13faa91230bde46da937bf33010b9accc5bdeb59sd *
13faa91230bde46da937bf33010b9accc5bdeb59sd * CDDL HEADER END
13faa91230bde46da937bf33010b9accc5bdeb59sd */
13faa91230bde46da937bf33010b9accc5bdeb59sd/*
13faa91230bde46da937bf33010b9accc5bdeb59sd * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
b64bfe7dc77dc5c5561cdcd10c80b0b550701a24Trang Do * Use is subject to license terms.
13faa91230bde46da937bf33010b9accc5bdeb59sd */
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd#pragma ident "%Z%%M% %I% %E% SMI"
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd/*
13faa91230bde46da937bf33010b9accc5bdeb59sd * Fault Management for Nexus Device Drivers
13faa91230bde46da937bf33010b9accc5bdeb59sd *
13faa91230bde46da937bf33010b9accc5bdeb59sd * In addition to implementing and supporting Fault Management for Device
13faa91230bde46da937bf33010b9accc5bdeb59sd * Drivers (ddifm.c), nexus drivers must support their children by
13faa91230bde46da937bf33010b9accc5bdeb59sd * reporting FM capabilities, intializing interrupt block cookies
13faa91230bde46da937bf33010b9accc5bdeb59sd * for error handling callbacks and caching mapped resources for lookup
13faa91230bde46da937bf33010b9accc5bdeb59sd * during the detection of an IO transaction error.
13faa91230bde46da937bf33010b9accc5bdeb59sd *
13faa91230bde46da937bf33010b9accc5bdeb59sd * It is typically the nexus driver that receives an error indication
13faa91230bde46da937bf33010b9accc5bdeb59sd * for a fault that may have occurred in the data path of an IO transaction.
13faa91230bde46da937bf33010b9accc5bdeb59sd * Errors may be detected or received via an interrupt, a callback from
13faa91230bde46da937bf33010b9accc5bdeb59sd * another subsystem (e.g. a cpu trap) or examination of control data.
13faa91230bde46da937bf33010b9accc5bdeb59sd *
13faa91230bde46da937bf33010b9accc5bdeb59sd * Upon detection of an error, the nexus has a responsibility to alert
13faa91230bde46da937bf33010b9accc5bdeb59sd * its children of the error and the transaction associated with that
13faa91230bde46da937bf33010b9accc5bdeb59sd * error. The actual implementation may vary depending upon the capabilities
13faa91230bde46da937bf33010b9accc5bdeb59sd * of the nexus, its underlying hardware and its children. In this file,
13faa91230bde46da937bf33010b9accc5bdeb59sd * we provide support for typical nexus driver fault management tasks.
13faa91230bde46da937bf33010b9accc5bdeb59sd *
13faa91230bde46da937bf33010b9accc5bdeb59sd * Fault Management Initialization
13faa91230bde46da937bf33010b9accc5bdeb59sd *
13faa91230bde46da937bf33010b9accc5bdeb59sd * Nexus drivers must implement two new busops, bus_fm_init() and
13faa91230bde46da937bf33010b9accc5bdeb59sd * bus_fm_fini(). bus_fm_init() is called from a child nexus or device
13faa91230bde46da937bf33010b9accc5bdeb59sd * driver and is expected to initialize any per-child state and return
13faa91230bde46da937bf33010b9accc5bdeb59sd * the FM and error interrupt priority levels of the nexus driver.
13faa91230bde46da937bf33010b9accc5bdeb59sd * Similarly, bus_fm_fini() is called by child drivers and should
13faa91230bde46da937bf33010b9accc5bdeb59sd * clean-up any resources allocated during bus_fm_init().
13faa91230bde46da937bf33010b9accc5bdeb59sd * These functions are called from passive kernel context, typically from
13faa91230bde46da937bf33010b9accc5bdeb59sd * driver attach(9F) and detach(9F) entry points.
13faa91230bde46da937bf33010b9accc5bdeb59sd *
13faa91230bde46da937bf33010b9accc5bdeb59sd * Error Handler Dispatching
13faa91230bde46da937bf33010b9accc5bdeb59sd *
13faa91230bde46da937bf33010b9accc5bdeb59sd * Nexus drivers implemented to support error handler capabilities
13faa91230bde46da937bf33010b9accc5bdeb59sd * should invoke registered error handler callbacks for child drivers
13faa91230bde46da937bf33010b9accc5bdeb59sd * thought to be involved in the error.
13faa91230bde46da937bf33010b9accc5bdeb59sd * ndi_fm_handler_dispatch() is used to invoke
13faa91230bde46da937bf33010b9accc5bdeb59sd * all error handlers and returns one of the following status
13faa91230bde46da937bf33010b9accc5bdeb59sd * indications:
13faa91230bde46da937bf33010b9accc5bdeb59sd *
13faa91230bde46da937bf33010b9accc5bdeb59sd * DDI_FM_OK - No errors found by any child
13faa91230bde46da937bf33010b9accc5bdeb59sd * DDI_FM_FATAL - one or more children have detected a fatal error
13faa91230bde46da937bf33010b9accc5bdeb59sd * DDI_FM_NONFATAL - no fatal errors, but one or more children have
13faa91230bde46da937bf33010b9accc5bdeb59sd * detected a non-fatal error
13faa91230bde46da937bf33010b9accc5bdeb59sd *
13faa91230bde46da937bf33010b9accc5bdeb59sd * ndi_fm_handler_dispatch() may be called in any context
13faa91230bde46da937bf33010b9accc5bdeb59sd * subject to the constraints specified by the interrupt iblock cookie
13faa91230bde46da937bf33010b9accc5bdeb59sd * returned during initialization.
13faa91230bde46da937bf33010b9accc5bdeb59sd *
13faa91230bde46da937bf33010b9accc5bdeb59sd * Protected Accesses
13faa91230bde46da937bf33010b9accc5bdeb59sd *
13faa91230bde46da937bf33010b9accc5bdeb59sd * When an access handle is mapped or a DMA handle is bound via the
13faa91230bde46da937bf33010b9accc5bdeb59sd * standard busops, bus_map() or bus_dma_bindhdl(), a child driver
13faa91230bde46da937bf33010b9accc5bdeb59sd * implemented to support DDI_FM_ACCCHK_CAPABLE or
13faa91230bde46da937bf33010b9accc5bdeb59sd * DDI_FM_DMACHK_CAPABLE capabilites
13faa91230bde46da937bf33010b9accc5bdeb59sd * expects the nexus to flag any errors detected for transactions
13faa91230bde46da937bf33010b9accc5bdeb59sd * associated with the mapped or bound handles.
13faa91230bde46da937bf33010b9accc5bdeb59sd *
13faa91230bde46da937bf33010b9accc5bdeb59sd * Children nexus or device drivers will set the following flags
13faa91230bde46da937bf33010b9accc5bdeb59sd * in their ddi_device_access or dma_attr_flags when requesting
13faa91230bde46da937bf33010b9accc5bdeb59sd * the an access or DMA handle mapping:
13faa91230bde46da937bf33010b9accc5bdeb59sd *
13faa91230bde46da937bf33010b9accc5bdeb59sd * DDI_DMA_FLAGERR - nexus should set error status for any errors
13faa91230bde46da937bf33010b9accc5bdeb59sd * detected for a failed DMA transaction.
13faa91230bde46da937bf33010b9accc5bdeb59sd * DDI_ACC_FLAGERR - nexus should set error status for any errors
13faa91230bde46da937bf33010b9accc5bdeb59sd * detected for a failed PIO transaction.
13faa91230bde46da937bf33010b9accc5bdeb59sd *
13faa91230bde46da937bf33010b9accc5bdeb59sd * A nexus is expected to provide additional error detection and
13faa91230bde46da937bf33010b9accc5bdeb59sd * handling for handles with these flags set.
13faa91230bde46da937bf33010b9accc5bdeb59sd *
13faa91230bde46da937bf33010b9accc5bdeb59sd * Exclusive Bus Access
13faa91230bde46da937bf33010b9accc5bdeb59sd *
13faa91230bde46da937bf33010b9accc5bdeb59sd * In cases where a driver requires a high level of fault tolerance
25351652d920ae27c5a56c199da581033ce763f6Vuong Nguyen * for a programmed IO transaction, it is neccessary to grant exclusive
13faa91230bde46da937bf33010b9accc5bdeb59sd * access to the bus resource. Exclusivity guarantees that a fault
13faa91230bde46da937bf33010b9accc5bdeb59sd * resulting from a transaction on the bus can be easily traced and
67d4b2f88b8e27bb035d67a046d5aad7db3bfc71gk * reported to the driver requesting the transaction.
13faa91230bde46da937bf33010b9accc5bdeb59sd *
13faa91230bde46da937bf33010b9accc5bdeb59sd * Nexus drivers must implement two new busops to support exclusive
13faa91230bde46da937bf33010b9accc5bdeb59sd * access, bus_fm_access_enter() and bus_fm_access_exit(). The IO
13faa91230bde46da937bf33010b9accc5bdeb59sd * framework will use these functions when it must set-up access
67d4b2f88b8e27bb035d67a046d5aad7db3bfc71gk * handles that set devacc_attr_access to DDI_ACC_CAUTIOUS in
13faa91230bde46da937bf33010b9accc5bdeb59sd * their ddi_device_acc_attr_t request.
13faa91230bde46da937bf33010b9accc5bdeb59sd *
13faa91230bde46da937bf33010b9accc5bdeb59sd * Upon receipt of a bus_fm_access_enter() request, the nexus must prevent
13faa91230bde46da937bf33010b9accc5bdeb59sd * all other access requests until it receives bus_fm_access_exit()
13faa91230bde46da937bf33010b9accc5bdeb59sd * for the requested bus instance. bus_fm_access_enter() and
13faa91230bde46da937bf33010b9accc5bdeb59sd * bus_fm_access_exit() may be called from user, kernel or kernel
67d4b2f88b8e27bb035d67a046d5aad7db3bfc71gk * interrupt context.
13faa91230bde46da937bf33010b9accc5bdeb59sd *
13faa91230bde46da937bf33010b9accc5bdeb59sd * Access and DMA Handle Caching
25351652d920ae27c5a56c199da581033ce763f6Vuong Nguyen *
25351652d920ae27c5a56c199da581033ce763f6Vuong Nguyen * To aid a nexus driver in associating access or DMA handles with
25351652d920ae27c5a56c199da581033ce763f6Vuong Nguyen * a detected error, the nexus should cache all handles that are
25351652d920ae27c5a56c199da581033ce763f6Vuong Nguyen * associated with DDI_ACC_FLAGERR, DDI_ACC_CAUTIOUS_ACC or
25351652d920ae27c5a56c199da581033ce763f6Vuong Nguyen * DDI_DMA_FLAGERR requests from its children. ndi_fmc_insert() is
25351652d920ae27c5a56c199da581033ce763f6Vuong Nguyen * called by a nexus to cache handles with the above protection flags
25351652d920ae27c5a56c199da581033ce763f6Vuong Nguyen * and ndi_fmc_remove() is called when that handle is unmapped or
25351652d920ae27c5a56c199da581033ce763f6Vuong Nguyen * unbound by the requesting child. ndi_fmc_insert() and
13faa91230bde46da937bf33010b9accc5bdeb59sd * ndi_fmc_remove() may be called from any user or kernel context.
13faa91230bde46da937bf33010b9accc5bdeb59sd *
67d4b2f88b8e27bb035d67a046d5aad7db3bfc71gk * FM caches are allocated during ddi_fm_init() and maintained
13faa91230bde46da937bf33010b9accc5bdeb59sd * as an array of elements that may be on one of two lists:
25351652d920ae27c5a56c199da581033ce763f6Vuong Nguyen * free or active. The free list is a singly-linked list of
13faa91230bde46da937bf33010b9accc5bdeb59sd * elements available for activity. ndi_fm_insert() moves the
13faa91230bde46da937bf33010b9accc5bdeb59sd * element at the head of the free to the active list. The active
13faa91230bde46da937bf33010b9accc5bdeb59sd * list is a doubly-linked searchable list.
13faa91230bde46da937bf33010b9accc5bdeb59sd * When a handle is unmapped or unbound, its associated cache
13faa91230bde46da937bf33010b9accc5bdeb59sd * entry is removed from the active list back to the free list.
13faa91230bde46da937bf33010b9accc5bdeb59sd *
13faa91230bde46da937bf33010b9accc5bdeb59sd * Upon detection of an error, the nexus may invoke ndi_fmc_error() to
67d4b2f88b8e27bb035d67a046d5aad7db3bfc71gk * iterate over the handle cache of one or more of its FM compliant
13faa91230bde46da937bf33010b9accc5bdeb59sd * children. A comparison callback function is provided upon each
13faa91230bde46da937bf33010b9accc5bdeb59sd * invocation of ndi_fmc_error() to tell the IO framework if a
13faa91230bde46da937bf33010b9accc5bdeb59sd * handle is associated with an error. If so, the framework will
13faa91230bde46da937bf33010b9accc5bdeb59sd * set the error status for that handle before returning from
13faa91230bde46da937bf33010b9accc5bdeb59sd * ndi_fmc_error().
13faa91230bde46da937bf33010b9accc5bdeb59sd *
13faa91230bde46da937bf33010b9accc5bdeb59sd * ndi_fmc_error() may be called in any context
13faa91230bde46da937bf33010b9accc5bdeb59sd * subject to the constraints specified by the interrupt iblock cookie
13faa91230bde46da937bf33010b9accc5bdeb59sd * returned during initialization of the nexus and its children.
67d4b2f88b8e27bb035d67a046d5aad7db3bfc71gk *
13faa91230bde46da937bf33010b9accc5bdeb59sd */
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd#include <sys/types.h>
13faa91230bde46da937bf33010b9accc5bdeb59sd#include <sys/param.h>
13faa91230bde46da937bf33010b9accc5bdeb59sd#include <sys/debug.h>
13faa91230bde46da937bf33010b9accc5bdeb59sd#include <sys/sunddi.h>
13faa91230bde46da937bf33010b9accc5bdeb59sd#include <sys/sunndi.h>
13faa91230bde46da937bf33010b9accc5bdeb59sd#include <sys/ddi.h>
13faa91230bde46da937bf33010b9accc5bdeb59sd#include <sys/ndi_impldefs.h>
13faa91230bde46da937bf33010b9accc5bdeb59sd#include <sys/devctl.h>
67d4b2f88b8e27bb035d67a046d5aad7db3bfc71gk#include <sys/nvpair.h>
13faa91230bde46da937bf33010b9accc5bdeb59sd#include <sys/ddifm.h>
13faa91230bde46da937bf33010b9accc5bdeb59sd#include <sys/ndifm.h>
13faa91230bde46da937bf33010b9accc5bdeb59sd#include <sys/spl.h>
13faa91230bde46da937bf33010b9accc5bdeb59sd#include <sys/sysmacros.h>
13faa91230bde46da937bf33010b9accc5bdeb59sd#include <sys/devops.h>
13faa91230bde46da937bf33010b9accc5bdeb59sd#include <sys/atomic.h>
13faa91230bde46da937bf33010b9accc5bdeb59sd#include <sys/fm/io/ddi.h>
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd/*
13faa91230bde46da937bf33010b9accc5bdeb59sd * Allocate and initialize a fault management resource cache
13faa91230bde46da937bf33010b9accc5bdeb59sd * A fault management cache consists of a set of cache elements that
13faa91230bde46da937bf33010b9accc5bdeb59sd * may be on one of two lists: free or active.
13faa91230bde46da937bf33010b9accc5bdeb59sd *
13faa91230bde46da937bf33010b9accc5bdeb59sd * At creation time, every element but one is placed on the free list
13faa91230bde46da937bf33010b9accc5bdeb59sd * except for the first element. This element is reserved as the first
186d582bd9dbcd38e0aeea49036d47d3426a3536Surya Prakki * element of the active list and serves as an anchor for the active
13faa91230bde46da937bf33010b9accc5bdeb59sd * list in ndi_fmc_insert() and ndi_fmc_remove(). In these functions,
13faa91230bde46da937bf33010b9accc5bdeb59sd * it is not neccessary to check for the existence or validity of
13faa91230bde46da937bf33010b9accc5bdeb59sd * the active list.
13faa91230bde46da937bf33010b9accc5bdeb59sd */
13faa91230bde46da937bf33010b9accc5bdeb59sdvoid
13faa91230bde46da937bf33010b9accc5bdeb59sdi_ndi_fmc_create(ndi_fmc_t **fcpp, int qlen, ddi_iblock_cookie_t ibc)
67d4b2f88b8e27bb035d67a046d5aad7db3bfc71gk{
13faa91230bde46da937bf33010b9accc5bdeb59sd ndi_fmc_t *fcp;
13faa91230bde46da937bf33010b9accc5bdeb59sd ndi_fmcentry_t *fep;
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd ASSERT(qlen > 1);
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd fcp = kmem_zalloc(sizeof (ndi_fmc_t), KM_SLEEP);
13faa91230bde46da937bf33010b9accc5bdeb59sd mutex_init(&fcp->fc_lock, NULL, MUTEX_DRIVER, ibc);
13faa91230bde46da937bf33010b9accc5bdeb59sd mutex_init(&fcp->fc_free_lock, NULL, MUTEX_DRIVER, NULL);
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd /* Preallocate and initialize entries for this fm cache */
13faa91230bde46da937bf33010b9accc5bdeb59sd fcp->fc_elems = kmem_zalloc(qlen * sizeof (ndi_fmcentry_t), KM_SLEEP);
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd fcp->fc_len = qlen;
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd /* Intialize the active and free lists */
13faa91230bde46da937bf33010b9accc5bdeb59sd fcp->fc_active = fcp->fc_tail = fcp->fc_elems;
13faa91230bde46da937bf33010b9accc5bdeb59sd fcp->fc_free = fcp->fc_elems + 1;
13faa91230bde46da937bf33010b9accc5bdeb59sd qlen--;
13faa91230bde46da937bf33010b9accc5bdeb59sd for (fep = fcp->fc_free; qlen > 1; qlen--) {
13faa91230bde46da937bf33010b9accc5bdeb59sd fep->fce_prev = fep + 1;
13faa91230bde46da937bf33010b9accc5bdeb59sd fep++;
13faa91230bde46da937bf33010b9accc5bdeb59sd }
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd *fcpp = fcp;
13faa91230bde46da937bf33010b9accc5bdeb59sd}
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd/*
67d4b2f88b8e27bb035d67a046d5aad7db3bfc71gk * Destroy and resources associated with the given fault management cache.
13faa91230bde46da937bf33010b9accc5bdeb59sd */
13faa91230bde46da937bf33010b9accc5bdeb59sdvoid
13faa91230bde46da937bf33010b9accc5bdeb59sdi_ndi_fmc_destroy(ndi_fmc_t *fcp)
13faa91230bde46da937bf33010b9accc5bdeb59sd{
13faa91230bde46da937bf33010b9accc5bdeb59sd if (fcp == NULL)
13faa91230bde46da937bf33010b9accc5bdeb59sd return;
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd kmem_free(fcp->fc_elems, fcp->fc_len * sizeof (ndi_fmcentry_t));
13faa91230bde46da937bf33010b9accc5bdeb59sd kmem_free(fcp, sizeof (ndi_fmc_t));
13faa91230bde46da937bf33010b9accc5bdeb59sd}
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd/*
13faa91230bde46da937bf33010b9accc5bdeb59sd * Grow an existing fault management cache by grow_sz number of entries
13faa91230bde46da937bf33010b9accc5bdeb59sd */
13faa91230bde46da937bf33010b9accc5bdeb59sdstatic int
13faa91230bde46da937bf33010b9accc5bdeb59sdfmc_grow(ndi_fmc_t *fcp, int flag, int grow_sz)
13faa91230bde46da937bf33010b9accc5bdeb59sd{
13faa91230bde46da937bf33010b9accc5bdeb59sd int olen, nlen;
13faa91230bde46da937bf33010b9accc5bdeb59sd void *resource;
13faa91230bde46da937bf33010b9accc5bdeb59sd ndi_fmcentry_t *ncp, *oep, *nep, *nnep;
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd ASSERT(grow_sz);
13faa91230bde46da937bf33010b9accc5bdeb59sd ASSERT(MUTEX_HELD(&fcp->fc_free_lock));
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd /* Allocate a new cache */
13faa91230bde46da937bf33010b9accc5bdeb59sd nlen = grow_sz + fcp->fc_len;
13faa91230bde46da937bf33010b9accc5bdeb59sd if ((ncp = kmem_zalloc(nlen * sizeof (ndi_fmcentry_t),
13faa91230bde46da937bf33010b9accc5bdeb59sd KM_NOSLEEP)) == NULL)
13faa91230bde46da937bf33010b9accc5bdeb59sd return (1);
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd /* Migrate old cache to new cache */
13faa91230bde46da937bf33010b9accc5bdeb59sd oep = fcp->fc_elems;
13faa91230bde46da937bf33010b9accc5bdeb59sd olen = fcp->fc_len;
13faa91230bde46da937bf33010b9accc5bdeb59sd for (nep = ncp; ; olen--) {
13faa91230bde46da937bf33010b9accc5bdeb59sd resource = nep->fce_resource = oep->fce_resource;
13faa91230bde46da937bf33010b9accc5bdeb59sd nep->fce_bus_specific = oep->fce_bus_specific;
13faa91230bde46da937bf33010b9accc5bdeb59sd if (resource) {
13faa91230bde46da937bf33010b9accc5bdeb59sd if (flag == DMA_HANDLE) {
13faa91230bde46da937bf33010b9accc5bdeb59sd ((ddi_dma_impl_t *)resource)->
13faa91230bde46da937bf33010b9accc5bdeb59sd dmai_error.err_fep = nep;
13faa91230bde46da937bf33010b9accc5bdeb59sd } else if (flag == ACC_HANDLE) {
13faa91230bde46da937bf33010b9accc5bdeb59sd ((ddi_acc_impl_t *)resource)->
13faa91230bde46da937bf33010b9accc5bdeb59sd ahi_err->err_fep = nep;
13faa91230bde46da937bf33010b9accc5bdeb59sd }
13faa91230bde46da937bf33010b9accc5bdeb59sd }
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd /*
13faa91230bde46da937bf33010b9accc5bdeb59sd * This is the last entry. Set the tail pointer and
13faa91230bde46da937bf33010b9accc5bdeb59sd * terminate processing of the old cache.
13faa91230bde46da937bf33010b9accc5bdeb59sd */
13faa91230bde46da937bf33010b9accc5bdeb59sd if (olen == 1) {
13faa91230bde46da937bf33010b9accc5bdeb59sd fcp->fc_tail = nep;
13faa91230bde46da937bf33010b9accc5bdeb59sd ++nep;
13faa91230bde46da937bf33010b9accc5bdeb59sd break;
13faa91230bde46da937bf33010b9accc5bdeb59sd }
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd /*
13faa91230bde46da937bf33010b9accc5bdeb59sd * Set the next and previous pointer for the new cache
13faa91230bde46da937bf33010b9accc5bdeb59sd * entry.
13faa91230bde46da937bf33010b9accc5bdeb59sd */
13faa91230bde46da937bf33010b9accc5bdeb59sd nnep = nep + 1;
13faa91230bde46da937bf33010b9accc5bdeb59sd nep->fce_next = nnep;
13faa91230bde46da937bf33010b9accc5bdeb59sd nnep->fce_prev = nep;
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd /* Advance to the next entry */
13faa91230bde46da937bf33010b9accc5bdeb59sd ++oep;
13faa91230bde46da937bf33010b9accc5bdeb59sd nep = nnep;
9c94f155585ea35e938fea603bc227c685223abdCheng Sean Ye }
9c94f155585ea35e938fea603bc227c685223abdCheng Sean Ye
13faa91230bde46da937bf33010b9accc5bdeb59sd kmem_free(fcp->fc_elems, fcp->fc_len * sizeof (ndi_fmcentry_t));
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd /* Initialize and add remaining new cache entries to the free list */
13faa91230bde46da937bf33010b9accc5bdeb59sd olen = fcp->fc_len + 1;
13faa91230bde46da937bf33010b9accc5bdeb59sd fcp->fc_len = nlen;
13faa91230bde46da937bf33010b9accc5bdeb59sd for (fcp->fc_free = nep; nlen > olen; nlen--) {
13faa91230bde46da937bf33010b9accc5bdeb59sd nep->fce_prev = nep + 1;
13faa91230bde46da937bf33010b9accc5bdeb59sd nep++;
13faa91230bde46da937bf33010b9accc5bdeb59sd }
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd fcp->fc_active = ncp;
13faa91230bde46da937bf33010b9accc5bdeb59sd fcp->fc_elems = ncp;
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd return (0);
13faa91230bde46da937bf33010b9accc5bdeb59sd}
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd/*
13faa91230bde46da937bf33010b9accc5bdeb59sd * ndi_fmc_insert -
13faa91230bde46da937bf33010b9accc5bdeb59sd * Add a new entry to the specified cache.
13faa91230bde46da937bf33010b9accc5bdeb59sd *
13faa91230bde46da937bf33010b9accc5bdeb59sd * This function must be called at or below LOCK_LEVEL
13faa91230bde46da937bf33010b9accc5bdeb59sd */
13faa91230bde46da937bf33010b9accc5bdeb59sdvoid
13faa91230bde46da937bf33010b9accc5bdeb59sdndi_fmc_insert(dev_info_t *dip, int flag, void *resource, void *bus_specific)
13faa91230bde46da937bf33010b9accc5bdeb59sd{
13faa91230bde46da937bf33010b9accc5bdeb59sd struct dev_info *devi = DEVI(dip);
13faa91230bde46da937bf33010b9accc5bdeb59sd ndi_fmc_t *fcp;
13faa91230bde46da937bf33010b9accc5bdeb59sd ndi_fmcentry_t *fep, **fpp;
13faa91230bde46da937bf33010b9accc5bdeb59sd struct i_ddi_fmhdl *fmhdl;
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd ASSERT(devi);
13faa91230bde46da937bf33010b9accc5bdeb59sd ASSERT(flag == DMA_HANDLE || flag == ACC_HANDLE);
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd fmhdl = devi->devi_fmhdl;
13faa91230bde46da937bf33010b9accc5bdeb59sd if (fmhdl == NULL) {
13faa91230bde46da937bf33010b9accc5bdeb59sd i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_NOSLEEP);
13faa91230bde46da937bf33010b9accc5bdeb59sd return;
13faa91230bde46da937bf33010b9accc5bdeb59sd }
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd if (flag == DMA_HANDLE) {
13faa91230bde46da937bf33010b9accc5bdeb59sd if (!DDI_FM_DMA_ERR_CAP(fmhdl->fh_cap)) {
13faa91230bde46da937bf33010b9accc5bdeb59sd i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL,
13faa91230bde46da937bf33010b9accc5bdeb59sd DDI_NOSLEEP);
13faa91230bde46da937bf33010b9accc5bdeb59sd return;
13faa91230bde46da937bf33010b9accc5bdeb59sd }
13faa91230bde46da937bf33010b9accc5bdeb59sd fcp = fmhdl->fh_dma_cache;
13faa91230bde46da937bf33010b9accc5bdeb59sd fpp = &((ddi_dma_impl_t *)resource)->dmai_error.err_fep;
13faa91230bde46da937bf33010b9accc5bdeb59sd } else if (flag == ACC_HANDLE) {
13faa91230bde46da937bf33010b9accc5bdeb59sd if (!DDI_FM_ACC_ERR_CAP(fmhdl->fh_cap)) {
13faa91230bde46da937bf33010b9accc5bdeb59sd i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL,
13faa91230bde46da937bf33010b9accc5bdeb59sd DDI_NOSLEEP);
13faa91230bde46da937bf33010b9accc5bdeb59sd return;
13faa91230bde46da937bf33010b9accc5bdeb59sd }
13faa91230bde46da937bf33010b9accc5bdeb59sd fcp = fmhdl->fh_acc_cache;
13faa91230bde46da937bf33010b9accc5bdeb59sd fpp = &((ddi_acc_impl_t *)resource)->ahi_err->err_fep;
13faa91230bde46da937bf33010b9accc5bdeb59sd }
13faa91230bde46da937bf33010b9accc5bdeb59sd ASSERT(*fpp == NULL);
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd mutex_enter(&fcp->fc_free_lock);
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd /* Get an entry from the free list */
13faa91230bde46da937bf33010b9accc5bdeb59sd fep = fcp->fc_free;
13faa91230bde46da937bf33010b9accc5bdeb59sd if (fep == NULL) {
13faa91230bde46da937bf33010b9accc5bdeb59sd if (fmc_grow(fcp, flag,
13faa91230bde46da937bf33010b9accc5bdeb59sd (flag == ACC_HANDLE ? default_acccache_sz :
13faa91230bde46da937bf33010b9accc5bdeb59sd default_dmacache_sz)) != 0) {
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd /* Unable to get an entry or grow this cache */
13faa91230bde46da937bf33010b9accc5bdeb59sd atomic_add_64(
13faa91230bde46da937bf33010b9accc5bdeb59sd &fmhdl->fh_kstat.fek_fmc_full.value.ui64, 1);
13faa91230bde46da937bf33010b9accc5bdeb59sd mutex_exit(&fcp->fc_free_lock);
13faa91230bde46da937bf33010b9accc5bdeb59sd return;
13faa91230bde46da937bf33010b9accc5bdeb59sd }
13faa91230bde46da937bf33010b9accc5bdeb59sd atomic_add_64(&fmhdl->fh_kstat.fek_fmc_grew.value.ui64, 1);
13faa91230bde46da937bf33010b9accc5bdeb59sd fep = fcp->fc_free;
13faa91230bde46da937bf33010b9accc5bdeb59sd }
13faa91230bde46da937bf33010b9accc5bdeb59sd fcp->fc_free = fep->fce_prev;
13faa91230bde46da937bf33010b9accc5bdeb59sd mutex_exit(&fcp->fc_free_lock);
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd /*
13faa91230bde46da937bf33010b9accc5bdeb59sd * Set-up the handle resource and bus_specific information.
13faa91230bde46da937bf33010b9accc5bdeb59sd * Also remember the pointer back to the cache for quick removal.
13faa91230bde46da937bf33010b9accc5bdeb59sd */
13faa91230bde46da937bf33010b9accc5bdeb59sd fep->fce_bus_specific = bus_specific;
13faa91230bde46da937bf33010b9accc5bdeb59sd fep->fce_resource = resource;
13faa91230bde46da937bf33010b9accc5bdeb59sd fep->fce_next = NULL;
13faa91230bde46da937bf33010b9accc5bdeb59sd *fpp = fep;
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd /* Add entry to the end of the active list */
13faa91230bde46da937bf33010b9accc5bdeb59sd mutex_enter(&fcp->fc_lock);
13faa91230bde46da937bf33010b9accc5bdeb59sd fep->fce_prev = fcp->fc_tail;
13faa91230bde46da937bf33010b9accc5bdeb59sd fcp->fc_tail->fce_next = fep;
13faa91230bde46da937bf33010b9accc5bdeb59sd fcp->fc_tail = fep;
13faa91230bde46da937bf33010b9accc5bdeb59sd mutex_exit(&fcp->fc_lock);
13faa91230bde46da937bf33010b9accc5bdeb59sd}
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd/*
13faa91230bde46da937bf33010b9accc5bdeb59sd * Remove an entry from the specified cache of access or dma mappings
13faa91230bde46da937bf33010b9accc5bdeb59sd *
13faa91230bde46da937bf33010b9accc5bdeb59sd * This function must be called at or below LOCK_LEVEL.
13faa91230bde46da937bf33010b9accc5bdeb59sd */
13faa91230bde46da937bf33010b9accc5bdeb59sdvoid
13faa91230bde46da937bf33010b9accc5bdeb59sdndi_fmc_remove(dev_info_t *dip, int flag, const void *resource)
13faa91230bde46da937bf33010b9accc5bdeb59sd{
13faa91230bde46da937bf33010b9accc5bdeb59sd ndi_fmc_t *fcp;
13faa91230bde46da937bf33010b9accc5bdeb59sd ndi_fmcentry_t *fep;
13faa91230bde46da937bf33010b9accc5bdeb59sd struct dev_info *devi = DEVI(dip);
13faa91230bde46da937bf33010b9accc5bdeb59sd struct i_ddi_fmhdl *fmhdl;
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd ASSERT(devi);
13faa91230bde46da937bf33010b9accc5bdeb59sd ASSERT(flag == DMA_HANDLE || flag == ACC_HANDLE);
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd fmhdl = devi->devi_fmhdl;
13faa91230bde46da937bf33010b9accc5bdeb59sd if (fmhdl == NULL) {
13faa91230bde46da937bf33010b9accc5bdeb59sd i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_NOSLEEP);
13faa91230bde46da937bf33010b9accc5bdeb59sd return;
13faa91230bde46da937bf33010b9accc5bdeb59sd }
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd /* Find cache entry pointer for this resource */
13faa91230bde46da937bf33010b9accc5bdeb59sd if (flag == DMA_HANDLE) {
13faa91230bde46da937bf33010b9accc5bdeb59sd if (!DDI_FM_DMA_ERR_CAP(fmhdl->fh_cap)) {
13faa91230bde46da937bf33010b9accc5bdeb59sd i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL,
13faa91230bde46da937bf33010b9accc5bdeb59sd DDI_NOSLEEP);
13faa91230bde46da937bf33010b9accc5bdeb59sd return;
13faa91230bde46da937bf33010b9accc5bdeb59sd }
13faa91230bde46da937bf33010b9accc5bdeb59sd fcp = fmhdl->fh_dma_cache;
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd ASSERT(fcp);
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd fep = ((ddi_dma_impl_t *)resource)->dmai_error.err_fep;
13faa91230bde46da937bf33010b9accc5bdeb59sd ((ddi_dma_impl_t *)resource)->dmai_error.err_fep = NULL;
13faa91230bde46da937bf33010b9accc5bdeb59sd } else if (flag == ACC_HANDLE) {
13faa91230bde46da937bf33010b9accc5bdeb59sd if (!DDI_FM_ACC_ERR_CAP(fmhdl->fh_cap)) {
13faa91230bde46da937bf33010b9accc5bdeb59sd i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL,
13faa91230bde46da937bf33010b9accc5bdeb59sd DDI_NOSLEEP);
13faa91230bde46da937bf33010b9accc5bdeb59sd return;
13faa91230bde46da937bf33010b9accc5bdeb59sd }
13faa91230bde46da937bf33010b9accc5bdeb59sd fcp = fmhdl->fh_acc_cache;
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd ASSERT(fcp);
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd fep = ((ddi_acc_impl_t *)resource)->ahi_err->err_fep;
13faa91230bde46da937bf33010b9accc5bdeb59sd ((ddi_acc_impl_t *)resource)->ahi_err->err_fep = NULL;
13faa91230bde46da937bf33010b9accc5bdeb59sd }
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd /*
13faa91230bde46da937bf33010b9accc5bdeb59sd * Resource not in cache, return
13faa91230bde46da937bf33010b9accc5bdeb59sd */
13faa91230bde46da937bf33010b9accc5bdeb59sd if (fep == NULL)
13faa91230bde46da937bf33010b9accc5bdeb59sd return;
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd mutex_enter(&fcp->fc_lock);
13faa91230bde46da937bf33010b9accc5bdeb59sd fep->fce_prev->fce_next = fep->fce_next;
13faa91230bde46da937bf33010b9accc5bdeb59sd if (fep == fcp->fc_tail)
13faa91230bde46da937bf33010b9accc5bdeb59sd fcp->fc_tail = fep->fce_prev;
13faa91230bde46da937bf33010b9accc5bdeb59sd else
13faa91230bde46da937bf33010b9accc5bdeb59sd fep->fce_next->fce_prev = fep->fce_prev;
13faa91230bde46da937bf33010b9accc5bdeb59sd mutex_exit(&fcp->fc_lock);
b64bfe7dc77dc5c5561cdcd10c80b0b550701a24Trang Do
b64bfe7dc77dc5c5561cdcd10c80b0b550701a24Trang Do /* Add entry back to the free list */
b64bfe7dc77dc5c5561cdcd10c80b0b550701a24Trang Do mutex_enter(&fcp->fc_free_lock);
b64bfe7dc77dc5c5561cdcd10c80b0b550701a24Trang Do fep->fce_prev = fcp->fc_free;
b64bfe7dc77dc5c5561cdcd10c80b0b550701a24Trang Do fcp->fc_free = fep;
b64bfe7dc77dc5c5561cdcd10c80b0b550701a24Trang Do mutex_exit(&fcp->fc_free_lock);
b64bfe7dc77dc5c5561cdcd10c80b0b550701a24Trang Do}
b64bfe7dc77dc5c5561cdcd10c80b0b550701a24Trang Do
b64bfe7dc77dc5c5561cdcd10c80b0b550701a24Trang Doint
b64bfe7dc77dc5c5561cdcd10c80b0b550701a24Trang Dondi_fmc_entry_error(dev_info_t *dip, int flag, ddi_fm_error_t *derr,
b64bfe7dc77dc5c5561cdcd10c80b0b550701a24Trang Do const void *bus_err_state)
b64bfe7dc77dc5c5561cdcd10c80b0b550701a24Trang Do{
b64bfe7dc77dc5c5561cdcd10c80b0b550701a24Trang Do int status, fatal = 0, nonfatal = 0;
b64bfe7dc77dc5c5561cdcd10c80b0b550701a24Trang Do ndi_fmc_t *fcp = NULL;
b64bfe7dc77dc5c5561cdcd10c80b0b550701a24Trang Do ndi_fmcentry_t *fep;
b64bfe7dc77dc5c5561cdcd10c80b0b550701a24Trang Do struct i_ddi_fmhdl *fmhdl;
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd ASSERT(flag == DMA_HANDLE || flag == ACC_HANDLE);
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd fmhdl = DEVI(dip)->devi_fmhdl;
13faa91230bde46da937bf33010b9accc5bdeb59sd ASSERT(fmhdl);
13faa91230bde46da937bf33010b9accc5bdeb59sd status = DDI_FM_UNKNOWN;
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd if (flag == DMA_HANDLE && DDI_FM_DMA_ERR_CAP(fmhdl->fh_cap)) {
13faa91230bde46da937bf33010b9accc5bdeb59sd fcp = fmhdl->fh_dma_cache;
13faa91230bde46da937bf33010b9accc5bdeb59sd ASSERT(fcp);
13faa91230bde46da937bf33010b9accc5bdeb59sd } else if (flag == ACC_HANDLE && DDI_FM_ACC_ERR_CAP(fmhdl->fh_cap)) {
13faa91230bde46da937bf33010b9accc5bdeb59sd fcp = fmhdl->fh_acc_cache;
13faa91230bde46da937bf33010b9accc5bdeb59sd ASSERT(fcp);
13faa91230bde46da937bf33010b9accc5bdeb59sd }
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd if (fcp != NULL) {
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd /*
13faa91230bde46da937bf33010b9accc5bdeb59sd * Check active resource entries
13faa91230bde46da937bf33010b9accc5bdeb59sd */
13faa91230bde46da937bf33010b9accc5bdeb59sd mutex_enter(&fcp->fc_lock);
13faa91230bde46da937bf33010b9accc5bdeb59sd for (fep = fcp->fc_active->fce_next; fep != NULL;
13faa91230bde46da937bf33010b9accc5bdeb59sd fep = fep->fce_next) {
13faa91230bde46da937bf33010b9accc5bdeb59sd ddi_fmcompare_t compare_func;
67d4b2f88b8e27bb035d67a046d5aad7db3bfc71gk
67d4b2f88b8e27bb035d67a046d5aad7db3bfc71gk /*
67d4b2f88b8e27bb035d67a046d5aad7db3bfc71gk * Compare captured error state with handle
67d4b2f88b8e27bb035d67a046d5aad7db3bfc71gk * resources. During the comparison and
67d4b2f88b8e27bb035d67a046d5aad7db3bfc71gk * subsequent error handling, we block
67d4b2f88b8e27bb035d67a046d5aad7db3bfc71gk * attempts to free the cache entry.
67d4b2f88b8e27bb035d67a046d5aad7db3bfc71gk */
67d4b2f88b8e27bb035d67a046d5aad7db3bfc71gk compare_func = (flag == ACC_HANDLE) ?
67d4b2f88b8e27bb035d67a046d5aad7db3bfc71gk i_ddi_fm_acc_err_cf_get((ddi_acc_handle_t)
67d4b2f88b8e27bb035d67a046d5aad7db3bfc71gk fep->fce_resource) :
67d4b2f88b8e27bb035d67a046d5aad7db3bfc71gk i_ddi_fm_dma_err_cf_get((ddi_dma_handle_t)
67d4b2f88b8e27bb035d67a046d5aad7db3bfc71gk fep->fce_resource);
67d4b2f88b8e27bb035d67a046d5aad7db3bfc71gk
67d4b2f88b8e27bb035d67a046d5aad7db3bfc71gk status = compare_func(dip, fep->fce_resource,
67d4b2f88b8e27bb035d67a046d5aad7db3bfc71gk bus_err_state, fep->fce_bus_specific);
67d4b2f88b8e27bb035d67a046d5aad7db3bfc71gk if (status == DDI_FM_UNKNOWN || status == DDI_FM_OK)
67d4b2f88b8e27bb035d67a046d5aad7db3bfc71gk continue;
67d4b2f88b8e27bb035d67a046d5aad7db3bfc71gk
67d4b2f88b8e27bb035d67a046d5aad7db3bfc71gk if (status == DDI_FM_FATAL)
67d4b2f88b8e27bb035d67a046d5aad7db3bfc71gk ++fatal;
13faa91230bde46da937bf33010b9accc5bdeb59sd else if (status == DDI_FM_NONFATAL)
b64bfe7dc77dc5c5561cdcd10c80b0b550701a24Trang Do ++nonfatal;
b64bfe7dc77dc5c5561cdcd10c80b0b550701a24Trang Do
13faa91230bde46da937bf33010b9accc5bdeb59sd /* Set the error for this resource handle */
b64bfe7dc77dc5c5561cdcd10c80b0b550701a24Trang Do if (flag == ACC_HANDLE) {
13faa91230bde46da937bf33010b9accc5bdeb59sd ddi_acc_handle_t ap = fep->fce_resource;
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd i_ddi_fm_acc_err_set(ap, derr->fme_ena, status,
13faa91230bde46da937bf33010b9accc5bdeb59sd DDI_FM_ERR_UNEXPECTED);
13faa91230bde46da937bf33010b9accc5bdeb59sd ddi_fm_acc_err_get(ap, derr, DDI_FME_VERSION);
13faa91230bde46da937bf33010b9accc5bdeb59sd derr->fme_acc_handle = ap;
13faa91230bde46da937bf33010b9accc5bdeb59sd } else {
13faa91230bde46da937bf33010b9accc5bdeb59sd ddi_dma_handle_t dp = fep->fce_resource;
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd i_ddi_fm_dma_err_set(dp, derr->fme_ena, status,
13faa91230bde46da937bf33010b9accc5bdeb59sd DDI_FM_ERR_UNEXPECTED);
b64bfe7dc77dc5c5561cdcd10c80b0b550701a24Trang Do ddi_fm_dma_err_get(dp, derr, DDI_FME_VERSION);
13faa91230bde46da937bf33010b9accc5bdeb59sd derr->fme_dma_handle = dp;
13faa91230bde46da937bf33010b9accc5bdeb59sd }
13faa91230bde46da937bf33010b9accc5bdeb59sd break;
13faa91230bde46da937bf33010b9accc5bdeb59sd }
13faa91230bde46da937bf33010b9accc5bdeb59sd mutex_exit(&fcp->fc_lock);
13faa91230bde46da937bf33010b9accc5bdeb59sd }
13faa91230bde46da937bf33010b9accc5bdeb59sd return (fatal ? DDI_FM_FATAL : nonfatal ? DDI_FM_NONFATAL :
b64bfe7dc77dc5c5561cdcd10c80b0b550701a24Trang Do DDI_FM_UNKNOWN);
13faa91230bde46da937bf33010b9accc5bdeb59sd}
13faa91230bde46da937bf33010b9accc5bdeb59sd
13faa91230bde46da937bf33010b9accc5bdeb59sd/*
13faa91230bde46da937bf33010b9accc5bdeb59sd * Check error state against the handle resource stored in the specified
13faa91230bde46da937bf33010b9accc5bdeb59sd * FM cache. If tdip != NULL, we check only the cache entries for tdip.
13faa91230bde46da937bf33010b9accc5bdeb59sd * The caller must ensure that tdip is valid throughout the call and
b64bfe7dc77dc5c5561cdcd10c80b0b550701a24Trang Do * all FM data structures can be safely accesses.
13faa91230bde46da937bf33010b9accc5bdeb59sd *
13faa91230bde46da937bf33010b9accc5bdeb59sd * If tdip == NULL, we check all children that have registered their
13faa91230bde46da937bf33010b9accc5bdeb59sd * FM_DMA_CHK or FM_ACC_CHK capabilities.
13faa91230bde46da937bf33010b9accc5bdeb59sd *
13faa91230bde46da937bf33010b9accc5bdeb59sd * The following status values may be returned:
13faa91230bde46da937bf33010b9accc5bdeb59sd *
13faa91230bde46da937bf33010b9accc5bdeb59sd * DDI_FM_FATAL - if at least one cache entry comparison yields a
13faa91230bde46da937bf33010b9accc5bdeb59sd * fatal error.
b64bfe7dc77dc5c5561cdcd10c80b0b550701a24Trang Do *
13faa91230bde46da937bf33010b9accc5bdeb59sd * DDI_FM_NONFATAL - if at least one cache entry comparison yields a
13faa91230bde46da937bf33010b9accc5bdeb59sd * non-fatal error and no comparison yields a fatal error.
25351652d920ae27c5a56c199da581033ce763f6Vuong Nguyen *
13faa91230bde46da937bf33010b9accc5bdeb59sd * DDI_FM_UNKNOWN - cache entry comparisons did not yield fatal or
13faa91230bde46da937bf33010b9accc5bdeb59sd * non-fatal errors.
13faa91230bde46da937bf33010b9accc5bdeb59sd *
13faa91230bde46da937bf33010b9accc5bdeb59sd */
13faa91230bde46da937bf33010b9accc5bdeb59sdint
b64bfe7dc77dc5c5561cdcd10c80b0b550701a24Trang Dondi_fmc_error(dev_info_t *dip, dev_info_t *tdip, int flag, uint64_t ena,
13faa91230bde46da937bf33010b9accc5bdeb59sd const void *bus_err_state)
13faa91230bde46da937bf33010b9accc5bdeb59sd{
13faa91230bde46da937bf33010b9accc5bdeb59sd int status, fatal = 0, nonfatal = 0;
b64bfe7dc77dc5c5561cdcd10c80b0b550701a24Trang Do ddi_fm_error_t derr;
13faa91230bde46da937bf33010b9accc5bdeb59sd struct i_ddi_fmhdl *fmhdl;
13faa91230bde46da937bf33010b9accc5bdeb59sd struct i_ddi_fmtgt *tgt;
ASSERT(flag == DMA_HANDLE || flag == ACC_HANDLE);
i_ddi_fm_handler_enter(dip);
fmhdl = DEVI(dip)->devi_fmhdl;
ASSERT(fmhdl);
bzero(&derr, sizeof (ddi_fm_error_t));
derr.fme_version = DDI_FME_VERSION;
derr.fme_flag = DDI_FM_ERR_UNEXPECTED;
derr.fme_ena = ena;
for (tgt = fmhdl->fh_tgts; tgt != NULL; tgt = tgt->ft_next) {
if (tdip != NULL && tdip != tgt->ft_dip)
continue;
/*
* Attempt to find the entry in this childs handle cache
*/
status = ndi_fmc_entry_error(tgt->ft_dip, flag, &derr,
bus_err_state);
if (status == DDI_FM_FATAL)
++fatal;
else if (status == DDI_FM_NONFATAL)
++nonfatal;
else
continue;
/*
* Call our child to process this error.
*/
status = tgt->ft_errhdl->eh_func(tgt->ft_dip, &derr,
tgt->ft_errhdl->eh_impl);
if (status == DDI_FM_FATAL)
++fatal;
else if (status == DDI_FM_NONFATAL)
++nonfatal;
}
i_ddi_fm_handler_exit(dip);
if (fatal)
return (DDI_FM_FATAL);
else if (nonfatal)
return (DDI_FM_NONFATAL);
return (DDI_FM_UNKNOWN);
}
int
ndi_fmc_entry_error_all(dev_info_t *dip, int flag, ddi_fm_error_t *derr)
{
ndi_fmc_t *fcp = NULL;
ndi_fmcentry_t *fep;
struct i_ddi_fmhdl *fmhdl;
int nonfatal = 0;
ASSERT(flag == DMA_HANDLE || flag == ACC_HANDLE);
fmhdl = DEVI(dip)->devi_fmhdl;
ASSERT(fmhdl);
if (flag == DMA_HANDLE && DDI_FM_DMA_ERR_CAP(fmhdl->fh_cap)) {
fcp = fmhdl->fh_dma_cache;
ASSERT(fcp);
} else if (flag == ACC_HANDLE && DDI_FM_ACC_ERR_CAP(fmhdl->fh_cap)) {
fcp = fmhdl->fh_acc_cache;
ASSERT(fcp);
}
if (fcp != NULL) {
/*
* Check active resource entries
*/
mutex_enter(&fcp->fc_lock);
for (fep = fcp->fc_active->fce_next; fep != NULL;
fep = fep->fce_next) {
/* Set the error for this resource handle */
nonfatal++;
if (flag == ACC_HANDLE) {
ddi_acc_handle_t ap = fep->fce_resource;
i_ddi_fm_acc_err_set(ap, derr->fme_ena,
DDI_FM_NONFATAL, DDI_FM_ERR_UNEXPECTED);
ddi_fm_acc_err_get(ap, derr, DDI_FME_VERSION);
derr->fme_acc_handle = ap;
} else {
ddi_dma_handle_t dp = fep->fce_resource;
i_ddi_fm_dma_err_set(dp, derr->fme_ena,
DDI_FM_NONFATAL, DDI_FM_ERR_UNEXPECTED);
ddi_fm_dma_err_get(dp, derr, DDI_FME_VERSION);
derr->fme_dma_handle = dp;
}
}
mutex_exit(&fcp->fc_lock);
}
return (nonfatal ? DDI_FM_NONFATAL : DDI_FM_UNKNOWN);
}
/*
* Dispatch registered error handlers for dip. If tdip != NULL, only
* the error handler (if available) for tdip is invoked. Otherwise,
* all registered error handlers are invoked.
*
* The following status values may be returned:
*
* DDI_FM_FATAL - if at least one error handler returns a
* fatal error.
*
* DDI_FM_NONFATAL - if at least one error handler returns a
* non-fatal error and none returned a fatal error.
*
* DDI_FM_UNKNOWN - if at least one error handler returns
* unknown status and none return fatal or non-fatal.
*
* DDI_FM_OK - if all error handlers return DDI_FM_OK
*/
int
ndi_fm_handler_dispatch(dev_info_t *dip, dev_info_t *tdip,
const ddi_fm_error_t *nerr)
{
int status;
int unknown = 0, fatal = 0, nonfatal = 0;
struct i_ddi_fmhdl *hdl;
struct i_ddi_fmtgt *tgt;
status = DDI_FM_UNKNOWN;
i_ddi_fm_handler_enter(dip);
hdl = DEVI(dip)->devi_fmhdl;
tgt = hdl->fh_tgts;
while (tgt != NULL) {
if (tdip == NULL || tdip == tgt->ft_dip) {
struct i_ddi_errhdl *errhdl;
errhdl = tgt->ft_errhdl;
status = errhdl->eh_func(tgt->ft_dip, nerr,
errhdl->eh_impl);
if (status == DDI_FM_FATAL)
++fatal;
else if (status == DDI_FM_NONFATAL)
++nonfatal;
else if (status == DDI_FM_UNKNOWN)
++unknown;
/* Only interested in one target */
if (tdip != NULL)
break;
}
tgt = tgt->ft_next;
}
i_ddi_fm_handler_exit(dip);
if (fatal)
return (DDI_FM_FATAL);
else if (nonfatal)
return (DDI_FM_NONFATAL);
else if (unknown)
return (DDI_FM_UNKNOWN);
else
return (DDI_FM_OK);
}
/*
* Set error status for specified access or DMA handle
*
* May be called in any context but caller must insure validity of
* handle.
*/
void
ndi_fm_acc_err_set(ddi_acc_handle_t handle, ddi_fm_error_t *dfe)
{
i_ddi_fm_acc_err_set(handle, dfe->fme_ena, dfe->fme_status,
dfe->fme_flag);
}
void
ndi_fm_dma_err_set(ddi_dma_handle_t handle, ddi_fm_error_t *dfe)
{
i_ddi_fm_dma_err_set(handle, dfe->fme_ena, dfe->fme_status,
dfe->fme_flag);
}
/*
* Call parent busop fm initialization routine.
*
* Called during driver attach(1M)
*/
int
i_ndi_busop_fm_init(dev_info_t *dip, int tcap, ddi_iblock_cookie_t *ibc)
{
int pcap;
dev_info_t *pdip = (dev_info_t *)DEVI(dip)->devi_parent;
if (dip == ddi_root_node())
return (ddi_system_fmcap | DDI_FM_EREPORT_CAPABLE);
/* Valid operation for BUSO_REV_6 and above */
if (DEVI(pdip)->devi_ops->devo_bus_ops->busops_rev < BUSO_REV_6)
return (DDI_FM_NOT_CAPABLE);
if (DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_init == NULL)
return (DDI_FM_NOT_CAPABLE);
pcap = (*DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_init)
(pdip, dip, tcap, ibc);
return (pcap);
}
/*
* Call parent busop fm clean-up routine.
*
* Called during driver detach(1M)
*/
void
i_ndi_busop_fm_fini(dev_info_t *dip)
{
dev_info_t *pdip = (dev_info_t *)DEVI(dip)->devi_parent;
if (dip == ddi_root_node())
return;
/* Valid operation for BUSO_REV_6 and above */
if (DEVI(pdip)->devi_ops->devo_bus_ops->busops_rev < BUSO_REV_6)
return;
if (DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_fini == NULL)
return;
(*DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_fini)(pdip, dip);
}
/*
* The following routines provide exclusive access to a nexus resource
*
* These busops may be called in user or kernel driver context.
*/
void
i_ndi_busop_access_enter(dev_info_t *dip, ddi_acc_handle_t handle)
{
dev_info_t *pdip = (dev_info_t *)DEVI(dip)->devi_parent;
/* Valid operation for BUSO_REV_6 and above */
if (DEVI(pdip)->devi_ops->devo_bus_ops->busops_rev < BUSO_REV_6)
return;
if (DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_access_enter == NULL)
return;
(*DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_access_enter)
(pdip, handle);
}
void
i_ndi_busop_access_exit(dev_info_t *dip, ddi_acc_handle_t handle)
{
dev_info_t *pdip = (dev_info_t *)DEVI(dip)->devi_parent;
/* Valid operation for BUSO_REV_6 and above */
if (DEVI(pdip)->devi_ops->devo_bus_ops->busops_rev < BUSO_REV_6)
return;
if (DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_access_exit == NULL)
return;
(*DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_access_exit)(pdip, handle);
}