Day103 — DMA Completion, Callback, and Transaction Status¶
Today's Goal¶
Understand how Linux DMA Engine tracks transaction completion and exposes completion state to DMA client drivers.
The main goals were:
- Distinguish hardware completion from DMA Engine transaction completion.
- Understand
dmaengine_tx_status(). - Understand
device_tx_status(). - Understand
dma_cookie_status()anddma_async_is_complete(). - Understand
struct dma_tx_state. - Understand DMA residue reporting.
- Understand DMA cookie wrap-around.
- Understand completion callbacks and result-aware callbacks.
- Trace the
virt-dmacompletion path. - Compare callback-based completion with transaction-status queries.
- Understand descriptor lifetime after hardware completion.
What I Learned¶
DMA Completion Is Not a Single Event¶
DMA completion should not be treated as one synchronous event.
A more accurate model is:
Hardware Completion
↓
Cookie Completion
↓
Transaction Status Becomes Observable
↓
Deferred Completion Processing
↓
Callback Execution
↓
Descriptor Finalization
These stages may occur at different times.
In particular:
A DMA transaction may already report DMA_COMPLETE while its callback is still waiting for deferred execution.
Hardware Completion vs Cookie Completion¶
Hardware completion is detected by the DMA controller and its controller driver.
The DMA Engine Framework does not directly know that hardware has completed a transfer.
The controller driver translates hardware completion into DMA Engine transaction state.
For a virt-dma based implementation, the completion path commonly reaches:
which includes:
dma_cookie_complete() updates generic DMA cookie bookkeeping.
Therefore:
Hardware completion
↓
Controller Driver
↓
dma_cookie_complete()
↓
DMA Engine transaction completion state
dmaengine_tx_status()¶
A DMA client can query a previously submitted transaction using:
The API does not directly inspect DMA hardware.
Conceptually:
dmaengine_tx_status() is therefore an observation interface rather than a DMA control operation.
Calling it does not start, stop, or pause the DMA transaction.
device_tx_status()¶
device_tx_status() is a DMA controller operation provided through struct dma_device.
A controller driver commonly combines:
Conceptually:
device_tx_status()
↓
dma_cookie_status()
↓
generic completion state
+
controller-specific hardware state
↓
status + residue
Generic DMA cookie tracking determines whether a transaction has crossed the completion boundary.
Detailed progress reporting normally requires controller-specific information.
dma_cookie_status()¶
dma_cookie_status() determines generic transaction state from the channel's cookie bookkeeping.
Important values include:
The queried cookie is compared against these boundaries.
The generic cookie mechanism can determine whether a transaction is:
or:
but it does not know detailed DMA hardware progress.
Cookie Wrap-Around¶
DMA cookies eventually wrap because dma_cookie_t has a finite range.
DMA Engine does not maintain an additional ring-generation counter.
Instead, dma_async_is_complete() handles two cases.
When:
the outstanding transaction interval does not cross the wrap boundary.
When:
the outstanding transaction interval crosses the wrap boundary.
The requested cookie is then checked against the appropriate interval.
Therefore the algorithm is essentially:
Determine whether the outstanding range wraps
↓
Determine whether the queried cookie lies
inside the outstanding range
No separate wrap counter is required.
Cookie Value Zero¶
Valid submitted DMA transaction cookies begin at:
which is a positive value.
A useful mental model is:
cookie < 0
→ submission error
cookie = 0
→ no valid submitted transaction cookie
cookie > 0
→ valid transaction cookie
dma_submit_error() checks submission errors rather than performing complete cookie validation.
Status helpers assume that the caller provides a cookie obtained from a valid submitted transaction.
struct dma_tx_state¶
struct dma_tx_state represents transaction state observed through the status-query path.
Important information includes:
The structure represents a snapshot of transaction state at query time.
It should not be confused with:
which represents completion-result information supplied through a result-aware callback.
The distinction is:
DMA Residue¶
DMA residue represents the remaining transfer amount according to the progress information available to the controller driver.
However:
Residue is expressed as a remaining byte count, but its accuracy depends on controller capability.
DMA Engine defines several residue granularities:
Descriptor granularity provides only coarse transaction-level progress.
Segment granularity can distinguish transfer-segment boundaries.
Burst granularity can provide finer progress inside a segment.
A controller may therefore continue transferring data while the reported status reflects only the most recently observable progress boundary.
Status reporting does not control DMA execution.
Callback vs callback_result¶
DMA descriptors support two callback forms:
A normal callback provides asynchronous notification.
A result-aware callback additionally receives:
which can contain:
The two callback interfaces therefore represent:
callback
→ completion notification
callback_result
→ completion notification
+ completion outcome
+ completion residue
Transaction Status vs Completion Result¶
enum dma_status and enum dmaengine_tx_result represent different concepts.
enum dma_status answers:
Examples include:
enum dmaengine_tx_result answers:
Examples include:
Therefore:
The first is transaction status.
The second is completion outcome.
DMA Engine Internal Callback Helpers¶
Normal DMA client drivers include:
Internal DMA Engine implementation code also uses:
This internal header contains helpers such as:
These are DMA Engine implementation details rather than normal DMA client-driver APIs.
This clarified the boundary between:
include/linux/dmaengine.h
→ kernel-facing DMA Engine API
drivers/dma/dmaengine.h
→ DMA Engine internal implementation
virt-dma Deferred Completion¶
For normal non-cyclic DMA descriptors, the virt-dma completion path is conceptually:
Hardware completion
↓
vchan_cookie_complete()
↓
dma_cookie_complete()
↓
desc_completed
↓
tasklet_schedule()
↓
vchan_complete()
↓
Client callback
↓
Descriptor finalization
The descriptor is not destroyed when it is removed from desc_completed.
Removing an object from a Linux linked list only changes its list membership.
It does not free the object.
Therefore:
The descriptor remains valid until the later finalization step.
Local Completion List¶
vchan_complete() first moves completed descriptors from the shared channel list to a local list while holding vc->lock.
Conceptually:
This separates shared queue manipulation from callback processing.
New hardware completions can continue entering vc->desc_completed while the tasklet processes the detached local list.
This keeps the critical section short and allows asynchronous DMA completion to continue.
Callback Locking¶
Client callbacks should not execute while vc->lock is held.
The general pattern is:
This avoids running arbitrary client code while holding an internal DMA Engine spinlock.
Client callbacks may interact with other kernel mechanisms or initiate additional DMA transactions.
Descriptor Finalization and Reuse¶
After callback processing, virt-dma finalizes the descriptor.
A descriptor may either:
or:
If a reusable descriptor later represents another transaction, it receives a new transaction cookie when submitted again.
Therefore:
dma_cookie_complete() completes the current transaction represented by the descriptor.
It does not necessarily end the lifetime of the descriptor object itself.
Cyclic DMA Observation¶
While tracing vchan_complete(), the source also showed a separate cyclic callback path.
A cyclic DMA callback may represent:
rather than:
Conceptually:
The cyclic descriptor remains active.
Cyclic DMA was not studied in detail today and should be treated as a separate topic.
Callback Execution Context¶
For the virt-dma completion implementation examined today:
The callback therefore does not execute in Process Context.
Code in this callback path must respect atomic-context restrictions and must not perform operations that may sleep.
If sleepable processing is required, the callback can defer that work to a mechanism such as a workqueue.
This tasklet behavior is an implementation characteristic of the virt-dma path studied here and should not automatically be assumed for every possible DMA controller implementation.
Callback-Based Completion vs Status Query¶
DMA Engine supports two independent ways to observe a transaction.
Callback-based completion is event-driven:
Status queries are client-driven:
They may be used together.
For example:
DMA running
↓
dmaengine_tx_status()
↓
DMA_IN_PROGRESS
DMA completes
↓
dma_cookie_complete()
↓
dmaengine_tx_status()
↓
DMA_COMPLETE
↓
callback executes later
This demonstrates that transaction completion state and callback execution are independent.
Important Source-Code Discoveries¶
Important source locations examined today include:
Important symbols include:
dmaengine_tx_status()
device_tx_status()
dma_cookie_status()
dma_async_is_complete()
dma_cookie_complete()
struct dma_tx_state
struct dmaengine_result
struct dmaengine_desc_callback
vchan_cookie_complete()
vchan_complete()
vchan_vdesc_fini()
The most important implementation lesson was to keep public DMA Engine APIs separate from internal DMA Engine and virt-dma helpers.
Diagram¶
A reusable completion timeline was created:
docs/assets/diagrams/kernel-driver/dma/
dma-completion-status-timeline.drawio
dma-completion-status-timeline.svg
The diagram shows the ordering between:
Descriptor state
Hardware execution
Cookie state
Transaction-status queries
Deferred callback processing
Its main observation is:
Hardware Complete
↓
Cookie Complete
↓
DMA_COMPLETE may already be observable
↓
Callback executes later
↓
Descriptor finalization
Summary¶
Today completed the transaction-completion side of the DMA Engine mental model.
The most important conclusions are:
- Hardware completion, cookie completion, status reporting, callback execution, and descriptor finalization are separate events.
dmaengine_tx_status()queries transaction state through the controller'sdevice_tx_status()operation.dma_cookie_status()provides generic completion tracking based on DMA cookies.dma_async_is_complete()handles cookie wrap-around through outstanding-range comparison rather than a separate generation counter.struct dma_tx_staterepresents query-time state.struct dmaengine_resultrepresents completion-time result information.- Residue accuracy depends on DMA controller capabilities.
- Status queries observe DMA execution without controlling it.
virt-dmadefers normal completion callbacks through tasklet processing.- Removing a descriptor from a list does not destroy the descriptor object.
- Descriptor object lifetime and transaction cookie lifetime are independent.
- Cyclic period callbacks are different from normal transaction-completion callbacks.
The complete mental model is:
DMA Hardware
↓
Hardware Completion
↓
Controller Driver
↓
Cookie Completion
↓
Transaction Status Becomes Observable
↓
Deferred Completion Processing
↓
Client Callback
↓
Descriptor Finalization
Next Plan¶
Continue the DMA Engine learning path by studying the next transaction-management topic after completion and status tracking.
Potential follow-up areas include:
- DMA termination and synchronization
- Cyclic DMA
- DMA Engine integration in a real client driver
- Controller-specific DMA execution and error handling