async_tx API 提供了描述异步批量内存传输/转换链的方法,并支持事务间的依赖关系。它被实现为一个 dmaengine 客户端,可以屏蔽不同硬件卸载(hardware offload )引擎实现的细节。使用该 API 编写的代码可以优化异步操作,并且该 API 将适应可用的卸载资源来适配操作链。
2. 家谱
该 API 最初设计为使用 Intel(R) Xscale 系列 I/O 处理器中的卸载引擎来卸载 md-raid5 驱动程序的内存复制和 xor-parity-calculation。它还建立在“dmaengine”层之上,该层是为使用英特尔(R) I/OAT 引擎卸载网络堆栈中的内存副本而开发的。因此,出现了以下设计特征:
-
隐式同步路径:API 的用户不需要知道他们运行的平台是否具有卸载功能。当引擎可用时,该操作将被卸载,否则将在软件中执行
-
跨通道依赖链:API 允许提交一系列依赖操作,例如 raid5 情况下的 xor->copy->xor。API 会自动处理从一个操作转换到另一个操作意味着硬件通道切换的情况。
-
dmaengine 扩展支持“memcpy”以外的多个客户端和操作类型
3. 用法
3.1 API 的一般格式
struct dma_async_tx_descriptor *
async_<operation>(<op specific parameters>, struct async_submit_ctl *submit)
3.2 支持的操作
3.3 描述符管理
当操作已经被排队执行异步时,返回值是非空且指向一个“描述符”(descriptor)
。描述符是被卸载引擎驱动程序控制的可循环利用的资源,用于在操作完成后进行重用。当应用程序需要提交一系列操作时,必须确保在依赖关系被提交之前,描述符不会被自动回收。这要求应用程序在允许卸载引擎驱动程序回收(或释放)描述符之前,必须对所有描述符进行确认。可以通过以下方法对描述符进行确认:
- 如果不需要提交子操作,可以设置ASYNC_TX_ACK标志。
- 将未确认的描述符提交为另一个async_tx调用的依赖关系将隐式设置已确认状态。
- 在描述符上调用async_tx_ack()。
3.4 操作何时执行?
从 async_ 调用返回后,操作不会立即执行。卸载引擎驱动程序会批处理操作,通过减少管理通道所需的 mmio 周期数量来提高性能。一旦达到驱动程序特定的阈值,驱动程序会自动执行挂起的操作。应用程序可以通过调用 async_tx_issue_pending_all() 强制触发此事件。这将对所有通道进行操作,因为应用程序不知道通道到操作的映射。
3.5 操作何时完成?
应用程序可通过两种方法了解操作的完成情况。
-
调用 dma_wait_for_async_tx() 函数。该函数会导致 CPU 一直忙于轮询操作的完成情况。它可以处理依赖链并执行未决操作。
-
指定完成回调。如果卸载引擎驱动程序支持中断,则回调例程在 tasklet 上下文中运行,如果操作在软件中同步执行,则在应用程序上下文中调用回调例程。可以在对 async_ 的调用中设置回调,或者当应用程序需要提交未知长度的链时,可以使用 async_trigger_callback() 例程在链的末尾设置完成中断/回调。
3.6 约束
- 在 IRQ 上下文中不允许调用 async_。在不违反约束 #2 的情况下,允许其他上下文。
- 完成回调例程(Completion callback routines)无法提交新操作。在同步情况下这会导致递归,而在异步情况下会两次获取spin_locks。
3.7 示例
执行 xor->copy->xor 操作,其中每个操作都依赖于前一个操作的结果:
void callback(void *param)
{
struct completion *cmp = param;
complete(cmp);
}
void run_xor_copy_xor(struct page **xor_srcs,
int xor_src_cnt,
struct page *xor_dest,
size_t xor_len,
struct page *copy_src,
struct page *copy_dest,
size_t copy_len)
{
struct dma_async_tx_descriptor *tx;
addr_conv_t addr_conv[xor_src_cnt];
struct async_submit_ctl submit;
addr_conv_t addr_conv[NDISKS];
struct completion cmp;
init_async_submit(&submit, ASYNC_TX_XOR_DROP_DST, NULL, NULL, NULL,
addr_conv);
tx = async_xor(xor_dest, xor_srcs, 0, xor_src_cnt, xor_len, &submit)
submit->depend_tx = tx;
tx = async_memcpy(copy_dest, copy_src, 0, 0, copy_len, &submit);
init_completion(&cmp);
init_async_submit(&submit, ASYNC_TX_XOR_DROP_DST | ASYNC_TX_ACK, tx,
callback, &cmp, addr_conv);
tx = async_xor(xor_dest, xor_srcs, 0, xor_src_cnt, xor_len, &submit);
async_tx_issue_pending_all();
wait_for_completion(&cmp);
}
有关标志的更多信息,请参阅 include/linux/async_tx.h。有关更多实现示例,请参阅 drivers/md/raid5.c 中的 ops_run_* 和 ops_complete_* 例程。
4. 驱动开发说明
4.1 一致性点
dmaengine 驱动程序中需要一些一致性点(conformance points),以适应使用 async_tx API 的应用程序所做的假设:
-
完成回调(Completion callbacks )应在 tasklet 上下文中发生
-
dma_async_tx_descriptor 字段从不在 IRQ 上下文中操作
-
在描述符清理路径中使用 async_tx_run_dependencies() 处理依赖操作的提交
4.2 “我的应用需要独占控制硬件通道”
此要求主要源于使用 DMA 引擎驱动程序来支持设备到内存操作的情况。由于许多平台特定原因,无法共享执行这些操作的通道。对于这些情况,提供了 dma_request_channel() 接口。
接口为:
struct dma_chan *dma_request_channel(dma_cap_mask_t mask,
dma_filter_fn filter_fn,
void *filter_param);
其中dma_filter_fn定义为:
typedef bool (*dma_filter_fn)(struct dma_chan *chan, void *filter_param);
当可选参数filter_fn
被设置为NULL时,dma_request_channel只会返回第一个满足能力掩码的通道。否则,在mask参数不足以指定所需通道时,可以使用filter_fn例程分配系统中的可用通道。对于系统中的每个空闲通道,filter_fn例程都会调用一次。在看到合适的通道时,filter_fn返回DMA_ACK,该ack将该通道标记为dma_request_channel的返回值。在调用dma_release_channel()之前,通过该接口分配的通道对调用者是独占的。
DMA_PRIVATE标志用于标记通用分配器不应该使用的dma设备。如果知道通道将始终是私有的,则可以在初始化时设置它。或者,当dma_request_channel()发现未使用的“公共”通道时设置该参数。
实现驱动程序和使用者时要注意的几个注意事项:
- 一旦信道被私有分配,即使在调用dma_release_channel()之后,通用分配器也不会再考虑它。
- 由于功能是在设备级别指定的,具有多个通道的dma_device要么将所有通道都设置为public,要么将所有通道设置为private。
5. 源代码
include/linux/dmaengine.h:\
core header file for DMA drivers and api users
drivers/dma/dmaengine.c:\
offload engine channel management routines
drivers/dma/:\
location for offload engine drivers
include/linux/async_tx.h:\
core header file for the async_tx api
crypto/async_tx/async_tx.c:\
async_tx interface to dmaengine and common code
crypto/async_tx/async_memcpy.c:\
copy offload
crypto/async_tx/async_xor.c:\
xor and xor zero sum offload
ref: https://www.kernel.org/doc/html/latest/crypto/async-tx-api.html