编写设备驱动程序

错误处理

本节介绍如何使用 I/O 故障服务 API 来处理驱动程序内的错误。本节讨论驱动程序应如何指示和初始化其故障管理功能、生成错误报告以及注册驱动程序的错误处理程序例程。

摘录内容来自源代码示例,这些示例演示如何从 Broadcom 1Gb NIC 驱动程序 bge 中使用 I/O 故障服务 API。以这些示例为模型,了解如何将故障管理功能集成到您自己的驱动程序中。请按照以下步骤研究完整的 bge 驱动程序代码:

被指示提供 FMA 错误报告遥测的驱动程序将检测错误,并确定这些错误对驱动程序所提供服务的影响。检测到错误后,驱动程序应确定其服务受影响的时间以及程度。

I/O 驱动程序必须立即响应检测到的错误。相应的响应包括:

驱动程序检测到的错误以 ereport 的形式传递给故障管理守护进程。ereport 是由 FMA 事件协议定义的结构化事件。该事件协议是一组常用数据字段的规范,除了可疑故障列表外,这些字段还必须用于描述所有可能的错误和故障事件。Ereport 被收集为错误遥测流,并分发给诊断引擎。

声明故障管理功能

强化的设备驱动程序必须向 I/O 故障管理框架声明其故障管理功能。使用 ddi_fm_init(9F) 函数声明驱动程序的故障管理功能。

void ddi_fm_init(dev_info_t *dip, int *fmcap, ddi_iblock_cookie_t *ibcp)

可从驱动程序 attach(9E)detach(9E) 入口点的内核上下文中调用 ddi_fm_init() 函数。通常会从 attach() 入口点调用 ddi_fm_init() 函数。ddi_fm_init() 函数根据 fmcap 来分配和初始化资源。fmcap 参数必须设置为以下故障管理功能的按位或:

强化的叶驱动程序通常设置上述所有功能。但是,如果父结点不能支持任何一项请求的功能,则关联的位会被清除并按此情况返回给驱动程序。在从 ddi_fm_init(9F) 返回之前,I/O 故障服务框架会创建一组故障管理功能属性: fm-ereport-capablefm-accchk-capablefm-dmachk-capablefm-errcb-capable。可使用 prtconf(1M) 命令来观察当前支持的故障管理功能级别。

为了使驱动程序支持故障管理功能的管理选择,请导出故障管理功能级别属性并将其设置为上面 driver.conf(4) 文件中描述的值。在使用所需功能列表调用 ddi_fm_init() 之前,必须设置并读取 fm-capable 属性。

来自 bge 驱动程序的以下示例显示了 bge_fm_init() 函数,该函数调用 ddi_fm_init(9F) 函数的包装。可在 bge_attach() 函数中调用 bge_fm_init() 函数。

static void
bge_fm_init(bge_t *bgep)
{
        ddi_iblock_cookie_t iblk;

        /* Only register with IO Fault Services if we have some capability */
        if (bgep->fm_capabilities) {
                bge_reg_accattr.devacc_attr_access = DDI_FLAGERR_ACC;
                dma_attr.dma_attr_flags = DDI_DMA_FLAGERR;
                /* 
                 * Register capabilities with IO Fault Services
                 */
                ddi_fm_init(bgep->devinfo, &bgep->fm_capabilities, &iblk);
                /*
                 * Initialize pci ereport capabilities if ereport capable
                 */
                if (DDI_FM_EREPORT_CAP(bgep->fm_capabilities) ||
                    DDI_FM_ERRCB_CAP(bgep->fm_capabilities))
                        pci_ereport_setup(bgep->devinfo);
                /*
                 * Register error callback if error callback capable
                 */
                if (DDI_FM_ERRCB_CAP(bgep->fm_capabilities))
                        ddi_fm_handler_register(bgep->devinfo,
                        bge_fm_error_cb, (void*) bgep);
        } else {
                /*
                 * These fields have to be cleared of FMA if there are no
                 * FMA capabilities at runtime.
                 */
                bge_reg_accattr.devacc_attr_access = DDI_DEFAULT_ACC;
                dma_attr.dma_attr_flags = 0;
        }
}

