Skip to content

DMA Completion and Transaction Status

Overview

DMA completion is not a single event.

When DMA hardware finishes transferring data, several software-visible events may follow:

  • The DMA controller reports hardware completion.
  • DMA Engine records the transaction cookie as completed.
  • A client may observe the transaction through a status query.
  • Deferred completion processing may be scheduled.
  • The client callback may execute later.
  • The descriptor may finally be released or prepared for reuse.

These events are related, but they do not necessarily occur at the same time.

Understanding this separation is important when working with asynchronous DMA because a transaction may already report DMA_COMPLETE before its callback executes.

This page explains:

  • Hardware completion and cookie completion
  • DMA transaction status queries
  • DMA cookie completion tracking and wrap-around
  • struct dma_tx_state
  • DMA residue reporting
  • Completion callbacks and result-aware callbacks
  • virt-dma completion processing
  • Callback execution context and descriptor finalization

Completion Is Not a Single Event

A useful completion model separates five different concepts:

Hardware Completion
        |
        v
Cookie Completion
        |
        v
Transaction Status
        |
        v
Deferred Completion Processing
        |
        v
Callback Execution

These concepts describe different aspects of the same DMA transaction.

Concept Meaning
Hardware completion The DMA controller has finished or stopped the hardware transfer.
Cookie completion DMA Engine records the submitted transaction as completed.
Transaction status A client queries the current state of a previously submitted transaction.
Deferred completion processing Software processes completed descriptors outside the immediate hardware-completion path.
Callback execution The client receives asynchronous notification of a completion event.

The ordering between these events matters.

For example, cookie completion may already be visible to a status query while the client callback is still pending.


Completion and Status Architecture

DMA completion information crosses several layers:

Client Driver
      |
      | dmaengine_tx_status()
      | callback / callback_result
      v
DMA Engine Framework
      |
      | cookie bookkeeping
      | generic callback abstraction
      v
DMA Controller Driver
      |
      | hardware completion handling
      | controller-specific status
      | residue calculation
      v
DMA Hardware

Each layer has a different responsibility.

DMA Hardware

The DMA hardware performs the actual data transfer.

Depending on the controller, hardware may expose information such as:

  • Transfer-complete interrupts
  • Error conditions
  • Current descriptor state
  • Remaining transfer count
  • Current segment or burst position

The available information is hardware-specific.

DMA Controller Driver

The controller driver translates hardware state into DMA Engine semantics.

Typical responsibilities include:

  • Handling DMA completion interrupts
  • Tracking the active descriptor
  • Recording transaction completion
  • Reporting controller-specific transaction status
  • Calculating residue when supported
  • Preparing completion result information

DMA Engine Framework

DMA Engine provides generic transaction abstractions such as:

  • DMA cookies
  • Completion tracking
  • Status-query interfaces
  • Callback interfaces
  • Common descriptor-management helpers

The framework cannot infer detailed hardware progress by itself.

Client Driver

The client driver consumes the DMA Engine interfaces.

It may:

  • Save the cookie returned by dmaengine_submit()
  • Query the transaction with dmaengine_tx_status()
  • Receive asynchronous completion callbacks
  • Use residue information when supported

Transaction Status Query

A submitted DMA transaction can be queried through:

dmaengine_tx_status()

Conceptually:

Client Driver
      |
      | dmaengine_tx_status()
      v
DMA Engine
      |
      | device_tx_status()
      v
Controller Driver
      |
      +--> generic cookie status
      |
      +--> controller-specific progress
      |
      v
enum dma_status
struct dma_tx_state

dmaengine_tx_status() does not control DMA execution.

It observes the transaction state exposed by the DMA controller driver.

A status query may therefore report information that is less precise than the hardware's actual instantaneous progress.


dmaengine_tx_status()

dmaengine_tx_status() is the client-facing DMA Engine interface for querying a transaction.

The client provides:

  • The DMA channel
  • The cookie returned when the transaction was submitted
  • An optional struct dma_tx_state

The framework dispatches the request to the DMA controller driver's device_tx_status() operation.

Conceptually:

dmaengine_tx_status()
        |
        v
chan->device->device_tx_status()

The returned enum dma_status describes the current transaction state.

Common states include:

  • DMA_COMPLETE
  • DMA_IN_PROGRESS
  • DMA_PAUSED
  • DMA_ERROR

The exact status information available depends on the controller driver.


device_tx_status()

