JavaScript is required to for searching.
跳过导航链接
退出打印视图
编写设备驱动程序     Oracle Solaris 11.1 Information Library (简体中文)
为本文档评分
search filter icon
search icon

文档信息

前言

第 1 部分针对 Oracle Solaris 平台设计设备驱动程序

1.  Oracle Solaris 设备驱动程序概述

2.  Oracle Solaris 内核和设备树

3.  多线程

4.  属性

5.  管理事件和排队任务

6.  驱动程序自动配置

7.  设备访问:程控 I/O

8.  中断处理程序

9.  直接内存访问 (Direct Memory Access, DMA)

DMA 模型

设备 DMA 的类型

总线主控器 DMA

第三方 DMA

第一方 DMA

主机平台 DMA 的类型

DMA 软件组件:句柄、窗口和 Cookie

DMA 操作

执行总线主控器 DMA 传送

执行第一方 DMA 传送

执行第三方 DMA 传送

DMA 特性

ddi_dma_attr 结构

S 总线示例

ISA 总线示例

管理 DMA 资源

对象锁定

分配 DMA 句柄

分配 DMA 资源

设备寄存器结构

DMA 回调示例

确定最大突发流量大小

分配专用 DMA 缓冲区

处理资源分配故障

对 DMA 引擎进行编程

释放 DMA 资源

释放 DMA 句柄

取消 DMA 回调

同步内存对象

高速缓存

ddi_dma_sync() 函数

DMA 窗口

10.  映射设备和内核内存

11.  设备上下文管理

12.  电源管理

13.  强化 Oracle Solaris 驱动程序

14.  分层驱动程序接口 (Layered Driver Interface, LDI)

第 2 部分设计特定种类的设备驱动程序

15.  字符设备驱动程序

16.  块设备驱动程序

17.  SCSI 目标驱动程序

18.  SCSI 主机总线适配器驱动程序

19.  网络设备驱动程序

20.  USB 驱动程序

21.  SR-IOV 驱动程序

第 3 部分生成设备驱动程序

22.  编译、装入、打包和测试驱动程序

23.  调试、测试和调优设备驱动程序

24.  推荐的编码方法

第 4 部分附录

A.  硬件概述

B.  Oracle Solaris DDI/DKI 服务汇总

C.  使设备驱动程序支持 64 位

D.  控制台帧缓存器驱动程序

E.  pci.conf 文件

索引

请告诉我们如何提高我们的文档:
过于简略
不易阅读或难以理解
重要信息缺失
错误的内容
需要翻译的版本
其他
Your rating has been updated
感谢您的反馈!

您的反馈将非常有助于我们提供更好的文档。 您是否愿意参与我们的内容改进并提供进一步的意见?

管理 DMA 资源

本节介绍如何管理 DMA 资源。

对象锁定

为内存对象分配 DMA 资源之前,必须防止该对象移动。否则,在设备尝试向该对象进行写入时,系统会从内存中删除该对象。缺少对象会导致数据传送失败,并且可能损坏系统。防止内存对象在 DMA 传送过程中移动的过程称为锁定对象

以下对象类型不要求显式锁定:

对于其他对象(如用户空间中的缓冲区),必须使用 physio(9F)ddi_umem_lock(9F) 来锁定对象。使用这些函数来锁定对象通常在字符设备驱动程序的 read(9E)write(9E) 例程中执行。有关示例,请参见数据传输方法

分配 DMA 句柄

DMA 句柄是一个不透明的对象,用作对后续分配的 DMA 资源的引用。DMA 句柄通常在驱动程序的使用 ddi_dma_alloc_handle(9F)attach() 入口点中分配。ddi_dma_alloc_handle() 函数采用 dip 引用的设备信息以及 ddi_dma_attr(9S) 结构描述的设备的 DMA 特性作为参数。ddi_dma_alloc_handle() 函数的语法如下所示:

int ddi_dma_alloc_handle(dev_info_t *dip,
    ddi_dma_attr_t *attr, int (*callback)(caddr_t),
    caddr_t arg, ddi_dma_handle_t *handlep);

其中:

dip

指向设备的 dev_info 结构的指针。

attr

指向 ddi_dma_attr(9S) 结构的指针,如DMA 特性中所述。

callback

用于处理资源分配故障的回调函数的地址。

arg