清除故障管理资源

ddi_fm_fini(9F) 函数清除为支持 dip 的故障管理而分配的资源。

void ddi_fm_fini(dev_info_t *dip)

可从驱动程序 attach(9E)detach(9E) 入口点的内核上下文中调用 ddi_fm_fini() 函数。

来自 bge 驱动程序的以下示例显示了 bge_fm_fini() 函数,该函数调用 ddi_fm_fini(9F) 函数的包装。可在 bge_unattach() 函数中调用 bge_fm_fini() 函数,而在 bge_attach()bge_detach() 函数中调用 bge_unattach 函数。

static void
bge_fm_fini(bge_t *bgep)
{
        /* Only unregister FMA capabilities if we registered some */
        if (bgep->fm_capabilities) {
                /*
                 * Release any resources allocated by pci_ereport_setup()
                 */
                if (DDI_FM_EREPORT_CAP(bgep->fm_capabilities) ||
                    DDI_FM_ERRCB_CAP(bgep->fm_capabilities))
                        pci_ereport_teardown(bgep->devinfo);
                /*
                 * Un-register error callback if error callback capable
                 */
                if (DDI_FM_ERRCB_CAP(bgep->fm_capabilities))
                        ddi_fm_handler_unregister(bgep->devinfo);
                /*
                 * Unregister from IO Fault Services
                 */
                ddi_fm_fini(bgep->devinfo);
        }
}

获取故障管理功能位掩码

ddi_fm_capable(9F) 函数返回当前为 dip 设置的功能位掩码。

void ddi_fm_capable(dev_info_t *dip)

报告错误

本节提供有关以下主题的信息:

对错误事件排队

ddi_fm_ereport_post(9F) 函数对 ereport 事件排队,以便传送给故障管理器守护进程 fmd(1M)

void ddi_fm_ereport_post(dev_info_t *dip, 
                         const char *error_class, 
                         uint64_t ena, 
                         int sflag, ...)

sflag 参数指示调用方是否愿意等待系统内存和事件通道资源变为可用。

ENA 指示此错误报告的错误编号关联 (Error Numeric Association, ENA)。ENA 可能已初始化,并且是从其他错误检测软件模块(如总线结点驱动程序)中获得的。如果 ENA 设置为 0,它将被 ddi_fm_ereport_post() 初始化。

名称-值对 (nvpair) 变量参数列表包含非数组 data_type_t 类型的一个或多个名称、类型、值指针 nvpair 元组,或者包含 data_type_t 数组类型的一个或多个名称、类型、元素数、值指针元组。nvpair 元组补足诊断所需要的 ereport 事件有效负荷。参数列表的结尾由 NULL 指定。

报告标准 I/O 控制器错误中介绍的用于 I/O 控制器的 ereport 类名和有效负荷可适用于 error_class。可以定义其他 ereport 类名和有效负荷,但必须在 Sun 事件注册表中进行注册,并伴有特定于驱动程序的诊断引擎软件或 Eversholt 故障树 (Eversholt fault tree, eft) 规则。有关 Sun 事件注册表和 Eversholt 故障树规则的更多信息,请参见 OpenSolaris 项目故障管理社区

void
bge_fm_ereport(bge_t *bgep, char *detail)
{
        uint64_t ena;
        char buf[FM_MAX_CLASS];
        (void) snprintf(buf, FM_MAX_CLASS, "%s.%s", DDI_FM_DEVICE, detail);
        ena = fm_ena_generate(0, FM_ENA_FMT1);
        if (DDI_FM_EREPORT_CAP(bgep->fm_capabilities)) {
                ddi_fm_ereport_post(bgep->devinfo, buf, ena, DDI_NOSLEEP,
                    FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0, NULL);
        }
}

检测和报告与 PCI 相关的错误

使用 pci_ereport_post(9F) 时,会自动检测和报告与 PCI(包括 PCI、PCI-X 和 PCI-E)相关的错误。

