요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
.. SPDX-License-Identifier: GPL-2.0
=====================================
Asynchronous Transfers/Transforms API
=====================================
.. Contents
1. INTRODUCTION
2 GENEALOGY
3 USAGE
3.1 General format of the API
3.2 Supported operations
3.3 Descriptor management
3.4 When does the operation execute?
3.5 When does the operation complete?
3.6 Constraints
3.7 Example
4 DMAENGINE DRIVER DEVELOPER NOTES
4.1 Conformance points
4.2 "My application needs exclusive control of hardware channels"
5 SOURCE
1. Introduction
===============
The async_tx API provides methods for describing a chain of asynchronous
bulk memory transfers/transforms with support for inter-transactional
dependencies. It is implemented as a dmaengine client that smooths over
the details of different hardware offload engine implementations. Code
that is written to the API can optimize for asynchronous operation and
the API will fit the chain of operations to the available offload
resources.
2.Genealogy
===========
The API was initially designed to offload the memory copy and
xor-parity-calculations of the md-raid5 driver using the offload engines
present in the Intel(R) Xscale series of I/O processors. It also built
on the 'dmaengine' layer developed for offloading memory copies in the
network stack using Intel(R) I/OAT engines. The following design
features surfaced as a result:
1. implicit synchronous path: users of the API do not need to know if
the platform they are running on has offload capabilities. The
operation will be offloaded when an engine is available and carried out
in software otherwise.
2. cross channel dependency chains: the API allows a chain of dependent
operations to be submitted, like xor->copy->xor in the raid5 case. The
API automatically handles cases where the transition from one operation
to another implies a hardware channel switch.
3. dmaengine extensions to support multiple clients and operation types
beyond 'memcpy'
3. Usage
========
3.1 General format of the API
-----------------------------
::
struct dma_async_tx_descriptor *
async_<operation>(<op specific parameters>, struct async_submit_ctl *submit)
3.2 Supported operations
------------------------
======== ====================================================================
memcpy memory copy between a source and a destination buffer
memset fill a destination buffer with a byte value
xor xor a series of source buffers and write the result to a
destination buffer
xor_val xor a series of source buffers and set a flag if the
result is zero. The implementation attempts to prevent
writes to memory
pq generate the p+q (raid6 syndrome) from a series of source buffers
pq_val validate that a p and or q buffer are in sync with a given series of
sources
datap (raid6_datap_recov) recover a raid6 data block and the p block
from the given sources
2data (raid6_2data_recov) recover 2 raid6 data blocks from the given
sources
======== ====================================================================
3.3 Descriptor management
-------------------------
The return value is non-NULL and points to a 'descriptor' when the operation
has been queued to execute asynchronously. Descriptors are recycled
resources, under control of the offload engine driver, to be reused as
operations complete. When an application needs to submit a chain of
operations it must guarantee that the descriptor is not automatically recycled
before the dependency is submitted. This requires that all descriptors be
acknowledged by the application before the offload engine driver is allowed to
recycle (or free) the descriptor. A descriptor can be acked by one of the
following methods:
1. setting the ASYNC_TX_ACK flag if no child operations are to be submitted
2. submitting an unacknowledged descriptor as a dependency to another
async_tx call will implicitly set the acknowledged state.
3. calling async_tx_ack() on the descriptor.
3.4 When does the operation execute?
------------------------------------
Operations do not immediately issue after return from the
async_<operation> call. Offload engine drivers batch operations to
improve performance by reducing the number of mmio cycles needed to
manage the channel. Once a driver-specific threshold is met the driver
automatically issues pending operations. An application can force this
event by calling async_tx_issue_pending_all(). This operates on all
channels since the application has no knowledge of channel to operation
mapping.
3.5 When does the operation complete?
-------------------------------------
There are two methods for an application to learn about the completion
of an operation.
1. Call dma_wait_for_async_tx(). This call causes the CPU to spin while
it polls for the completion of the operation. It handles dependency
chains and issuing pending operations.
2. Specify a completion callback. The callback routine runs in tasklet
context if the offload engine driver supports interrupts, or it is
called in application context if the operation is carried out
synchronously in software. The callback can be set in the call to
async_<operation>, or when the application needs to submit a chain of
unknown length it can use the async_trigger_callback() routine to set a
completion interrupt/callback at the end of the chain.
3.6 Constraints
---------------
1. Calls to async_<operation> are not permitted in IRQ context. Other
contexts are permitted provided constraint #2 is not violated.
2. Completion callback routines cannot submit new operations. This
results in recursion in the synchronous case and spin_locks being
acquired twice in the asynchronous case.
3.7 Example
-----------
Perform a xor->copy->xor operation where each operation depends on the
result from the previous operation::
#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);
}
See include/linux/async_tx.h for more information on the flags. See the
ops_run_* and ops_complete_* routines in drivers/md/raid5.c for more
implementation examples.
4. Driver Development Notes
===========================
4.1 Conformance points
----------------------
There are a few conformance points required in dmaengine drivers to
accommodate assumptions made by applications using the async_tx API:
1. Completion callbacks are expected to happen in tasklet context
2. dma_async_tx_descriptor fields are never manipulated in IRQ context
3. Use async_tx_run_dependencies() in the descriptor clean up path to
handle submission of dependent operations
4.2 "My application needs exclusive control of hardware channels"
-----------------------------------------------------------------
Primarily this requirement arises from cases where a DMA engine driver
is being used to support device-to-memory operations. A channel that is
performing these operations cannot, for many platform specific reasons,
be shared. For these cases the dma_request_channel() interface is
provided.
The interface is::
struct dma_chan *dma_request_channel(dma_cap_mask_t mask,
dma_filter_fn filter_fn,
void *filter_param);
Where dma_filter_fn is defined as::
typedef bool (*dma_filter_fn)(struct dma_chan *chan, void *filter_param);
When the optional 'filter_fn' parameter is set to NULL
dma_request_channel simply returns the first channel that satisfies the
capability mask. Otherwise, when the mask parameter is insufficient for
specifying the necessary channel, the filter_fn routine can be used to
disposition the available channels in the system. The filter_fn routine
is called once for each free channel in the system. Upon seeing a
suitable channel filter_fn returns DMA_ACK which flags that channel to
be the return value from dma_request_channel. A channel allocated via
this interface is exclusive to the caller, until dma_release_channel()
is called.
The DMA_PRIVATE capability flag is used to tag dma devices that should
not be used by the general-purpose allocator. It can be set at
initialization time if it is known that a channel will always be
private. Alternatively, it is set when dma_request_channel() finds an
unused "public" channel.
A couple caveats to note when implementing a driver and consumer:
1. Once a channel has been privately allocated it will no longer be
considered by the general-purpose allocator even after a call to
dma_release_channel().
2. Since capabilities are specified at the device level a dma_device
with multiple channels will either have all channels public, or all
channels private.
5. Source
---------
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
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
비동기 transfer·transform API
1-27SPDX 라이선스 식별자: `GPL-2.0`
비동기 transfer·transform API
문서 목차는 다음과 같습니다.
- 1. 소개
- 2. 계보
- 3. 사용법: API 일반 형식, 지원 연산, descriptor 관리, 실행·완료 시점, 제약, 예제
- 4. DMAENGINE driver 개발자 참고 사항: 준수 항목과 hardware channel 독점 제어
- 5. Source
1. 소개
28-381. 소개
`async_tx` API는 transaction 사이의 dependency를 지원하면서 비동기 bulk memory transfer·transform chain을 기술하는 방법을 제공합니다. 서로 다른 hardware offload engine 구현의 세부 차이를 감추는 dmaengine client로 구현됩니다. 이 API를 사용하는 코드는 비동기 연산에 맞게 최적화할 수 있고, API가 연산 chain을 사용 가능한 offload resource에 맞춥니다.
2. 계보
39-592. 계보
이 API는 처음에 Intel(R) Xscale I/O processor의 offload engine을 사용하여 `md-raid5` driver의 memory copy와 XOR parity 계산을 offload하기 위해 설계되었습니다. 또한 Intel(R) I/OAT engine으로 network stack의 memory copy를 offload하기 위해 개발된 `dmaengine` 계층을 기반으로 했습니다.
그 결과 다음 설계 특성이 생겼습니다.
- 1. 암시적 동기 경로: 사용자는 실행 중인 platform의 offload capability를 알 필요가 없습니다. Engine이 있으면 연산을 offload하고, 없으면 software로 수행합니다.
- 2. Channel 간 dependency chain: RAID5의 `xor->copy->xor`처럼 서로 의존하는 연산 chain을 제출할 수 있습니다. 연산 전환에 hardware channel 변경이 필요하면 API가 자동으로 처리합니다.
- 3. 여러 client와 `memcpy` 이외의 연산 유형을 지원하도록 dmaengine을 확장했습니다.
3. 사용법과 API 일반 형식
60-703. 사용법
3.1 API의 일반 형식
::
struct dma_async_tx_descriptor *
async_<operation>(<op specific parameters>, struct async_submit_ctl *submit)
각 `async_<operation>()` 함수는 연산별 매개변수와 submission 제어 정보인 `struct async_submit_ctl *submit`을 받고 `struct dma_async_tx_descriptor *`를 반환합니다.
3.2 지원 연산
71-903.2 지원 연산
| 연산 | 설명 |
|---|---|
| `memcpy` | source buffer와 destination buffer 사이의 memory copy |
| `memset` | destination buffer를 하나의 byte 값으로 채움 |
| `xor` | 여러 source buffer를 XOR하고 결과를 destination buffer에 기록 |
| `xor_val` | 여러 source buffer를 XOR하여 결과가 0이면 flag를 설정하며, 구현은 memory write를 피하려고 시도함 |
| `pq` | 여러 source buffer에서 p+q, 즉 RAID6 syndrome을 생성 |
| `pq_val` | p 또는 q buffer가 주어진 source 집합과 동기화되어 있는지 검증 |
| `datap` | `raid6_datap_recov`: 주어진 source에서 RAID6 data block 하나와 p block을 복구 |
| `2data` | `raid6_2data_recov`: 주어진 source에서 RAID6 data block 두 개를 복구 |
3.3 Descriptor 관리
91-1083.3 Descriptor 관리
연산이 비동기 실행 queue에 들어가면 반환값은 NULL이 아니며 `descriptor`를 가리킵니다. Descriptor는 offload engine driver가 관리하는 재활용 resource로, 연산이 완료되면 다시 사용됩니다.
Application이 연산 chain을 제출하려면 dependency를 제출하기 전에 descriptor가 자동 재활용되지 않음을 보장해야 합니다. Offload engine driver가 descriptor를 재활용하거나 해제할 수 있기 전에 application이 모든 descriptor를 acknowledge해야 합니다.
Descriptor를 acknowledge하는 방법은 다음과 같습니다.
- 1. Child 연산을 제출하지 않을 경우 `ASYNC_TX_ACK` flag를 설정합니다.
- 2. Acknowledge되지 않은 descriptor를 다른 `async_tx` 호출의 dependency로 제출하면 acknowledge 상태가 암시적으로 설정됩니다.
- 3. Descriptor에 `async_tx_ack()`를 호출합니다.
3.4 연산 실행 시점
109-1203.4 연산은 언제 실행되는가?
`async_<operation>` 호출이 반환된 직후 연산이 발행되는 것은 아닙니다. Offload engine driver는 channel 관리에 필요한 MMIO cycle 수를 줄여 성능을 높이기 위해 연산을 batch 처리합니다. Driver별 threshold에 도달하면 pending 연산을 자동 발행합니다.
Application은 `async_tx_issue_pending_all()`을 호출하여 이 event를 강제할 수 있습니다. Application은 channel과 연산의 mapping을 모르므로 이 함수는 모든 channel에 작동합니다.
3.5 연산 완료 시점
121-1373.5 연산은 언제 완료되는가?
Application이 연산 완료를 알아내는 방법은 두 가지입니다.
- 1. `dma_wait_for_async_tx()`를 호출합니다. CPU가 연산 완료를 polling하며 spin하도록 하고 dependency chain과 pending 연산 발행을 처리합니다.
- 2. 완료 callback을 지정합니다. Offload engine driver가 interrupt를 지원하면 callback은 tasklet context에서 실행되고, 연산이 software에서 동기적으로 수행되면 application context에서 호출됩니다. `async_<operation>` 호출에서 callback을 설정할 수 있습니다. 길이를 알 수 없는 chain이라면 `async_trigger_callback()`으로 chain 끝에 완료 interrupt·callback을 설정할 수 있습니다.
3.6 제약
138-1463.6 제약
- 1. IRQ context에서는 `async_<operation>`을 호출할 수 없습니다. 2번 제약을 위반하지 않는 다른 context에서는 호출할 수 있습니다.
- 2. 완료 callback은 새 연산을 제출할 수 없습니다. 동기 경로에서는 recursion이 발생하고 비동기 경로에서는 `spin_lock`을 두 번 획득하게 됩니다.
3.7 예제
147-1943.7 예제
각 연산이 이전 연산의 결과에 의존하는 `xor->copy->xor` 연산을 수행합니다.
#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);
}
Flag에 관한 자세한 내용은 `include/linux/async_tx.h`를 참조하십시오. 추가 구현 예제는 `drivers/md/raid5.c`의 `ops_run_*` 및 `ops_complete_*` routine을 참조하십시오.
4. Driver 개발 참고 사항과 준수 항목
195-2084. Driver 개발 참고 사항
4.1 준수 항목
`async_tx` API application의 가정을 수용하기 위해 dmaengine driver가 지켜야 하는 항목은 다음과 같습니다.
- 1. 완료 callback은 tasklet context에서 발생해야 합니다.
- 2. `dma_async_tx_descriptor` field는 IRQ context에서 조작하지 않습니다.
- 3. Descriptor 정리 경로에서 `async_tx_run_dependencies()`를 사용해 dependent 연산 제출을 처리합니다.
4.2 Hardware channel 독점 제어
209-2534.2 "Application이 hardware channel을 독점 제어해야 하는 경우"
이 요구는 주로 DMA engine driver가 device-to-memory 연산을 지원하는 경우에 생깁니다. 이러한 연산을 수행하는 channel은 platform별 여러 이유로 공유할 수 없습니다. 이를 위해 `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()`은 capability mask를 만족하는 첫 channel을 반환합니다. Mask만으로 필요한 channel을 지정하기 부족하면 `filter_fn`으로 시스템의 사용 가능한 channel을 선별할 수 있습니다.
`filter_fn`은 시스템의 free channel마다 한 번 호출됩니다. 적합한 channel을 찾으면 `DMA_ACK`를 반환하여 그 channel을 `dma_request_channel()`의 반환값으로 표시합니다. 이 인터페이스로 할당한 channel은 `dma_release_channel()`을 호출할 때까지 호출자가 독점합니다.
`DMA_PRIVATE` capability flag는 범용 allocator가 사용해서는 안 되는 DMA device를 표시합니다. Channel이 항상 private일 것을 안다면 초기화 시 설정할 수 있고, `dma_request_channel()`이 사용하지 않는 public channel을 찾을 때 설정되기도 합니다.
Driver와 consumer 구현 시 다음 주의 사항이 있습니다.
- 1. Channel이 한 번 private으로 할당되면 `dma_release_channel()` 이후에도 범용 allocator의 고려 대상이 되지 않습니다.
- 2. Capability는 device 수준에서 지정되므로 여러 channel이 있는 `dma_device`는 모든 channel이 public이거나 모두 private입니다.
5. Source
254-2705. Source
| Source path | 역할 |
|---|---|
| `include/linux/dmaengine.h` | DMA driver와 API 사용자를 위한 core header |
| `drivers/dma/dmaengine.c` | Offload engine channel 관리 routine |
| `drivers/dma/` | Offload engine driver 위치 |
| `include/linux/async_tx.h` | `async_tx` API core header |
| `crypto/async_tx/async_tx.c` | dmaengine에 대한 `async_tx` interface와 공통 code |
| `crypto/async_tx/async_memcpy.c` | Copy offload |
| `crypto/async_tx/async_xor.c` | XOR 및 XOR zero-sum offload |
요약과 해설
async-tx-api.rst:1-270`async_tx`는 `xor->copy->xor`처럼 서로 의존하는 bulk memory 연산을 hardware offload와 software fallback 사이에서 같은 API로 실행합니다. Descriptor acknowledge와 dependency 연결이 재활용 시점을 제어합니다.
Driver는 tasklet 완료 callback, IRQ context 제약, dependency 정리 계약을 지켜야 합니다. Device-to-memory처럼 공유할 수 없는 channel은 `dma_request_channel()`과 `filter_fn`, `DMA_PRIVATE`로 독점 할당합니다. 원문의 연산 표와 source path 표를 구조화된 표로 보존했습니다.