非同步傳輸/轉換 API

1. 簡介

async_tx API 提供了描述一系列非同步批次記憶體傳輸/轉換的方法,支援事務間依賴關係。它被實現為 dmaengine 客戶端,平滑了不同硬體解除安裝引擎實現的細節。為該 API 編寫的程式碼可以針對非同步操作進行最佳化,並且 API 將使操作鏈適應可用的解除安裝資源。

2. 淵源

該 API 最初旨在解除安裝 md-raid5 驅動程式的記憶體複製和異或奇偶校驗計算,使用 Intel(R) Xscale 系列 I/O 處理器中的解除安裝引擎。它還構建在為網路堆疊中解除安裝記憶體複製而開發的“dmaengine”層之上,使用 Intel(R) I/OAT 引擎。因此,以下設計特性浮出水面

  1. 隱式同步路徑:API 的使用者不需要知道他們執行的平臺是否具有解除安裝功能。當引擎可用時,操作將被解除安裝,否則將在軟體中執行。

  2. 跨通道依賴鏈:API 允許提交一系列相關的操作,例如 raid5 中的異或->複製->異或。API 自動處理從一個操作到另一個操作的轉換意味著硬體通道切換的情況。

  3. dmaengine 擴充套件支援多個客戶端和 memcpy 以外的操作型別

3. 用法

3.1 API 的通用格式

struct dma_async_tx_descriptor *
async_<operation>(<op specific parameters>, struct async_submit_ctl *submit)

3.2 支援的操作

memcpy

源緩衝區和目標緩衝區之間的記憶體複製

memset

用位元組值填充目標緩衝區

xor

異或一系列源緩衝區,並將結果寫入目標緩衝區

xor_val

異或一系列源緩衝區,如果結果為零,則設定一個標誌。該實現嘗試阻止寫入記憶體

pq

從一系列源緩衝區生成 p+q (raid6 綜合徵)

pq_val

驗證 p 和/或 q 緩衝區是否與給定的源系列同步

datap

(raid6_datap_recov) 從給定的源恢復 raid6 資料塊和 p 塊

2data

(raid6_2data_recov) 從給定的源恢復 2 個 raid6 資料塊

3.3 描述符管理

當操作已排隊非同步執行時,返回值是非 NULL 並且指向一個“描述符”。描述符是可回收資源,受解除安裝引擎驅動程式的控制,以便在操作完成時重複使用。當應用程式需要提交一系列操作時,它必須保證描述符不會在提交依賴項之前自動回收。這需要應用程式確認所有描述符,然後才允許解除安裝引擎驅動程式回收(或釋放)描述符。可以透過以下方法之一確認描述符

  1. 如果沒有要提交的子操作,則設定 ASYNC_TX_ACK 標誌

  2. 將未經確認的描述符作為依賴項提交給另一個 async_tx 呼叫將隱式設定確認狀態。

  3. 在描述符上呼叫 async_tx_ack()。

3.4 操作何時執行?

從 async_ 呼叫返回後,操作不會立即發出。解除安裝引擎驅動程式批次處理操作,以透過減少管理通道所需的 mmio 週期數來提高效能。一旦達到驅動程式特定的閾值,驅動程式會自動發出掛起的操作。應用程式可以透過呼叫 async_tx_issue_pending_all() 強制執行此事件。由於應用程式不知道通道到操作的對映,因此它在所有通道上執行。

3.5 操作何時完成?

應用程式可以透過兩種方法瞭解操作的完成情況。

  1. 呼叫 dma_wait_for_async_tx()。此呼叫導致 CPU 在輪詢操作完成時旋轉。它處理依賴鏈併發出掛起的操作。

  2. 指定完成回撥。如果解除安裝引擎驅動程式支援中斷,則回撥例程在 tasklet 上下文中執行,如果操作在軟體中同步執行,則在應用程式上下文中呼叫它。回撥可以在呼叫 async_ 時設定,或者當應用程式需要提交長度未知的鏈時,它可以使用 async_trigger_callback() 例程在鏈的末尾設定完成中斷/回撥。

3.6 約束

  1. 不允許在 IRQ 上下文中呼叫 async_。如果未違反約束 #2,則允許其他上下文。

  2. 完成回撥例程無法提交新操作。這導致同步情況下的遞迴和非同步情況下兩次獲取自旋鎖。

3.7 示例

執行異或->複製->異或操作,其中每個操作都依賴於前一個操作的結果

#include <linux/async_tx.h>

static void callback(void *param)
{
        complete(param);
}

#define NDISKS  2

static void run_xor_copy_xor(struct page **xor_srcs,
                             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;
        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, NDISKS, 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, NDISKS, 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 驅動程式中需要一些一致性點,以適應使用 async_tx API 的應用程式所做的假設

  1. 完成回撥應發生在 tasklet 上下文中

  2. dma_async_tx_descriptor 欄位永遠不會在 IRQ 上下文中操作

  3. 在描述符清理路徑中使用 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 只會返回滿足功能掩碼的第一個通道。否則,當掩碼引數不足以指定必要的通道時,可以使用 filter_fn 例程來處置系統中的可用通道。filter_fn 例程會為系統中的每個空閒通道呼叫一次。在看到合適的通道時,filter_fn 返回 DMA_ACK,該標誌通道將作為 dma_request_channel 的返回值。透過此介面分配的通道對呼叫者是獨佔的,直到呼叫 dma_release_channel()。

DMA_PRIVATE 功能標誌用於標記不應由通用分配器使用的 dma 裝置。如果在初始化時已知通道將始終是私有的,則可以在初始化時設定它。或者,當 dma_request_channel() 找到未使用的“公共”通道時,將設定它。

實施驅動程式和使用者時需要注意以下幾點

  1. 一旦私下分配了通道,即使在呼叫 dma_release_channel() 後,通用分配器也不再考慮該通道。

  2. 由於功能是在裝置級別指定的,因此具有多個通道的 dma_device 要麼具有所有公共通道,要麼具有所有私有通道。

5. 原始碼

include/linux/dmaengine.h

DMA 驅動程式和 api 使用者的核心標頭檔案

drivers/dma/dmaengine.c

解除安裝引擎通道管理例程

drivers/dma/

解除安裝引擎驅動程式的位置

include/linux/async_tx.h

async_tx api 的核心標頭檔案

crypto/async_tx/async_tx.c

dmaengine 和公共程式碼的 async_tx 介面

crypto/async_tx/async_memcpy.c

複製解除安裝

crypto/async_tx/async_xor.c

異或和異或零和解除安裝