device_tx_status() is a DMA controller operation exposed through struct dma_device.

It is implemented by the controller driver rather than by the DMA client.

A common implementation pattern is:

device_tx_status()
        |
        +--> dma_cookie_status()
        |
        +--> inspect controller state
        |
        +--> inspect hardware progress
        |
        +--> calculate residue
        |
        v
return enum dma_status

The generic cookie state can determine whether the transaction has crossed the completion boundary.

More detailed progress information, however, normally requires controller-specific state or hardware registers.

Therefore:

Cookie tracking provides generic transaction completion information, while detailed progress reporting is controller-specific.


DMA cookies identify submitted transactions.

A DMA channel maintains cookie bookkeeping that allows the framework to distinguish completed transactions from transactions that are still outstanding.

Two important channel values are conceptually:

chan->cookie
chan->completed_cookie

chan->cookie tracks the most recently assigned transaction cookie.

chan->completed_cookie tracks the completion boundary.

When a descriptor is submitted, dma_cookie_assign() assigns the next transaction cookie.

When the transaction completes, dma_cookie_complete() updates the channel's completion state.

Conceptually:

Submit transaction
        |
        v
dma_cookie_assign()
        |
        v
cookie = N
        |
        | hardware eventually completes
        v
dma_cookie_complete()
        |
        v
completed_cookie = N

Cookie completion is independent from callback execution.


DMA cookies use positive values for valid submitted transactions.

Conceptually:

cookie < 0
    |
    +--> submission error

cookie = 0
    |
    +--> no valid submitted transaction cookie

cookie > 0
    |
    +--> valid transaction cookie

Valid transaction cookies begin at DMA_MIN_COOKIE.

A descriptor that has not yet received a valid submitted transaction cookie may therefore use zero as the absence of a valid transaction cookie.

Submission errors are handled separately through the DMA submission error mechanism.

Status helpers assume that callers provide cookies obtained from valid submitted transactions rather than arbitrary integer values.


DMA cookies are finite integer values, so the sequence eventually wraps.

DMA Engine does not maintain a separate generation number or wrap counter for transaction-status comparison.

Instead, completion is determined from the relative positions of:

cookie
last_complete
last_used

The generic helper divides the sequence into two cases.

No Wrap in the Outstanding Window

When:

last_complete <= last_used

the outstanding range is:

(last_complete, last_used]

Conceptually:

... COMPLETE ... | outstanding transactions | ...
                 ^                          ^
           last_complete                last_used

A queried cookie inside this interval is still in progress.

Cookies outside the interval are considered complete relative to the current cookie window.

Outstanding Window Crosses Wrap-Around

When:

last_complete > last_used

the outstanding range crosses the cookie wrap boundary.

Conceptually:

             wrap
               |
               v

... last_complete | ... MAX ... MIN ... | last_used ...
                   \_____________________/
                       outstanding

The same comparison can therefore determine completion without maintaining an additional ring-generation counter.

The relevant helper is:

dma_async_is_complete()

Its responsibility is ordering and completion-range calculation, not cookie validity checking.


dma_cookie_status() uses the channel cookie bookkeeping to determine generic transaction completion state.

Conceptually:

dma_cookie_status()
        |
        +--> requested cookie
        +--> completed_cookie
        +--> last assigned cookie
        |
        v
dma_async_is_complete()
        |
        v
DMA_COMPLETE
or
DMA_IN_PROGRESS

This helper does not know detailed hardware progress.

For example, it cannot determine by itself:

  • How many bytes have already transferred
  • Which scatter-gather segment is active
  • Whether the hardware is partway through a burst

Those details must come from the controller driver when available.


struct dma_tx_state

struct dma_tx_state carries additional transaction-state information returned through the status-query path.

The most important information for normal DMA progress reporting is the residue.

Conceptually:

dmaengine_tx_status()
        |
        v
device_tx_status()
        |
        +--> status
        |
        +--> residue
        |
        v
struct dma_tx_state

The structure represents query-time state.

It should not be confused with struct dmaengine_result, which represents completion-result information delivered through a result-aware callback.

A useful distinction is:

struct dma_tx_state
    |
    +--> What is the transaction state now?

struct dmaengine_result
    |
    +--> How did the completion event finish?

DMA Residue

DMA residue represents the amount of transfer data that has not yet been completed according to the progress information available to the controller driver.

For example:

Total transfer       = 4096 bytes
Transferred           = 3072 bytes
Reported residue      = 1024 bytes