void pci_ereport_post(dev_info_t *dip, ddi_fm_error_t *derr, uint16_t *xx_status)

驱动程序不需要为 PCI 本地总线配置状态寄存器中发生的错误生成特定于驱动程序的 ereport。pci_ereport_post() 函数可以报告数据奇偶校验错误、主机异常中止、目标异常中止、发出信号的系统错误等。

如果 pci_ereport_post() 将由驱动程序使用,则此前 pci_ereport_setup(9F) 必须已经在驱动程序的 attach(9E) 例程中调用,pci_ereport_teardown(9F) 随后必须在驱动程序的 detach(9E) 例程中调用。

下面的 bge 代码样例显示了从驱动程序的错误处理程序中调用 pci_ereport_post() 函数的 bge 驱动程序。另请参见注册错误处理程序

/*
 * The I/O fault service error handling callback function
 */
/*ARGSUSED*/
static int
bge_fm_error_cb(dev_info_t *dip, ddi_fm_error_t *err, const void *impl_data)
{
     /*
      * as the driver can always deal with an error 
      * in any dma or access handle, we can just return 
      * the fme_status value.
      */
     pci_ereport_post(dip, err, NULL);
     return (err->fme_status);
}

报告标准 I/O 控制器错误

针对 I/O 控制器的常见错误定义了一组标准的设备 ereport。只要检测到本节中所述的错误症状之一,便应生成这些 ereport。

本节中所述的 ereport 将分发给 eft 诊断引擎以进行诊断,eft 诊断引擎使用一组常用的标准规则来诊断这些 ereport。设备驱动程序检测的其他任何错误都必须在 Sun 事件注册表中定义为 ereport 事件,并必须伴有特定于设备的诊断软件或 eft 规则。

DDI_FM_DEVICE_INVAL_STATE

驱动程序已检测到设备处于无效状态。

当驱动程序检测到所传送或接收的数据看起来无效时,该驱动程序应发布错误。例如,在 bge 代码中,当 bge_chip_reset()bge_receive_ring() 例程检测到无效数据时,这些例程将生成 ereport.io.device.inval_state 错误。

/*
 * The SEND INDEX registers should be reset to zero by the
 * global chip reset; if they're not, there'll be trouble
 * later on.
 */
sx0 = bge_reg_get32(bgep, NIC_DIAG_SEND_INDEX_REG(0));
if (sx0 != 0) {
    BGE_REPORT((bgep, "SEND INDEX - device didn't RESET"));
    bge_fm_ereport(bgep, DDI_FM_DEVICE_INVAL_STATE);
    return (DDI_FAILURE);
}
/* ... */
/*
 * Sync (all) the receive ring descriptors
 * before accepting the packets they describe
 */
DMA_SYNC(rrp->desc, DDI_DMA_SYNC_FORKERNEL);
if (*rrp->prod_index_p >= rrp->desc.nslots) {
    bgep->bge_chip_state = BGE_CHIP_ERROR;
    bge_fm_ereport(bgep, DDI_FM_DEVICE_INVAL_STATE);
    return (NULL);
}
DDI_FM_DEVICE_INTERN_CORR

设备已报告自我纠正的内部错误。例如,设备的内部缓冲区中的硬件已检测到可纠正的 ECC 错误。

bge 驱动程序中未使用此错误标志。有关使用此错误的示例,请参见 OpenSolaris 中的 nxge_fm.c 文件。执行以下步骤来研究 nxge 驱动程序代码:

DDI_FM_DEVICE_INTERN_UNCORR

设备已报告无法纠正的内部错误。例如,设备的内部缓冲区中的硬件已检测到不可纠正的 ECC 错误。

bge 驱动程序中未使用此错误标志。有关使用此错误的示例,请参见 OpenSolaris 中的 nxge_fm.c 文件。

DDI_FM_DEVICE_STALL

驱动程序检测到数据传输已意外停顿。

bge_factotum_stall_check() 例程提供了停顿检测的示例。