要传递给回调函数的参数。

handlep

指向 DMA 句柄的指针,用于存储返回的句柄。

分配 DMA 资源

以下两个接口用于分配 DMA 资源:

如果存在驱动程序的 xxstart() 例程,则 DMA 资源通常在 xxstart() 例程中分配。有关 xxstart() 的讨论,请参见异步数据传输(块驱动程序)。这两个接口的语法如下:

int ddi_dma_addr_bind_handle(ddi_dma_handle_t handle,
    struct as *as, caddr_t addr,
    size_t len, uint_t flags, int (*callback)(caddr_t),
    caddr_t arg, ddi_dma_cookie_t *cookiep, uint_t *ccountp);

int ddi_dma_buf_bind_handle(ddi_dma_handle_t handle,
    struct buf *bp, uint_t flags,
    int (*callback)(caddr_t), caddr_t arg,
    ddi_dma_cookie_t *cookiep, uint_t *ccountp);

以下参数对于 ddi_dma_addr_bind_handle(9F)ddi_dma_buf_bind_handle(9F) 是通用的:

handle

DMA 句柄和用于分配资源的对象。

flags

表示传送方向和其他特性的标志集。DDI_DMA_READ 表示从设备向内存传送数据。DDI_DMA_WRITE 表示从内存向设备传送数据。有关可用标志的完整讨论,请参见 ddi_dma_addr_bind_handle(9F)ddi_dma_buf_bind_handle(9F) 手册页。

callback

用于处理资源分配故障的回调函数的地址。请参见 ddi_dma_alloc_handle(9F) 手册页。

arg

要传递给回调函数的参数。

cookiep

指向此对象的第一个 DMA cookie 的指针。

ccountp

指向此对象的 DMA cookie 数的指针。

对于 ddi_dma_addr_bind_handle(9F),对象通过包含以下参数的地址范围进行描述:

as

指向地址空间结构的指针。as 的值必须是 NULL

addr

对象的基本内核地址。

len

对象长度(以字节为单位)。

对于 ddi_dma_buf_bind_handle(9F),对象通过 bp 所指向的 buf(9S) 结构进行描述。

设备寄存器结构

对于具有 DMA 功能的设备,要使用的寄存器比前面示例中所用寄存器多。

设备寄存器结构中使用以下字段来支持具有 DMA 功能但不支持分散/集中的设备:

uint32_t      dma_addr;      /* starting address for DMA */
uint32_t      dma_size;      /* amount of data to transfer */

设备寄存器结构中使用以下字段来支持具有 DMA 功能并支持分散/集中的设备:

struct sglentry {
    uint32_t    dma_addr;
    uint32_t    dma_size;
} sglist[SGLLEN];

caddr_t       iopb_addr;     /* When written, informs the device of the next */
                             /* command's parameter block address. */
                             /* When read after an interrupt, contains */
                             /* the address of the completed command. */

DMA 回调示例

示例 9-1 中,xxstart() 用作回调函数。特定设备状态结构用作 xxstart() 的参数。xxstart() 函数将尝试启动命令。如果由于资源不可用而无法启动该命令,则会安排以后在资源可用时调用 xxstart()

由于 xxstart() 用作 DMA 回调,因此 xxstart() 必须遵守以下规则,DMA 回调上施加了这些规则:

示例 9-1 DMA 回调示例

static int
xxstart(caddr_t arg)
{
    struct xxstate *xsp = (struct xxstate *)arg;
    struct device_reg *regp;
    int flags;
    mutex_enter(&xsp->mu);
    if (xsp->busy) {
        /* transfer in progress */
        mutex_exit(&xsp->mu);
        return (DDI_DMA_CALLBACK_RUNOUT);
    }
    xsp->busy = 1;
    regp = xsp->regp;
    if ( /* transfer is a read */ ) {
        flags = DDI_DMA_READ;
    } else {
        flags = DDI_DMA_WRITE;
    }
    mutex_exit(&xsp->mu);
    if (ddi_dma_buf_bind_handle(xsp->handle,xsp->bp,flags, xxstart,
        (caddr_t)xsp, &cookie, &ccount) != DDI_DMA_MAPPED) {
        /* really should check all return values in a switch */
        mutex_enter(&xsp->mu);
        xsp->busy=0;
        mutex_exit(&xsp->mu);
        return (DDI_DMA_CALLBACK_RUNOUT);
    }
    /* Program the DMA engine. */
    return (DDI_DMA_CALLBACK_DONE);
}