However, the numerical value is not necessarily an exact byte-by-byte view of current hardware progress.

The accuracy depends on what the DMA controller and its driver can observe.


Residue Granularity

DMA Engine describes residue-reporting capability through several granularity levels.

Granularity Meaning
DMA_RESIDUE_GRANULARITY_DESCRIPTOR Progress can only be distinguished at descriptor level.
DMA_RESIDUE_GRANULARITY_SEGMENT Progress can be distinguished at transfer-segment boundaries.
DMA_RESIDUE_GRANULARITY_BURST Progress can be reported with burst-level precision.

Descriptor Granularity

With descriptor-level granularity, the controller may only know whether the entire descriptor is still active or has completed.

For a 4096-byte transaction:

Hardware reality:

[======================--------]
          running

Reported view:

Descriptor still active

The driver may not be able to report the exact number of bytes already transferred.

Segment Granularity

For a scatter-gather transaction:

SG[0]  DONE
SG[1]  DONE
SG[2]  ACTIVE

the controller may know which segment is active without knowing the exact byte position inside that segment.

The reported residue therefore reflects segment-level progress.

Burst Granularity

Some controllers expose finer transfer counters.

If hardware progress can be observed at burst boundaries, the controller driver can provide more precise residue information.

The important distinction is:

Residue is expressed as a remaining data amount, but its actual accuracy is limited by the controller's residue-reporting capability.


Status Reporting Does Not Control DMA Execution

Transaction-status reporting is an observation path.

For example:

DMA Hardware

SG[0] complete
SG[1] currently transferring
SG[2] pending

A controller with segment-level reporting may still expose progress corresponding to the most recently known segment boundary.

Meanwhile, the hardware continues transferring data independently.

Conceptually:

DMA Execution Path

Hardware continues transfer
        |
        v
Hardware progress changes
        |
        v
Driver updates progress when supported


Status Query Path

dmaengine_tx_status()
        |
        v
Read current driver/hardware state
        |
        v
return dma_tx_state

A less precise or slightly delayed status report does not by itself stop or delay DMA execution.


Callback Completion

DMA Engine supports asynchronous client notification through descriptor callbacks.

Two callback forms are relevant:

dma_async_tx_callback
dma_async_tx_callback_result

The basic callback provides completion notification.

The result-aware callback additionally receives a struct dmaengine_result.

Conceptually:

Completion Event
      |
      +--> callback()
      |
      +--> callback_result(..., result)

The callback interface is associated with the DMA transaction descriptor.


struct dmaengine_result

struct dmaengine_result describes the outcome associated with a completion callback.

It contains information such as:

  • Transaction result
  • Completion residue

This is different from struct dma_tx_state.

The distinction is:

Object Purpose
struct dma_tx_state Query-time transaction state and progress
struct dmaengine_result Completion-time result information

For a successful transaction, a result-aware callback may observe a successful result with zero residue.

For an aborted or failed transfer, the result may describe the failure while residue reports the amount that remained when the transaction ended.

The exact result information depends on the DMA controller and completion path.


Transaction Status vs Completion Result

enum dma_status and enum dmaengine_tx_result answer different questions.

Transaction Status

enum dma_status answers:

What state is the transaction in?

Examples include:

DMA_IN_PROGRESS
DMA_COMPLETE
DMA_PAUSED
DMA_ERROR

Completion Result

enum dmaengine_tx_result answers:

What was the outcome associated with the completion event?

Examples include:

DMA_TRANS_NOERROR
DMA_TRANS_READ_FAILED
DMA_TRANS_WRITE_FAILED
DMA_TRANS_ABORTED

These two interfaces should not be treated as direct one-to-one mappings.

For example, a transaction may no longer be executing because it was aborted, but that does not mean the requested transfer completed successfully.


Callback-Based Completion vs Status Polling

Callbacks and status queries are two different ways for a DMA client to observe transaction progress.

Callback-Based Completion

Callback notification is event-driven:

DMA running
      |
      v
Completion event
      |
      v
Deferred completion processing
      |
      v
Client callback

The client does not need to repeatedly query whether the transaction has completed.

Status Polling

Status polling is query-driven:

Client
   |
   | dmaengine_tx_status()
   v
Current transaction state

It is also useful when the client needs current progress information such as residue.

They Are Not Mutually Exclusive

A transaction may use a callback while still being queried through its cookie.

For example:

