#include <sys/types.h>
#include <sys/sunddi.h>
#include <sys/sunndi.h>
#include <sys/kmem.h>
#include <sys/nvpair.h>
#include <sys/fm/protocol.h>
#include <sys/ndifm.h>
#include <sys/ddifm.h>
#include <sys/ddi_impldefs.h>
#include <sys/ddi_isa.h>
#include <sys/spl.h>
#include <sys/varargs.h>
#include <sys/systm.h>
#include <sys/disp.h>
#include <sys/atomic.h>
#include <sys/errorq_impl.h>
#include <sys/kobj.h>
#include <sys/fm/util.h>
#include <sys/fm/io/ddi.h>
#define ERPT_CLASS_SZ sizeof (DDI_IO_CLASS) + sizeof (FM_EREPORT_CLASS) + \
DDI_MAX_ERPT_CLASS + 2
int default_dmacache_sz = DEFAULT_DMACACHE_SZ;
int default_acccache_sz = DEFAULT_ACCCACHE_SZ;
int ddi_system_fmcap = 0;
static struct i_ddi_fmkstat ddifm_kstat_template = {
{"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 }
};
void
ddi_fm_service_impact(dev_info_t *dip, int svc_impact)
{
uint64_t ena;
char buf[FM_MAX_CLASS];
ena = fm_ena_generate(0, FM_ENA_FMT1);
mutex_enter(&(DEVI(dip)->devi_lock));
if (!DEVI_IS_DEVICE_OFFLINE(dip)) {
switch (svc_impact) {
case DDI_SERVICE_LOST:
DEVI_SET_DEVICE_DOWN(dip);
(void) snprintf(buf, FM_MAX_CLASS, "%s.%s",
DDI_FM_SERVICE_IMPACT, DDI_FM_SERVICE_LOST);
ddi_fm_ereport_post(dip, buf, ena, DDI_NOSLEEP,
FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0,
NULL);
break;
case DDI_SERVICE_DEGRADED:
DEVI_SET_DEVICE_DEGRADED(dip);
if (DEVI_IS_DEVICE_DEGRADED(dip)) {
(void) snprintf(buf, FM_MAX_CLASS, "%s.%s",
DDI_FM_SERVICE_IMPACT,
DDI_FM_SERVICE_DEGRADED);
ddi_fm_ereport_post(dip, buf, ena, DDI_NOSLEEP,
FM_VERSION, DATA_TYPE_UINT8,
FM_EREPORT_VERS0, NULL);
} else if (DEVI_IS_DEVICE_DOWN(dip)) {
(void) snprintf(buf, FM_MAX_CLASS, "%s.%s",
DDI_FM_SERVICE_IMPACT,
DDI_FM_SERVICE_LOST);
ddi_fm_ereport_post(dip, buf, ena, DDI_NOSLEEP,
FM_VERSION, DATA_TYPE_UINT8,
FM_EREPORT_VERS0, NULL);
}
break;
case DDI_SERVICE_RESTORED:
DEVI_SET_DEVICE_UP(dip);
(void) snprintf(buf, FM_MAX_CLASS, "%s.%s",
DDI_FM_SERVICE_IMPACT, DDI_FM_SERVICE_RESTORED);
ddi_fm_ereport_post(dip, buf, ena, DDI_NOSLEEP,
FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0,
NULL);
break;
case DDI_SERVICE_UNAFFECTED:
(void) snprintf(buf, FM_MAX_CLASS, "%s.%s",
DDI_FM_SERVICE_IMPACT, DDI_FM_SERVICE_UNAFFECTED);
ddi_fm_ereport_post(dip, buf, ena, DDI_NOSLEEP,
FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0,
NULL);
break;
default:
break;
}
}
mutex_exit(&(DEVI(dip)->devi_lock));
}
void
i_ddi_drv_ereport_post(dev_info_t *dip, const char *error_class,
nvlist_t *errp, int sflag)
{
int i;
int depth;
char classp[DDI_DVR_MAX_CLASS];
caddr_t stkp;
char *buf;
char **stkpp;
char *sym;
pc_t stack[DDI_FM_STKDEPTH];
ulong_t off;
dev_info_t *root_dip = ddi_root_node();
if (!DDI_FM_EREPORT_CAP(ddi_fm_capable(root_dip)))
return;
(void) snprintf(classp, DDI_DVR_MAX_CLASS, "%s%s", DVR_ERPT,
error_class);
if (sflag == DDI_SLEEP) {
depth = getpcstack(stack, DDI_FM_STKDEPTH);
stkpp = (char **)kmem_alloc(depth * sizeof (char *), KM_SLEEP);
buf = kmem_alloc(depth * DDI_FM_SYM_SZ, KM_SLEEP);
stkp = buf;
for (i = 0; i < depth; ++i) {
sym = kobj_getsymname(stack[i], &off);
(void) snprintf(stkp, DDI_FM_SYM_SZ,
"\t%s+%lx\n", sym ? sym : "?", off);
stkpp[i] = stkp;
stkp += DDI_FM_SYM_SZ;
}
if (errp)
ddi_fm_ereport_post(root_dip,
classp, fm_ena_generate(0, FM_ENA_FMT1), sflag,
FM_VERSION, DATA_TYPE_UINT8, 0,
DVR_NAME, DATA_TYPE_STRING, ddi_driver_name(dip),
DVR_STACK_DEPTH, DATA_TYPE_UINT32, depth,
DVR_STACK, DATA_TYPE_STRING_ARRAY, depth, stkpp,
DVR_ERR_SPECIFIC, DATA_TYPE_NVLIST, errp, NULL);
else
ddi_fm_ereport_post(root_dip,
classp, fm_ena_generate(0, FM_ENA_FMT1), sflag,
FM_VERSION, DATA_TYPE_UINT8, 0,
DVR_NAME, DATA_TYPE_STRING, ddi_driver_name(dip),
DVR_STACK_DEPTH, DATA_TYPE_UINT32, depth,
DVR_STACK, DATA_TYPE_STRING_ARRAY, depth, stkpp,
NULL);
kmem_free(stkpp, depth * sizeof (char *));
kmem_free(buf, depth * DDI_FM_SYM_SZ);
} else {
if (errp)
ddi_fm_ereport_post(root_dip,
classp, fm_ena_generate(0, FM_ENA_FMT1), sflag,
FM_VERSION, DATA_TYPE_UINT8, 0,
DVR_NAME, DATA_TYPE_STRING, ddi_driver_name(dip),
DVR_ERR_SPECIFIC, DATA_TYPE_NVLIST, errp, NULL);
else
ddi_fm_ereport_post(root_dip,
classp, fm_ena_generate(0, FM_ENA_FMT1), sflag,
FM_VERSION, DATA_TYPE_UINT8, 0,
DVR_NAME, DATA_TYPE_STRING, ddi_driver_name(dip),
NULL);
}
}
void
fm_dev_ereport_postv(dev_info_t *dip, dev_info_t *eqdip,
const char *devpath, const char *minor_name, const char *devid,
const char *tpl0, const char *error_class, uint64_t ena, int sflag,
nvlist_t *pl, va_list ap)
{
nv_alloc_t *nva = NULL;
struct i_ddi_fmhdl *fmhdl = NULL;
errorq_elem_t *eqep;
nvlist_t *ereport = NULL;
nvlist_t *detector = NULL;
char *name;
data_type_t type;
uint8_t version;
char class[ERPT_CLASS_SZ];
char path[MAXPATHLEN];
ASSERT(ap != NULL);
ASSERT(dip && eqdip && error_class);
if (!DDI_FM_EREPORT_CAP(ddi_fm_capable(eqdip)))
goto err;
if ((sflag == DDI_SLEEP) && !panicstr) {
if (servicing_interrupt()) {
i_ddi_drv_ereport_post(dip, DVR_ECONTEXT, NULL, sflag);
goto err;
}
if ((ereport = fm_nvlist_create(NULL)) == NULL)
goto err;
ASSERT(nva == NULL);
} else {
fmhdl = DEVI(eqdip)->devi_fmhdl;
ASSERT(fmhdl);
eqep = errorq_reserve(fmhdl->fh_errorq);
if (eqep == NULL)
goto err;
ereport = errorq_elem_nvl(fmhdl->fh_errorq, eqep);
nva = errorq_elem_nva(fmhdl->fh_errorq, eqep);
ASSERT(nva);
}
ASSERT(ereport);
name = va_arg(ap, char *);
type = va_arg(ap, data_type_t);
version = va_arg(ap, uint_t);
if ((strcmp(name, FM_VERSION) != 0) || (type != DATA_TYPE_UINT8)) {
i_ddi_drv_ereport_post(dip, DVR_EVER, NULL, sflag);
goto err;
}
(void) snprintf(class, ERPT_CLASS_SZ, "%s.%s",
DDI_IO_CLASS, error_class);
if (ena == 0)
ena = fm_ena_generate(0, FM_ENA_FMT1);
if (devpath) {
(void) strlcpy(path, devpath, sizeof (path));
} else {
if (dip == ddi_root_node())
(void) strcpy(path, "/");
else
(void) ddi_pathname(dip, path);
}
if (minor_name) {
(void) strlcat(path, ":", sizeof (path));
(void) strlcat(path, minor_name, sizeof (path));
}
detector = fm_nvlist_create(nva);
fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL, path,
devid, tpl0);
fm_ereport_set(ereport, version, class, ena, detector, NULL);
if (pl)
(void) nvlist_merge(ereport, pl, 0);
name = va_arg(ap, char *);
(void) i_fm_payload_set(ereport, name, ap);
if (nva)
errorq_commit(fmhdl->fh_errorq, eqep, ERRORQ_ASYNC);
else
fm_ereport_post(ereport, EVCH_SLEEP);
goto out;
err: if (fmhdl)
atomic_inc_64(&fmhdl->fh_kstat.fek_erpt_dropped.value.ui64);
out: if (ereport && (nva == NULL))
fm_nvlist_destroy(ereport, FM_NVA_FREE);
if (detector && (nva == NULL))
fm_nvlist_destroy(detector, FM_NVA_FREE);
}
void
ddi_fm_ereport_post(dev_info_t *dip,
const char *error_class, uint64_t ena, int sflag, ...)
{
va_list ap;
ASSERT(dip && error_class);
va_start(ap, sflag);
fm_dev_ereport_postv(dip, dip, NULL, NULL, NULL, NULL,
error_class, ena, sflag, NULL, ap);
va_end(ap);
}
void
ndi_fm_ereport_post(dev_info_t *dip,
const char *error_class, uint64_t ena, int sflag, ...)
{
va_list ap;
ASSERT(dip && error_class && (sflag == DDI_SLEEP));
va_start(ap, sflag);
fm_dev_ereport_postv(dip, ddi_get_parent(dip), NULL, NULL, NULL, NULL,
error_class, ena, sflag, NULL, ap);
va_end(ap);
}
void
i_ddi_fm_handler_enter(dev_info_t *dip)
{
struct i_ddi_fmhdl *hdl = DEVI(dip)->devi_fmhdl;
mutex_enter(&hdl->fh_lock);
hdl->fh_lock_owner = curthread;
}
void
i_ddi_fm_handler_exit(dev_info_t *dip)
{
struct i_ddi_fmhdl *hdl = DEVI(dip)->devi_fmhdl;
hdl->fh_lock_owner = NULL;
mutex_exit(&hdl->fh_lock);
}
boolean_t
i_ddi_fm_handler_owned(dev_info_t *dip)
{
struct i_ddi_fmhdl *hdl = DEVI(dip)->devi_fmhdl;
return (hdl->fh_lock_owner == curthread);
}
void
ddi_fm_handler_register(dev_info_t *dip, ddi_err_func_t handler,
void *impl_data)
{
dev_info_t *pdip;
struct i_ddi_fmhdl *pfmhdl;
struct i_ddi_errhdl *new_eh;
struct i_ddi_fmtgt *tgt;
if (servicing_interrupt()) {
i_ddi_drv_ereport_post(dip, DVR_ECONTEXT, NULL, DDI_NOSLEEP);
return;
}
if (dip == ddi_root_node())
pdip = dip;
else
pdip = (dev_info_t *)DEVI(dip)->devi_parent;
ASSERT(pdip);
if (!(DDI_FM_ERRCB_CAP(ddi_fm_capable(dip)) &&
DDI_FM_ERRCB_CAP(ddi_fm_capable(pdip)))) {
i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_SLEEP);
return;
}
new_eh = kmem_zalloc(sizeof (struct i_ddi_errhdl), KM_SLEEP);
new_eh->eh_func = handler;
new_eh->eh_impl = impl_data;
tgt = kmem_alloc(sizeof (struct i_ddi_fmtgt), KM_SLEEP);
tgt->ft_dip = dip;
tgt->ft_errhdl = new_eh;
i_ddi_fm_handler_enter(pdip);
pfmhdl = DEVI(pdip)->devi_fmhdl;
ASSERT(pfmhdl);
tgt->ft_next = pfmhdl->fh_tgts;
pfmhdl->fh_tgts = tgt;
i_ddi_fm_handler_exit(pdip);
}
void
ddi_fm_handler_unregister(dev_info_t *dip)
{
dev_info_t *pdip;
struct i_ddi_fmhdl *pfmhdl;
struct i_ddi_fmtgt *tgt, **ptgt;
if (servicing_interrupt()) {
i_ddi_drv_ereport_post(dip, DVR_ECONTEXT, NULL, DDI_NOSLEEP);
return;
}
if (dip == ddi_root_node())
pdip = dip;
else
pdip = (dev_info_t *)DEVI(dip)->devi_parent;
ASSERT(pdip);
if (!(DDI_FM_ERRCB_CAP(ddi_fm_capable(dip)) &&
DDI_FM_ERRCB_CAP(ddi_fm_capable(pdip)))) {
i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_SLEEP);
return;
}
i_ddi_fm_handler_enter(pdip);
pfmhdl = DEVI(pdip)->devi_fmhdl;
ASSERT(pfmhdl);
ptgt = &pfmhdl->fh_tgts;
for (tgt = pfmhdl->fh_tgts; tgt != NULL; tgt = tgt->ft_next) {
if (dip == tgt->ft_dip) {
*ptgt = tgt->ft_next;
kmem_free(tgt->ft_errhdl, sizeof (struct i_ddi_errhdl));
kmem_free(tgt, sizeof (struct i_ddi_fmtgt));
break;
}
ptgt = &tgt->ft_next;
}
i_ddi_fm_handler_exit(pdip);
}
void
ddi_fm_init(dev_info_t *dip, int *fmcap, ddi_iblock_cookie_t *ibcp)
{
struct dev_info *devi = DEVI(dip);
struct i_ddi_fmhdl *fmhdl;
ddi_iblock_cookie_t ibc;
int pcap, newcap = DDI_FM_NOT_CAPABLE;
if (!DEVI_IS_ATTACHING(dip)) {
i_ddi_drv_ereport_post(dip, DVR_ECONTEXT, NULL, DDI_NOSLEEP);
*fmcap = DDI_FM_NOT_CAPABLE;
return;
}
if (DDI_FM_DEFAULT_CAP(*fmcap))
return;
if (dip != ddi_root_node()) {
ibc = (ddi_iblock_cookie_t)ipltospl(FM_ERR_PIL);
pcap = i_ndi_busop_fm_init(dip, *fmcap, &ibc);
} else {
pcap = *fmcap;
ibc = *ibcp;
}
fmhdl = kmem_zalloc(sizeof (struct i_ddi_fmhdl), KM_SLEEP);
if ((fmhdl->fh_ksp = kstat_create((char *)ddi_driver_name(dip),
ddi_get_instance(dip), "fm", "misc",
KSTAT_TYPE_NAMED, sizeof (struct i_ddi_fmkstat) /
sizeof (kstat_named_t), KSTAT_FLAG_VIRTUAL)) == NULL) {
mutex_destroy(&fmhdl->fh_lock);
kmem_free(fmhdl, sizeof (struct i_ddi_fmhdl));
*fmcap = DDI_FM_NOT_CAPABLE;
return;
}
bcopy(&ddifm_kstat_template, &fmhdl->fh_kstat,
sizeof (struct i_ddi_fmkstat));
fmhdl->fh_ksp->ks_data = &fmhdl->fh_kstat;
fmhdl->fh_ksp->ks_private = fmhdl;
kstat_install(fmhdl->fh_ksp);
fmhdl->fh_dma_cache = NULL;
fmhdl->fh_acc_cache = NULL;
fmhdl->fh_tgts = NULL;
fmhdl->fh_dip = dip;
fmhdl->fh_ibc = ibc;
mutex_init(&fmhdl->fh_lock, NULL, MUTEX_DRIVER, fmhdl->fh_ibc);
devi->devi_fmhdl = fmhdl;
if (DDI_FM_EREPORT_CAP(*fmcap) && DDI_FM_EREPORT_CAP(pcap)) {
fmhdl->fh_errorq = ereport_errorq;
if (ddi_getprop(DDI_DEV_T_NONE, dip, DDI_PROP_DONTPASS,
"fm-ereport-capable", 0) == 0)
(void) ddi_prop_create(DDI_DEV_T_NONE, dip,
DDI_PROP_CANSLEEP, "fm-ereport-capable", NULL, 0);
newcap |= DDI_FM_EREPORT_CAPABLE;
}
if (DDI_FM_ERRCB_CAP(*fmcap) && DDI_FM_ERRCB_CAP(pcap)) {
if (ddi_getprop(DDI_DEV_T_NONE, dip, DDI_PROP_DONTPASS,
"fm-errcb-capable", 0) == 0)
(void) ddi_prop_create(DDI_DEV_T_NONE, dip,
DDI_PROP_CANSLEEP, "fm-errcb-capable", NULL, 0);
newcap |= DDI_FM_ERRCB_CAPABLE;
}
if (DDI_FM_DMA_ERR_CAP(*fmcap) && DDI_FM_DMA_ERR_CAP(pcap)) {
i_ndi_fmc_create(&fmhdl->fh_dma_cache, 2, ibc);
if (ddi_getprop(DDI_DEV_T_NONE, dip, DDI_PROP_DONTPASS,
"fm-dmachk-capable", 0) == 0)
(void) ddi_prop_create(DDI_DEV_T_NONE, dip,
DDI_PROP_CANSLEEP, "fm-dmachk-capable", NULL, 0);
newcap |= DDI_FM_DMACHK_CAPABLE;
}
if (DDI_FM_ACC_ERR_CAP(*fmcap) && DDI_FM_ACC_ERR_CAP(pcap)) {
i_ndi_fmc_create(&fmhdl->fh_acc_cache, 2, ibc);
if (ddi_getprop(DDI_DEV_T_NONE, dip, DDI_PROP_DONTPASS,
"fm-accchk-capable", 0) == 0)
(void) ddi_prop_create(DDI_DEV_T_NONE, dip,
DDI_PROP_CANSLEEP, "fm-accchk-capable", NULL, 0);
newcap |= DDI_FM_ACCCHK_CAPABLE;
}
fmhdl->fh_cap = newcap;
*fmcap = newcap;
if (ibcp != NULL)
*ibcp = ibc;
}
void
ddi_fm_fini(dev_info_t *dip)
{
struct i_ddi_fmhdl *fmhdl = DEVI(dip)->devi_fmhdl;
ASSERT(fmhdl);
if (!(DEVI_IS_DETACHING(dip) || DEVI_IS_ATTACHING(dip))) {
i_ddi_drv_ereport_post(dip, DVR_ECONTEXT, NULL, DDI_NOSLEEP);
return;
}
kstat_delete(fmhdl->fh_ksp);
if (DDI_FM_EREPORT_CAP(fmhdl->fh_cap)) {
(void) ddi_prop_remove(DDI_DEV_T_NONE, dip,
"fm-ereport-capable");
}
if (dip != ddi_root_node()) {
if (DDI_FM_ERRCB_CAP(fmhdl->fh_cap)) {
ddi_fm_handler_unregister(dip);
(void) ddi_prop_remove(DDI_DEV_T_NONE, dip,
"fm-errcb-capable");
}
if (DDI_FM_DMA_ERR_CAP(fmhdl->fh_cap) ||
DDI_FM_ACC_ERR_CAP(fmhdl->fh_cap)) {
if (fmhdl->fh_dma_cache != NULL) {
i_ndi_fmc_destroy(fmhdl->fh_dma_cache);
(void) ddi_prop_remove(DDI_DEV_T_NONE, dip,
"fm-dmachk-capable");
}
if (fmhdl->fh_acc_cache != NULL) {
i_ndi_fmc_destroy(fmhdl->fh_acc_cache);
(void) ddi_prop_remove(DDI_DEV_T_NONE, dip,
"fm-accachk-capable");
}
}
i_ndi_busop_fm_fini(dip);
}
kmem_free(fmhdl, sizeof (struct i_ddi_fmhdl));
DEVI(dip)->devi_fmhdl = NULL;
}
int
ddi_fm_capable(dev_info_t *dip)
{
struct i_ddi_fmhdl *fmhdl = DEVI(dip)->devi_fmhdl;
if (fmhdl == NULL)
return (DDI_FM_NOT_CAPABLE);
return (fmhdl->fh_cap);
}
static void
ddi_fm_acc_err_get_fail(ddi_acc_handle_t handle)
{
ddi_acc_hdl_t *hp = impl_acc_hdl_get(handle);
i_ddi_drv_ereport_post(hp->ah_dip, DVR_EVER, NULL, DDI_NOSLEEP);
cmn_err(CE_PANIC, "ddi_fm_acc_err_get: Invalid driver version\n");
}
void
ddi_fm_acc_err_get(ddi_acc_handle_t handle, ddi_fm_error_t *de, int version)
{
ndi_err_t *errp;
if (handle == NULL)
return;
if (version != DDI_FME_VER0 && version != DDI_FME_VER1) {
ddi_fm_acc_err_get_fail(handle);
return;
}
errp = ((ddi_acc_impl_t *)handle)->ahi_err;
if (errp->err_status == DDI_FM_OK) {
if (de->fme_status != DDI_FM_OK)
de->fme_status = DDI_FM_OK;
return;
}
de->fme_status = errp->err_status;
de->fme_ena = errp->err_ena;
de->fme_flag = errp->err_expected;
de->fme_acc_handle = handle;
}
void
ddi_fm_dma_err_get_fail(ddi_dma_handle_t handle)
{
i_ddi_drv_ereport_post(((ddi_dma_impl_t *)handle)->dmai_rdip,
DVR_EVER, NULL, DDI_NOSLEEP);
cmn_err(CE_PANIC, "ddi_fm_dma_err_get: Invalid driver version\n");
}
void
ddi_fm_dma_err_get(ddi_dma_handle_t handle, ddi_fm_error_t *de, int version)
{
ndi_err_t *errp;
if (handle == NULL)
return;
if (version != DDI_FME_VER0 && version != DDI_FME_VER1) {
ddi_fm_dma_err_get_fail(handle);
return;
}
errp = &((ddi_dma_impl_t *)handle)->dmai_error;
if (errp->err_status == DDI_FM_OK) {
if (de->fme_status != DDI_FM_OK)
de->fme_status = DDI_FM_OK;
return;
}
de->fme_status = errp->err_status;
de->fme_ena = errp->err_ena;
de->fme_flag = errp->err_expected;
de->fme_dma_handle = handle;
}
void
ddi_fm_acc_err_clear_fail(ddi_acc_handle_t handle)
{
ddi_acc_hdl_t *hp = impl_acc_hdl_get(handle);
i_ddi_drv_ereport_post(hp->ah_dip, DVR_EVER, NULL, DDI_NOSLEEP);
cmn_err(CE_PANIC, "ddi_fm_acc_err_clear: Invalid driver version\n");
}
void
ddi_fm_acc_err_clear(ddi_acc_handle_t handle, int version)
{
ndi_err_t *errp;
if (handle == NULL)
return;
if (version != DDI_FME_VER0 && version != DDI_FME_VER1) {
ddi_fm_acc_err_clear_fail(handle);
return;
}
errp = ((ddi_acc_impl_t *)handle)->ahi_err;
errp->err_status = DDI_FM_OK;
errp->err_ena = 0;
errp->err_expected = DDI_FM_ERR_UNEXPECTED;
}
void
ddi_fm_dma_err_clear_fail(ddi_dma_handle_t handle)
{
i_ddi_drv_ereport_post(((ddi_dma_impl_t *)handle)->dmai_rdip,
DVR_EVER, NULL, DDI_NOSLEEP);
cmn_err(CE_PANIC, "ddi_fm_dma_err_clear: Invalid driver version\n");
}
void
ddi_fm_dma_err_clear(ddi_dma_handle_t handle, int version)
{
ndi_err_t *errp;
if (handle == NULL)
return;
if (version != DDI_FME_VER0 && version != DDI_FME_VER1) {
ddi_fm_dma_err_clear_fail(handle);
return;
}
errp = &((ddi_dma_impl_t *)handle)->dmai_error;
errp->err_status = DDI_FM_OK;
errp->err_ena = 0;
errp->err_expected = DDI_FM_ERR_UNEXPECTED;
}
void
i_ddi_fm_acc_err_set(ddi_acc_handle_t handle, uint64_t ena, int status,
int flag)
{
ddi_acc_hdl_t *hdlp = impl_acc_hdl_get(handle);
ddi_acc_impl_t *i_hdlp = (ddi_acc_impl_t *)handle;
struct i_ddi_fmhdl *fmhdl = DEVI(hdlp->ah_dip)->devi_fmhdl;
i_hdlp->ahi_err->err_ena = ena;
i_hdlp->ahi_err->err_status = status;
i_hdlp->ahi_err->err_expected = flag;
atomic_inc_64(&fmhdl->fh_kstat.fek_acc_err.value.ui64);
}
void
i_ddi_fm_dma_err_set(ddi_dma_handle_t handle, uint64_t ena, int status,
int flag)
{
ddi_dma_impl_t *hdlp = (ddi_dma_impl_t *)handle;
struct i_ddi_fmhdl *fmhdl = DEVI(hdlp->dmai_rdip)->devi_fmhdl;
hdlp->dmai_error.err_ena = ena;
hdlp->dmai_error.err_status = status;
hdlp->dmai_error.err_expected = flag;
atomic_inc_64(&fmhdl->fh_kstat.fek_dma_err.value.ui64);
}
ddi_fmcompare_t
i_ddi_fm_acc_err_cf_get(ddi_acc_handle_t handle)
{
ddi_acc_impl_t *i_hdlp = (ddi_acc_impl_t *)handle;
return (i_hdlp->ahi_err->err_cf);
}
ddi_fmcompare_t
i_ddi_fm_dma_err_cf_get(ddi_dma_handle_t handle)
{
ddi_dma_impl_t *hdlp = (ddi_dma_impl_t *)handle;
return (hdlp->dmai_error.err_cf);
}