确定最大突发流量大小

驱动程序在 ddi_dma_attr(9S) 结构的 dma_attr_burstsizes 字段中指定其设备支持的 DMA 突发流量大小。此字段是所支持的突发流量大小的位图。但是,在分配 DMA 资源时,系统可能会对设备实际使用的突发流量大小施加更多限制。ddi_dma_burstsizes(9F) 例程可用来获取允许的突发流量大小。此例程将为设备返回适当的突发流量大小位图。分配 DMA 资源时,驱动程序可向系统请求用于其 DMA 引擎的适当突发流量大小。

示例 9-2 确定突发流量大小

#define BEST_BURST_SIZE 0x20 /* 32 bytes */

    if (ddi_dma_buf_bind_handle(xsp->handle,xsp->bp, flags, xxstart,
        (caddr_t)xsp, &cookie, &ccount) != DDI_DMA_MAPPED) {
        /* error handling */
    }
    burst = ddi_dma_burstsizes(xsp->handle);
    /* check which bit is set and choose one burstsize to */
    /* program the DMA engine */
    if (burst & BEST_BURST_SIZE) {
        /* program DMA engine to use this burst size */
    } else {
        /* other cases */
    }

分配专用 DMA 缓冲区

一些设备驱动程序除了执行用户线程和内核请求的传送外,可能还需要为 DMA 传送分配内存。分配专用 DMA 缓冲区的一些示例包括设置用于与设备之间进行通信的共享内存以及分配中间传送缓冲区。使用 ddi_dma_mem_alloc(9F) 可为 DMA 传送分配内存。

int ddi_dma_mem_alloc(ddi_dma_handle_t handle, size_t length,
    ddi_device_acc_attr_t *accattrp, uint_t flags,
    int (*waitfp)(caddr_t), caddr_t arg, caddr_t *kaddrp,
    size_t *real_length, ddi_acc_handle_t *handlep);

其中:

handle

DMA 句柄

length

所需分配的长度(以字节为单位)

accattrp

指向设备访问特性结构的指针

flags

数据传送模式标志。可能的值包括 DDI_DMA_CONSISTENTDDI_DMA_STREAMING

waitfp

用于处理资源分配故障的回调函数的地址。请参见 ddi_dma_alloc_handle(9F) 手册页。

arg

要传递给回调函数的参数

kaddrp

成功返回时包含已分配存储空间的地址的指针

real_length

分配的长度(以字节为单位)

handlep

指向数据访问句柄的指针

如果设备以不连续的方式进行访问,则应将 flags 参数设置为 DDI_DMA_CONSISTENT。由于会频繁应用于小型对象,因此使用 ddi_dma_sync(9F) 的同步步骤应尽可能为轻量级步骤。这种访问类型通常称为一致访问。一致访问对用于设备与驱动程序之间通信的 I/O 参数块特别有用。

在 x86 平台上,物理上连续的 DMA 内存的分配有以下要求:

以下示例说明如何分配 IOPB 内存以及访问此内存必需的 DMA 资源。仍然必须分配 DMA 资源,并且必须将 DDI_DMA_CONSISTENT 标志传递给分配函数。

示例 9-3 使用 ddi_dma_mem_alloc(9F)

if (ddi_dma_mem_alloc(xsp->iopb_handle, size, &accattr,
    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &xsp->iopb_array,
    &real_length, &xsp->acchandle) != DDI_SUCCESS) {
    /* error handling */
    goto failure;
}
if (ddi_dma_addr_bind_handle(xsp->iopb_handle, NULL,
    xsp->iopb_array, real_length,
    DDI_DMA_READ | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP,
    NULL, &cookie, &count) != DDI_DMA_MAPPED) {
    /* error handling */
    ddi_dma_mem_free(&xsp->acchandle);
    goto failure;
}

对于顺序、单向、块大小和按块对齐的内存传送,flags 参数应设置为 DDI_DMA_STREAMING。这种访问类型通常称为访问。

在某些情况下,使用 I/O 高速缓存可以加快 I/O 传送。I/O 高速缓存最少传送一个高速缓存行。ddi_dma_mem_alloc(9F) 例程会将 size 舍入为高速缓存行的倍数,以避免数据损坏。