Transaction running
      |
      +--> tx_status()
      |       |
      |       +--> DMA_IN_PROGRESS
      |
      v
Cookie completed
      |
      +--> tx_status()
      |       |
      |       +--> DMA_COMPLETE
      |
      v
Callback executes later

Therefore:

Callback notification and transaction-status queries are independent observation mechanisms.


DMA Completion and Status Timeline

The following timeline shows the relationship between descriptor state, hardware execution, cookie completion, status queries, and deferred callback execution.

DMA completion and transaction status timeline

The important ordering is:

Hardware Complete
        |
        v
Cookie Complete
        |
        | dmaengine_tx_status()
        | may already return DMA_COMPLETE
        v
Deferred Callback
        |
        v
Descriptor Finalization

Hardware completion, cookie completion, callback execution, and descriptor finalization should therefore not be treated as the same event.


virt-dma Completion Processing

virt-dma provides common descriptor-management infrastructure used by many DMA controller drivers.

It is an implementation helper, not a requirement for every DMA controller driver.

For a normal non-cyclic descriptor, a typical completion path begins with:

vchan_cookie_complete()

Conceptually:

Controller completion path
        |
        v
vchan_cookie_complete()
        |
        +--> dma_cookie_complete()
        |
        +--> desc_completed
        |
        +--> tasklet_schedule()

dma_cookie_complete() records generic transaction completion.

The descriptor is then queued for deferred completion processing.

The callback does not execute synchronously inside vchan_cookie_complete().


vchan_complete()

The virt-dma tasklet later processes completed descriptors through vchan_complete().

A simplified normal-descriptor path is:

vchan_complete()
      |
      v
lock vc->lock
      |
      v
move desc_completed to local list
      |
      v
unlock vc->lock
      |
      v
iterate local completed descriptors
      |
      v
obtain callback information
      |
      v
invoke callback
      |
      v
finalize descriptor

Moving a descriptor out of desc_completed does not destroy the descriptor object.

It only changes its list membership.

The descriptor remains valid until its finalization path releases it or prepares it for reuse.


Shared List vs Descriptor Lifetime

Linux list membership and object lifetime are separate concepts.

Conceptually:

vc->desc_completed

[A] -> [B] -> [C]

vchan_complete() can detach these descriptors into a local list:

vc->desc_completed
    empty

local list
[A] -> [B] -> [C]

Descriptors A, B, and C still exist.

They can therefore still provide:

  • Callback information
  • Completion result information
  • Controller-specific descriptor state

Only the later descriptor-finalization step determines whether the object is released or reused.


Descriptor Finalization and Reuse

After callback processing, virt-dma finalizes the descriptor.

Conceptually:

Completed descriptor
        |
        v
Callback processing
        |
        v
Descriptor finalization
        |
        +--> release descriptor
        |
        +--> return reusable descriptor
             to an allocated state

A reusable descriptor object may later represent another DMA transaction.

If it is submitted again, the new transaction receives a new cookie.

Therefore:

Descriptor object identity
        !=
Transaction identity
        !=
Cookie identity

A cookie identifies one submitted transaction, not the permanent lifetime of a descriptor object.


Callback Information Inside DMA Engine

DMA client drivers normally use the public DMA Engine declarations from:

#include <linux/dmaengine.h>

During internal completion processing, DMA Engine also uses private helper abstractions under drivers/dma/.

For example, internal completion code can snapshot callback information before invoking client code.

Conceptually:

struct dma_async_tx_descriptor
        |
        v
internal callback snapshot
        |
        v
invoke callback

This internal abstraction allows DMA Engine completion code to separate descriptor bookkeeping from callback invocation.

These helpers are DMA Engine implementation details and are not APIs that normal DMA client drivers should include directly.


Callback Locking

Client callback code should not execute while the controller's internal virtual-channel lock is held.

A typical virt-dma pattern is:

lock vc->lock
      |
      v
detach shared completion state
      |
      v
unlock vc->lock
      |
      v
invoke client callback

This keeps the critical section short and avoids executing arbitrary client code while holding DMA Engine internal state locks.

A callback may interact with other kernel mechanisms or initiate additional DMA operations, so invoking it outside the original completion critical section reduces locking and re-entrancy problems.


Callback Execution Context

For the virt-dma completion path described here, callbacks are dispatched through tasklet-based deferred processing.

Conceptually:

DMA Hardware
      |
      v
Completion IRQ
      |
      v