dogval = bge_atomic_shl32(&bgep->watchdog, 1);
if (dogval < bge_watchdog_count)
    return (B_FALSE);

BGE_REPORT((bgep, "Tx stall detected, 
watchdog code 0x%x", dogval));
bge_fm_ereport(bgep, DDI_FM_DEVICE_STALL);
return (B_TRUE);
DDI_FM_DEVICE_NO_RESPONSE

设备未对驱动程序命令进行响应。

bge_chip_poll_engine(bge_t *bgep, bge_regno_t regno,
        uint32_t mask, uint32_t val)
{
        uint32_t regval;
        uint32_t n;

        for (n = 200; n; --n) {
                regval = bge_reg_get32(bgep, regno);
                if ((regval & mask) == val)
                        return (B_TRUE);
                drv_usecwait(100);
        }
        bge_fm_ereport(bgep, DDI_FM_DEVICE_NO_RESPONSE);
        return (B_FALSE);
}
DDI_FM_DEVICE_BADINT_LIMIT

设备引发了过多的连续性无效中断。

bge() 驱动程序内的 bge_intr 例程提供了有问题的中断检测的示例。bge_fm_ereport() 函数是 ddi_fm_ereport_post(9F) 函数的包装。请参见对错误事件排队中的 bge_fm_ereport() 示例。

if (bgep->missed_dmas >= bge_dma_miss_limit) {
    /*
     * If this happens multiple times in a row,
     * it means DMA is just not working.  Maybe
     * the chip has failed, or maybe there's a
     * problem on the PCI bus or in the host-PCI
     * bridge (Tomatillo).
     *
     * At all events, we want to stop further
     * interrupts and let the recovery code take
     * over to see whether anything can be done
     * about it ...
     */
    bge_fm_ereport(bgep,
        DDI_FM_DEVICE_BADINT_LIMIT);
    goto chip_stop;
}

服务影响函数

具有故障管理功能的驱动程序必须指示错误是否影响了设备所提供的服务。检测错误并在必要时关闭服务之后,驱动程序应调用 ddi_fm_service_impact(9F) 例程来反映设备实例的当前服务状态。诊断和恢复软件可以使用该服务状态来帮助确定问题或对问题做出反应。

当驱动程序本身检测到错误时以及框架检测到错误并将访问或 DMA 句柄标记为有故障时,均应调用 ddi_fm_service_impact() 例程。

void ddi_fm_service_impact(dev_info_t *dip, int svc_impact)

ddi_fm_service_impact() 接受以下服务影响值 (svc_impact):

DDI_SERVICE_LOST

由于设备故障或软件缺陷,设备提供的服务不可用。

DDI_SERVICE_DEGRADED

驱动程序无法提供正常服务,但驱动程序可以提供部分服务或降级的服务。例如,驱动程序可能必须重复尝试执行操作才能取得成功,或者它至少要以配置的速度运行。

DDI_SERVICE_UNAFFECTED

驱动程序已检测到错误,但设备实例提供的服务不会受到影响。

DDI_SERVICE_RESTORED

设备提供的所有服务都已恢复。

调用 ddi_fm_service_impact() 时会根据服务影响例程的服务影响参数代表驱动程序生成以下 ereport:

在以下 bge 代码中,驱动程序确定由于出现错误,它无法成功地重新开始传送或接收数据包。设备的服务状态转换为 DDI_SERVICE_LOST。

/*
 * All OK, reinitialize hardware and kick off GLD scheduling
 */
mutex_enter(bgep->genlock);
if (bge_restart(bgep, B_TRUE) != DDI_SUCCESS) {
    (void) bge_check_acc_handle(bgep, bgep->cfg_handle);
    (void) bge_check_acc_handle(bgep, bgep->io_handle);
    ddi_fm_service_impact(bgep->devinfo, DDI_SERVICE_LOST);
    mutex_exit(bgep->genlock);
    return (DDI_FAILURE);
}

注 –

不应从已注册的回调例程中调用 ddi_fm_service_impact() 函数。


访问属性结构

DDI_FM_ACCCHK_CAPABLE 设备驱动程序必须设置其访问属性,以指示它能够处理寄存器读取或写入期间发生的程控 I/O (programmed I/O, PIO) 访问错误。应将 ddi_device_acc_attr(9S) 结构中的 devacc_attr_access 字段设置为驱动程序可以检查和处理数据路径错误的系统的指示器。ddi_device_acc_attr 结构包含以下成员:

ushort_t devacc_attr_version;
uchar_t devacc_attr_endian_flags;
uchar_t devacc_attr_dataorder;
uchar_t devacc_attr_access;             /* access error protection */

在到设备或来自设备的数据路径中检测到的错误可由设备驱动程序的一个或多个父结点来处理。

devacc_attr_version 字段必须至少设置为 DDI_DEVICE_ATTR_V1。如果 devacc_attr_version 字段未设置为 DDI_DEVICE_ATTR_V1,则将忽略 devacc_attr_access 字段。

可将 devacc_attr_access 字段设置为以下值:

DDI_DEFAULT_ACC

此标志指示当出现错误时系统将采取缺省操作(如果合适,则进入紧急状态)。DDI_FM_ACCCHK_CAPABLE 驱动程序不能使用此属性。

DDI_FLAGERR_ACC

此标志指示系统将尝试处理与访问句柄关联的错误并从该错误中恢复。驱动程序应使用用于 Solaris 设备驱动程序的防御性编程方法中介绍的技术,并应使用 ddi_fm_acc_err_get(9F) 定期检查错误,之后才能允许数据回传给调用应用程序。

DDI_FLAGERR_ACC 标志可提供:

  • 通过驱动程序回调收到的错误通知

  • 通过 ddi_fm_acc_err_get(9F) 注册的驱动程序回调获得的错误通知

DDI_CAUTIOUS_ACC

DDI_CAUTIOUS_ACC 标志可为驱动程序进行的每个程控 I/O 访问提供高级别的保护。


注 –

使用此标志将对驱动程序的性能造成重大影响。


DDI_CAUTIOUS_ACC 标志指示访问驱动程序可以预见错误。系统尝试尽可能正常地处理与此句柄关联的错误并从该错误中恢复。最终不会生成错误报告,但句柄的 fme_status 标志将设置为 DDI_FM_NONFATAL。此标志在功能上与 ddi_peek(9F)ddi_poke(9F) 等效。

使用 DDI_CAUTIOUS_ACC 可提供:

  • 对总线的独占访问

  • 陷阱 (On trap) 保护-(ddi_peek()ddi_poke()

  • 通过使用 ddi_fm_handler_register(9F) 注册的驱动程序回调获得的错误通知

  • 通过 ddi_fm_acc_err_get(9F) 注册的驱动程序回调获得的错误通知

通常,驱动程序应在代码路径中的适当接合点处检查数据路径错误,以确保数据一致并确保 I/O 软件堆栈中显示正确的错误状态。

DDI_FM_ACCCHK_CAPABLE 设备驱动程序必须将其 devacc_attr_access 字段设置为 DDI_FLAGERR_ACC 或 DDI_CAUTIOUS_ACC。

DMA 属性结构

与访问句柄设置一样,DDI_FM_DMACHK_CAPABLE 设备驱动程序必须将其 ddi_dma_attr(9S) 结构的 dma_attr_flag 字段设置为 DDI_DMA_FLAGERR 标志。系统将尝试从与设置了 DDI_DMA_FLAGERR 的句柄关联的错误中恢复。ddi_dma_attr 结构包含以下成员:

uint_t          dma_attr_version;       /* version number */
uint64_t        dma_attr_addr_lo;       /* low DMA address range */
uint64_t        dma_attr_addr_hi;       /* high DMA address range */
uint64_t        dma_attr_count_max;     /* DMA counter register */
uint64_t        dma_attr_align;         /* DMA address alignment */
uint_t          dma_attr_burstsizes;    /* DMA burstsizes */
uint32_t        dma_attr_minxfer;       /* min effective DMA size */
uint64_t        dma_attr_maxxfer;       /* max DMA xfer size */
uint64_t        dma_attr_seg;           /* segment boundary */
int             dma_attr_sgllen;        /* s/g length */
uint32_t        dma_attr_granular;      /* granularity of device */
uint_t          dma_attr_flags;         /* Bus specific DMA flags */

设置 DDI_DMA_FLAGERR 标志的驱动程序应使用用于 Solaris 设备驱动程序的防御性编程方法中介绍的技术,并且应该在 DMA 事务完成时或者代码路径的重要点处使用 ddi_fm_dma_err_get(9F) 检查数据路径错误。这样可以确保数据一致并且 I/O 软件堆栈中显示正确的错误状态。

使用 DDI_DMA_FLAGERR 可提供:

获取错误状态

如果发生的故障影响到通过句柄映射的资源,则会更新错误状态结构,以反映在总线或 I/O 数据路径中的其他设备驱动程序在处理错误期间捕获的错误信息。

void ddi_fm_dma_err_get(ddi_dma_handle_t handle, ddi_fm_error_t *de, int version)

void ddi_fm_acc_err_get(ddi_acc_handle_t handle, ddi_fm_error_t *de, int version)

ddi_fm_dma_err_get(9F) 和 ddi_fm_acc_err_get(9F) 函数分别为 DMA 或访问句柄返回错误状态。应将版本字段设置为 DDI_FME_VERSION。

访问句柄错误意味着已检测到一种错误,该错误影响到达使用该访问句柄的设备或来自该设备的 PIO 事务。该驱动程序接收到的任何数据(例如,通过最新的 ddi_get8(9F) 调用)均应被视为可能已损坏。发送到设备的任何数据(例如,通过最新的 ddi_put32(9F) 调用)也都可能已损坏,或根本未被接收。然而,基本故障可能是瞬态的,因而驱动程序可以通过调用 ddi_fm_acc_err_clear(9F)、将设备重置为已知状态、重试任何可能出错的事务来尝试进行恢复。

如果指示 DMA 句柄出现错误,则意味着检测到错误已经(或将要)影响设备和当前绑定到句柄(如果句柄当前未绑定,则为最近绑定)的内存之间的 DMA 事务。可能的原因包括 DMA 数据路径中的组件出现故障,或设备尝试进行无效的 DMA 访问。驱动程序通过重试和重新分配内存可能能够继续。应将当前(或以前)绑定到句柄的内存的内容视为不确定的,并应将其释放回系统。一旦绑定或重新绑定句柄,与当前事务关联的故障指示便会丢失,但由于故障可能持续存在,因此将来的 DMA 操作可能不会成功。

清除错误

在句柄检测到错误后,驱动程序希望在无需释放和重新分配句柄的前提下重试请求时,应调用 ddi_fm_acc_err_clear()ddi_fm_dma_err_clear(9F) 例程。

void ddi_fm_acc_err_clear(ddi_acc_handle_t handle, int version)

void ddi_fm_dma_err_clear(ddi_dma_handle_t handle, int version)

注册错误处理程序

当操作系统通过陷阱或错误中断检测到错误时,错误处理活动可能会开始。如果负责处理错误的软件(错误处理程序)无法立即隔离出现故障的 I/O 操作中涉及的设备,它必须尝试在设备树内查找可以执行错误隔离的软件模块。Solaris 设备树提供了向子级传播结点驱动程序错误处理活动的结构化方法,这些子级可能对错误具有更详细的了解,并可捕获错误状态和隔离问题设备。

驱动程序可以使用 I/O 故障服务框架注册错误处理程序回调。错误处理程序应特定于错误的类型以及进行错误检测的子系统。调用驱动程序的错误处理程序例程时,驱动程序必须检查与设备事务关联的任何未解决的错误并生成 ereport 事件。驱动程序还必须在其 ddi_fm_error(9S) 结构中返回错误处理程序状态。例如,如果已经确定系统的完整性受到威胁,则错误处理程序可能采取的最合适的操作是使系统进入紧急状态。

当错误可能与特定的设备实例关联时,父结点驱动程序会调用回调。注册错误处理程序的设备驱动程序必须为 DDI_FM_ERRCB_CAPABLE。

void ddi_fm_handler_register(dev_info_t *dip, ddi_err_func_t handler, void *impl_data)

ddi_fm_handler_register(9F) 例程向 I/O 故障服务框架注册错误处理程序回调。应在驱动程序故障管理初始化 (ddi_fm_init()) 之后在驱动程序的 attach(9E) 入口点中调用 ddi_fm_handler_register() 函数,以便进行回调注册。

错误处理程序回调函数必须执行以下操作:

驱动程序错误处理程序会接收以下内容:

必须在驱动程序的 attach(9E) 或 detach(9E) 入口点的内核上下文中调用 ddi_fm_handler_register()ddi_fm_handler_unregister(9F) 。可以从内核、中断或高级别中断上下文中调用注册的错误处理程序回调。因此,错误处理程序:

设备驱动程序负责:

可在错误处理程序函数内执行这些操作。但是,由于对锁定的限制以及错误处理程序函数并非始终了解故障发生时驱动程序所执行操作的上下文,因此,更通常的做法是,如前所述在驱动程序的正常路径内内联调用 ddi_fm_acc_err_get(9F) 和 ddi_fm_dma_err_get(9F) 之后执行这些操作。

/*
 * The I/O fault service error handling callback function
 */
/*ARGSUSED*/
static int
bge_fm_error_cb(dev_info_t *dip, ddi_fm_error_t *err, const void *impl_data)
{
     /*
      * as the driver can always deal with an error 
      * in any dma or access handle, we can just return 
      * the fme_status value.
      */
     pci_ereport_post(dip, err, NULL);
     return (err->fme_status);
}

故障管理数据和状态结构

驱动程序错误处理回调会被传递一个指向数据结构的指针,该数据结构包含常见的故障管理数据和错误处理状态。

数据结构 ddi_fm_error 包含用于当前错误的 FMA 协议 ENA、错误处理程序回调的状态、错误预期标志以及与父结点检测到的错误关联的任何潜在访问或 DMA 句柄。

fme_ena

此字段通过调用父结点来进行初始化,并可能在达到驱动程序的已注册回调例程之前随着错误处理传播链不断增大。如果驱动程序检测到自身的相关错误,它应在调用 ddi_fm_ereport_post() 之前使此 ENA 增大。

fme_acc_handlefme_dma_handle

如果父级能够将在其级别上检测到的错误与设备驱动程序映射或绑定的句柄相关联,则这些字段中包含有效的访问或 DMA 句柄。

fme_flag

如果调用父级确定错误是由于 DDI_CAUTIOUS_ACC 受保护的操作引起的,fme_flag 将设置为 DDI_FM_ERR_EXPECTED。在这种情况下,fme_acc_handle 有效,并且驱动程序应只检查并报告不与 DDI_CAUTIOUS_ACC 受保护操作关联的错误。否则,fme_flag 将设置为 DDI_FM_ERR_UNEXPECTED,并且驱动程序必须执行完整的错误处理任务。

fme_status

从其错误处理程序回调返回后,驱动程序必须立即将 fme_status 设置为以下值之一:

  • DDI_FM_OK-未检测到任何错误,此设备实例的操作状态一直保持不变。

  • DDI_FM_FATAL-出现错误,并且驱动程序将其视为对系统致命的错误。例如,对 pci_ereport_post(9F) 的调用可能已检测到系统致命错误。在这种情况下,驱动程序应报告驱动程序的上下文中可能存在的任何其他错误信息。

  • DDI_FM_NONFATAL-驱动程序已检测到错误,但不将其视为对系统致命的错误。驱动程序已确定错误,并且已隔离该错误或者确认将隔离该错误。

  • DDI_FM_UNKNOWN-已检测到错误,但驱动程序无法隔离设备或确定错误对系统操作状态的影响。