ddi_dma_mem_alloc(9F) 函数将返回已分配的内存对象的实际大小。由于存在填充和对齐要求,实际大小可能会大于所请求的大小。ddi_dma_addr_bind_handle(9F) 函数要求使用实际长度。

使用 ddi_dma_mem_free(9F) 函数可以释放 ddi_dma_mem_alloc(9F) 分配的内存。


注 - 驱动程序必须确保缓冲区适当对齐。要求下限 DMA 缓冲区对齐的设备的驱动程序可能需要将数据复制到满足该要求的驱动程序中间缓冲区,然后将该中间缓冲区绑定到 DMA 的 DMA 句柄。使用 ddi_dma_mem_alloc(9F) 可分配驱动程序中间缓冲区。请务必使用 ddi_dma_mem_alloc(9F) 而非 kmem_alloc(9F) 来为要进行访问的设备分配内存。


处理资源分配故障

资源分配例程在处理分配故障时可为驱动程序提供若干选项。waitfp 参数用于指明分配例程是阻塞、立即返回还是安排回调,如下表所示。

表 9-1 资源分配处理

waitfp
表示的操作
DDI_DMA_DONTWAIT
驱动程序不想等到资源可用
DDI_DMA_SLEEP
驱动程序愿意无限期地等到资源可用
其他值
当资源可能可用时要调用的函数的地址

对 DMA 引擎进行编程

如果资源已成功分配,则必须对设备进行编程。尽管对 DMA 引擎进行编程是特定于设备的,但所有 DMA 引擎都需要一个起始地址和一个传送计数。设备驱动程序将从 ddi_dma_addr_bind_handle(9F)ddi_dma_buf_bind_handle(9F)ddi_dma_getwin(9F) 的成功调用所返回的 DMA cookie 中检索这两个值。这些函数都会返回第一个 DMA cookie 以及指示 DMA 对象是否包含多个 cookie 的 cookie 计数。如果 cookie 计数 N 大于 1,则必须对 ddi_dma_nextcookie(9F) 调用 N-1 次,以检索其余所有 cookie。

DMA cookie 的类型为 ddi_dma_cookie(9S)。这一类型的 cookie 包含以下字段:

uint64_t    _dmac_ll;       /* 64-bit DMA address */
uint32_t    _dmac_la[2];    /* 2 x 32-bit address */
size_t      dmac_size;      /* DMA cookie size */
uint_t      dmac_type;      /* bus specific type bits */

dmac_laddress 指定适用于对设备的 DMA 引擎进行编程的 64 位 I/O 地址。如果设备具有 64 位 DMA 地址寄存器,则驱动程序应使用此字段对 DMA 引擎进行编程。dmac_address 字段指定应该用于具有 32 位 DMA 地址寄存器的设备的 32 位 I/O 地址。dmac_size 字段包含传送计数。根据总线体系结构,驱动程序可能需要 cookie 中的 dmac_type 字段。驱动程序不应对 cookie 执行任何处理,如逻辑或算术处理。

示例 9-4 ddi_dma_cookie(9S) 示例

ddi_dma_cookie_t            cookie;

     if (ddi_dma_buf_bind_handle(xsp->handle,xsp->bp, flags, xxstart,
     (caddr_t)xsp, &cookie, &xsp->ccount) != DDI_DMA_MAPPED) {
         /* error handling */
      }
     sglp = regp->sglist;
     for (cnt = 1; cnt <= SGLLEN; cnt++, sglp++) {
     /* store the cookie parms into the S/G list */
     ddi_put32(xsp->access_hdl, &sglp->dma_size,
         (uint32_t)cookie.dmac_size);
     ddi_put32(xsp->access_hdl, &sglp->dma_addr,
         cookie.dmac_address);
     /* Check for end of cookie list */
     if (cnt == xsp->ccount)
         break;
     /* Get next DMA cookie */
     (void) ddi_dma_nextcookie(xsp->handle, &cookie);
     }
     /* start DMA transfer */
     ddi_put8(xsp->access_hdl, &regp->csr,
     ENABLE_INTERRUPTS | START_TRANSFER);

释放 DMA 资源

DMA 传送完成后(通常在中断例程中),驱动程序可以通过调用 ddi_dma_unbind_handle(9F) 来释放 DMA 资源。