vchan_cookie_complete()
      |
      v
tasklet_schedule()
      |
      v
Tasklet / softirq context
      |
      v
Client callback

Tasklet context is not Process Context.

Code running in this path must therefore respect atomic-context restrictions and must not perform operations that may sleep.

If completion requires sleepable or longer-running processing, the callback can defer that work again to an appropriate mechanism such as a workqueue.

The exact callback execution mechanism may differ for DMA controller implementations that do not use this virt-dma completion path.


Cyclic DMA Note

vchan_complete() also contains handling for cyclic DMA period callbacks.

A cyclic period callback does not mean that the entire cyclic DMA transaction has completed.

Conceptually:

Cyclic DMA transaction
        |
        +--> Period 0 complete
        |       |
        |       +--> callback
        |
        +--> Period 1 complete
        |       |
        |       +--> callback
        |
        +--> Period 2 complete
                |
                +--> callback

The descriptor remains active while the cyclic transfer continues.

Cyclic DMA has a different completion model and is outside the scope of this page.


Complete Mental Model

A normal asynchronous DMA transaction can now be viewed through several independent state dimensions:

DMA Hardware
    |
    +--> running
    +--> hardware complete


DMA Descriptor
    |
    +--> active
    +--> completed queue
    +--> deferred processing
    +--> released / reused


DMA Cookie
    |
    +--> assigned
    +--> outstanding
    +--> complete


Status Query
    |
    +--> DMA_IN_PROGRESS
    +--> optional residue
    +--> DMA_COMPLETE


Callback
    |
    +--> pending
    +--> deferred execution
    +--> client notification

These dimensions are related but not synchronized to one identical completion point.

A particularly important ordering is:

Hardware completes
        |
        v
Cookie is marked complete
        |
        v
dmaengine_tx_status()
can report DMA_COMPLETE
        |
        v
Callback executes later
        |
        v
Descriptor is finalized

This separation allows DMA hardware scheduling, generic transaction bookkeeping, client observation, and software completion processing to operate asynchronously.


Kernel Source

Important source locations include:

Location Purpose
include/linux/dmaengine.h Public DMA Engine structures, status interfaces, callback types, and client-facing helpers
drivers/dma/dmaengine.c DMA Engine core implementation
drivers/dma/dmaengine.h DMA Engine internal helper interfaces
drivers/dma/virt-dma.h Common virtual DMA channel and descriptor infrastructure
drivers/dma/virt-dma.c Deferred virt-dma completion processing
drivers/dma/ Hardware-specific DMA controller drivers

Useful symbols to trace include:

dmaengine_tx_status()
device_tx_status()
dma_cookie_status()
dma_async_is_complete()
dma_cookie_complete()
vchan_cookie_complete()
vchan_complete()

When tracing source, keep the interface boundary clear:

include/linux/dmaengine.h
    |
    +--> kernel-facing DMA Engine interface

drivers/dma/dmaengine.h
drivers/dma/virt-dma.[ch]
    |
    +--> DMA Engine / virt-dma implementation internals

Day100 through Day103 primarily extend the architecture and kernel-source model around DMA controller, descriptor, submission, completion, and transaction status. A separate lab is not required when the learning work does not introduce a meaningful reproducible implementation or experiment.




Summary

DMA completion is a sequence of related events rather than one synchronous operation.

The DMA hardware first finishes or stops the transfer. The controller driver interprets the hardware state and updates DMA Engine bookkeeping. Cookie completion then allows transaction-status queries to observe the transaction as complete, while deferred completion processing and callback execution may occur later.

The key distinctions are:

  • Hardware completion is not callback execution.
  • Cookie completion is independent from callback execution.
  • dmaengine_tx_status() observes transaction state rather than controlling DMA execution.
  • struct dma_tx_state represents query-time state.
  • struct dmaengine_result represents completion-result information.
  • Residue accuracy depends on controller capabilities.
  • Cookie wrap-around is handled through circular range comparison rather than a separate generation counter.
  • virt-dma provides one common completion implementation, but it is not mandatory for every DMA controller.
  • Descriptor list membership and descriptor object lifetime are separate concepts.
  • A transaction may report DMA_COMPLETE before its callback executes.

The resulting mental model is:

Hardware Complete
        |
        v
Cookie Complete
        |
        v
Transaction Observable as Complete
        |
        v
Deferred Completion Processing
        |
        v
Callback
        |
        v
Descriptor Finalization