同步内存对象中所述,ddi_dma_unbind_handle(9F) 可调用 ddi_dma_sync(9F),从而无需进行任何显式同步。调用 ddi_dma_unbind_handle(9F) 之后,DMA 资源将无效,并且对资源的进一步引用会产生无法预料的结果。以下示例说明如何使用 ddi_dma_unbind_handle(9F)

示例 9-5 释放 DMA 资源

static uint_t
xxintr(caddr_t arg)
{
     struct xxstate *xsp = (struct xxstate *)arg;
     uint8_t    status;
     volatile   uint8_t   temp;
     mutex_enter(&xsp->mu);
     /* read status */
     status = ddi_get8(xsp->access_hdl, &xsp->regp->csr);
     if (!(status & INTERRUPTING)) {
        mutex_exit(&xsp->mu);
        return (DDI_INTR_UNCLAIMED);
     }
     ddi_put8(xsp->access_hdl, &xsp->regp->csr, CLEAR_INTERRUPT);
     /* for store buffers */
     temp = ddi_get8(xsp->access_hdl, &xsp->regp->csr);
     ddi_dma_unbind_handle(xsp->handle);
     /* Check for errors. */
     xsp->busy = 0;
     mutex_exit(&xsp->mu);
     if ( /* pending transfers */ ) {
        (void) xxstart((caddr_t)xsp);
     }
     return (DDI_INTR_CLAIMED);
}

应释放 DMA 资源。如果要在下一传送中使用不同对象,则应重新分配 DMA 资源。但是,如果始终使用同一个对象,则分配一次资源即可。只要保持对 ddi_dma_sync(9F) 的介入调用,随后便可重用资源。

释放 DMA 句柄

分离驱动程序时,必须释放 DMA 句柄。ddi_dma_free_handle(9F) 函数可销毁 DMA 句柄以及系统在该句柄上高速缓存的任何剩余资源。如果再对 DMA 句柄进行任何引用,将会产生无法预料的结果。

取消 DMA 回调

DMA 回调不能取消。取消 DMA 回调需要在驱动程序的 detach(9E) 入口点中附加一些代码。如果存在任何未完成的回调,则 detach() 例程一定不会返回 DDI_SUCCESS。请参见示例 9-6。发生 DMA 回调时,detach() 例程必须等待回调运行。回调完成时,detach() 必须防止回调自行重新安排。通过状态结构中的附加字段可以防止重新安排回调,如以下示例所示。

示例 9-6 取消 DMA 回调

static int
xxdetach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
     /* ... */
     mutex_enter(&xsp->callback_mutex);
     xsp->cancel_callbacks = 1;
     while (xsp->callback_count > 0) {
        cv_wait(&xsp->callback_cv, &xsp->callback_mutex);
     }
     mutex_exit(&xsp->callback_mutex);
     /* ... */
 }

static int
xxstrategy(struct buf *bp)
{
     /* ... */
     mutex_enter(&xsp->callback_mutex);
       xsp->bp = bp;
     error = ddi_dma_buf_bind_handle(xsp->handle, xsp->bp, flags,
         xxdmacallback, (caddr_t)xsp, &cookie, &ccount);
     if (error == DDI_DMA_NORESOURCES)
       xsp->callback_count++;
     mutex_exit(&xsp->callback_mutex);
     /* ... */
}

static int
xxdmacallback(caddr_t callbackarg)
{
     struct xxstate *xsp = (struct xxstate *)callbackarg;
     /* ... */
     mutex_enter(&xsp->callback_mutex);
     if (xsp->cancel_callbacks) {
        /* do not reschedule, in process of detaching */
        xsp->callback_count--;
        if (xsp->callback_count == 0)
           cv_signal(&xsp->callback_cv);
        mutex_exit(&xsp->callback_mutex);
        return (DDI_DMA_CALLBACK_DONE);    /* don't reschedule it */
     }
     /*
      * Presumably at this point the device is still active
      * and will not be detached until the DMA has completed.
      * A return of 0 means try again later
      */
     error = ddi_dma_buf_bind_handle(xsp->handle, xsp->bp, flags,
         DDI_DMA_DONTWAIT, NULL, &cookie, &ccount);
     if (error == DDI_DMA_MAPPED) {
        /* Program the DMA engine. */
        xsp->callback_count--;
        mutex_exit(&xsp->callback_mutex);
        return (DDI_DMA_CALLBACK_DONE);
     }
     if (error != DDI_DMA_NORESOURCES) {
        xsp->callback_count--;
        mutex_exit(&xsp->callback_mutex);
        return (DDI_DMA_CALLBACK_DONE);
     }
     mutex_exit(&xsp->callback_mutex);
     return (DDI_DMA_CALLBACK_RUNOUT);
}

同步内存对象

在访问内存对象的过程中,驱动程序可能需要同步与各种高速缓存有关的内存对象。本节提供了有关何时以及如何同步内存对象的准则。

高速缓存

CPU 高速缓存是位于 CPU 和系统的主内存之间的极高速内存。I/O 高速缓存位于设备和系统的主内存之间,如下图所示。

图 9-1 CPU 和系统 I/O 高速缓存

image:图中显示如何使用高速缓存加快涉及设备的数据传送。

尝试从主内存读取数据时,关联的高速缓存会对请求的数据进行检查。如果数据可用,高速缓存可快速提供这些数据。如果高速缓存中没有数据,则该高速缓存将从主内存中检索数据。然后,高速缓存会将数据传递给请求者并保存数据,以备在后续请求中使用。

类似地,在写循环中,数据会快速存储在高速缓存中。CPU 或设备可以继续执行,即传送数据。将数据存储在高速缓存中所需的时间比等待将数据写入内存所需的时间少得多。

采用此模型,在设备传送完成后,数据仍可位于 I/O 高速缓存中,而主内存中没有数据。如果 CPU 访问内存,CPU 可能会从 CPU 高速缓存中读取错误数据。驱动程序必须调用同步例程,以刷新 I/O 高速缓存中的数据,并使用新数据更新 CPU 高速缓存。此操作可确保内存的情况对于 CPU 而言保持一致。类似地,如果设备要对 CPU 修改的数据进行访问,则需要采用同步步骤。

可在设备和内存之间创建附加的高速缓存和缓冲区,如总线延伸架和桥。使用 ddi_dma_sync(9F) 可以同步所有适用的高速缓存。

ddi_dma_sync() 函数

一个内存对象可能有多个映射,如通过 DMA 句柄用于 CPU 和用于设备的映射。如果使用任何映射来修改内存对象,则具有多个映射的驱动程序需要调用 ddi_dma_sync(9F)。调用 ddi_dma_sync() 可以确保对内存对象的修改在通过不同映射访问该对象之前完成。如果对对象的任何高速缓存引用现在已过时,ddi_dma_sync() 函数还可以通知该对象的其他映射。此外,ddi_dma_sync() 还会根据需要刷新过时的高速缓存引用或使其无效。

通常,当 DMA 传送完成时,驱动程序必须调用 ddi_dma_sync()。此规则的例外情况是如果使用 ddi_dma_unbind_handle(9F) 取消分配 DMA 资源,则会代表驱动程序隐式执行 ddi_dma_sync()ddi_dma_sync() 的语法如下:

int ddi_dma_sync(ddi_dma_handle_t handle, off_t off,
size_t length, uint_t type);

如果设备的 DMA 引擎要读取对象,则必须通过将 type 设置为 DDI_DMA_SYNC_FORDEV 来同步该设备看到的对象信息。如果设备的 DMA 引擎已写入内存对象并且 CPU 将读取该对象,则必须通过将 type 设置为 DDI_DMA_SYNC_FORCPU 来同步该 CPU 看到的对象信息。

以下示例说明如何为 CPU 同步 DMA 对象:

if (ddi_dma_sync(xsp->handle, 0, length, DDI_DMA_SYNC_FORCPU)
    == DDI_SUCCESS) {
    /* the CPU can now access the transferred data */
    /* ... */
} else {
    /* error handling */
}

如果唯一的映射是用于内核的,请使用标志 DDI_DMA_SYNC_FORKERNEL,类似于 ddi_dma_mem_alloc(9F) 所分配的内存中的情况。系统会尝试以比同步 CPU 看到的信息更快的速度来同步内核看到的信息。如果系统无法更快地同步内核看到的信息,则系统将按照如同已设置 DDI_DMA_SYNC_FORCPU 标志的情况执行相应的操作。