Skip to content

Day105 — Cyclic DMA and Periodic Callbacks

Today's Goal

Understand how Linux DMA Engine supports long-running cyclic DMA transactions and period-based callback notification.

The main goals were:

  • Understand the purpose of cyclic DMA.
  • Understand dmaengine_prep_dma_cyclic().
  • Distinguish a cyclic transaction from repeated one-shot DMA transactions.
  • Understand cyclic buffers and DMA periods.
  • Understand period callbacks.
  • Distinguish period completion from transaction completion.
  • Understand cyclic descriptor and cookie lifetime.
  • Understand how DMA controller drivers implement cyclic execution.
  • Trace the virt-dma cyclic callback path.
  • Understand producer/consumer behavior around cyclic buffers.
  • Understand DMA residue and cyclic buffer position.
  • Understand cyclic DMA termination and synchronization.
  • Identify when cyclic DMA is appropriate compared with periodically triggered one-shot DMA.

What I Learned

Cyclic DMA Represents One Long-Running Transaction

A cyclic DMA transaction repeatedly traverses a DMA buffer until the client explicitly stops the operation.

A typical cyclic buffer can be divided into multiple periods:

Cyclic Buffer

┌──────────────┐
│ Period 0     │
├──────────────┤
│ Period 1     │
├──────────────┤
│ Period 2     │
├──────────────┤
│ Period 3     │
└──────────────┘
       └────────→ wrap to Period 0

The important point is that these periods do not represent independent DMA transactions.

Conceptually:

One Cyclic Transaction

P0 → P1 → P2 → P3
↑              │
└──────────────┘

The transaction is prepared, submitted, and issued once.

It then continues cycling until termination or another provider-specific stop condition ends the operation.


Cyclic DMA Is Not Descriptor Reuse

Cyclic DMA should not be modeled as:

Complete
Reuse Descriptor
Submit Again
Next Period

Instead, the same cyclic transaction remains active while DMA progresses through multiple periods:

Cyclic Descriptor
Submit Once
Issue
Period 0
Period 1
Period 2
Period 3
Period 0
...

Therefore:

Cyclic wrap
Descriptor completion
      +
Descriptor reuse
      +
Resubmission

The cyclic behavior is part of the long-running transaction itself.


dmaengine_prep_dma_cyclic()

A DMA client prepares a cyclic transaction using:

dmaengine_prep_dma_cyclic(chan,
                          buf_addr,
                          buf_len,
                          period_len,
                          direction,
                          flags);

Important parameters include:

Parameter Purpose
buf_addr DMA address of the cyclic buffer.
buf_len Total cyclic buffer length.
period_len Size of each notification period.
direction DMA transfer direction.
flags Descriptor preparation flags.

For example:

buf_len    = 4096 bytes
period_len = 1024 bytes

┌──────────┬──────────┬──────────┬──────────┐
│    P0    │    P1    │    P2    │    P3    │
└──────────┴──────────┴──────────┴──────────┘
   1024       1024       1024       1024

In this simple case, the buffer contains four periods.


DMA Engine Defines the Interface, Not the Hardware Ring

dmaengine_prep_dma_cyclic() provides the generic DMA Engine interface for requesting cyclic operation.

Conceptually:

DMA Client
dmaengine_prep_dma_cyclic()
DMA Engine Framework
device_prep_dma_cyclic()
DMA Controller Driver
DMA Hardware

The DMA controller driver translates the generic cyclic request into a controller-specific representation.

One controller might construct a hardware descriptor ring:

HW Desc 0
HW Desc 1
HW Desc 2
HW Desc 3
    └────────→ HW Desc 0

Another controller may provide hardware registers or a native circular mode.

Therefore:

DMA Engine defines cyclic transaction semantics, but the mechanism that performs the actual wrap is controller- and hardware-specific.

The generic DMA Engine framework does not resubmit the first period after the final period completes.


A Period Is Not a DMA Transaction

A period is a progress and notification unit inside the cyclic transaction.

For example:

One Descriptor
One Cookie
One Long-Running Transaction

P0 → P1 → P2 → P3 → P0 → ...
│    │    │    │
cb   cb   cb   cb

The individual periods do not receive separate DMA cookies and are not independently submitted through dmaengine_submit().

Therefore:

One Period
One DMA Transaction

Period Callback vs Transaction Completion

This was the most important distinction of the session.

For normal one-shot DMA:

Transaction Finished
Cookie Completion
Completion Processing
Client Callback

For cyclic DMA:

Period Boundary
Period Callback
Same Cyclic Transaction Continues

Therefore:

Period callback
Transaction completion

A period callback reports progress within a long-running transaction.

It does not normally retire the cyclic descriptor.


A cyclic transaction receives a cookie when the descriptor is submitted.

For example:

cookie = 42

P0 → callback
P1 → callback
P2 → callback
P3 → callback
P0 → callback
...

All of these period callbacks belong to the same cyclic transaction.

A new cookie is not assigned for every period.

Therefore:

Period completion
dma_cookie_complete()

The cyclic transaction remains outstanding while it continues to run.

This also means generic cookie completion bookkeeping must not be interpreted as a per-period progress counter.


virt-dma Separates Cyclic Callback from Normal Completion

The virt-dma source provides an important implementation example.

For a normal descriptor, a controller may eventually call:

vchan_cookie_complete(vd);

which enters normal cookie and descriptor completion handling.

For a cyclic period event, a virt-dma-based controller may instead call:

vchan_cyclic_callback(vd);

Conceptually:

Controller Event
Is the active descriptor cyclic?
      ├── Yes
      │     ↓
      │  vchan_cyclic_callback()
      │     ↓
      │  Period callback
      │     ↓
      │  Descriptor remains active
      └── No
        vchan_cookie_complete()
        Normal transaction completion

This makes the distinction explicit:

vchan_cyclic_callback()
vchan_cookie_complete()

Why Cyclic Callbacks Still Reach vchan_complete()

The virt-dma deferred processing function is named:

vchan_complete()

The name can initially suggest that every event entering the function represents full transaction completion.

That is not the correct interpretation.

vchan_complete() also participates in deferred cyclic callback processing.

A better conceptual interpretation is:

vchan_complete()
Deferred completion / callback processing
        ├── Normal descriptor completion
        └── Cyclic period callback

Therefore the important distinction is not whether execution reaches a function whose name contains complete.

The important distinction is:

Cyclic Period Completion
Normal Transaction Completion

Callback Execution Does Not Stop DMA Progress

When a period boundary is reached, the DMA controller can continue into the next period while software handles the callback.

For RX:

CPU                         DMA

                            Fill P0
                         P0 Boundary
P0 Callback  ←──────── notification
    │                          │
Process P0                  Fill P1
    │                          │
    ▼                          ▼

The callback is asynchronous relative to DMA hardware progress.

By the time the callback actually executes, the hardware may already have progressed into the next period.

Therefore:

A period callback indicates that a period boundary occurred; it does not mean that the DMA hardware is currently stopped at that boundary.


RX Producer / Consumer Model

For peripheral-to-memory cyclic DMA:

Peripheral
DMA
Cyclic Buffer

The roles can be modeled as:

DMA Hardware = Producer
CPU / Client = Consumer

When DMA completes P0 and continues into P1, software can process the completed P0 region.

Conceptually:

P0             P1
┌──────────────┬──────────────┐
│ CPU consumes │ DMA writes   │
└──────────────┴──────────────┘

Software must finish the required processing before DMA eventually wraps and writes P0 again.


TX Producer / Consumer Model

For memory-to-peripheral cyclic DMA, the roles are reversed:

CPU / Client = Producer
DMA Hardware = Consumer

After DMA consumes a period, software may prepare that region for future reuse according to the client or subsystem protocol.

Therefore the meaning of a period callback depends partly on transfer direction.

For RX it commonly indicates that newly received data is available.

For TX it commonly indicates that a region has been consumed and can eventually be prepared for future transmission.


Software Must Keep Up With the DMA Stream

Cyclic DMA does not provide unlimited buffering.

For example:

DMA:

P0 → P1 → P2 → P3 → P0
                 overwrite

If software is still processing the old P0 when DMA returns to it, the old data may no longer be valid.

Similarly, a TX producer that cannot prepare data quickly enough may experience an underrun or other subsystem-specific failure.

Therefore:

Period callbacks provide progress notification, but they do not preserve historical buffer contents.

Overrun, underrun, and recovery policies are client-, subsystem-, and controller-specific.


Period Callback Is Not an Unlimited Event Queue

A cyclic callback mechanism should not be modeled as an unlimited queue that permanently records every historical period boundary.

Conceptually, it is safer to treat the callback as:

DMA Progress
Period Notification
Update / Wake Software Processing

Even if software could retain notification history, the DMA hardware may already have wrapped and overwritten old buffer regions.

Correct streaming behavior therefore depends on the consumer or producer keeping up with the DMA data rate.


DMA Residue and Cyclic Position

dmaengine_tx_status() can provide:

struct dma_tx_state

including:

state.residue

For a controller that provides suitable residue reporting, residue may help software infer the DMA position inside a cyclic buffer.

A common conceptual relationship is:

position = buf_len - residue

However, this must not be treated as an unconditional byte-accurate DMA Engine guarantee.

Residue accuracy depends on the DMA controller and its advertised residue granularity.

Therefore:

Cookie
    → Transaction lifecycle/status

Residue
    → Transfer progress/position information

Period Callback
    → Period progress notification

These mechanisms represent different aspects of the cyclic transaction.


Cyclic DMA Normally Requires Explicit Stop

A cyclic transaction is intentionally long-running.

Conceptually:

Submit
Issue
P0 → P1 → P2 → P3
↑              │
└──────────────┘

There is normally no final period that automatically turns the cyclic transaction into a normal completed transaction.

The client eventually requests termination when its own stop condition is reached.

Possible stop conditions include:

  • A requested number of periods has been processed.
  • A requested streaming duration has elapsed.
  • Userspace requests the stream to stop.
  • The device is being removed.
  • An error requires shutdown.

Finite Duration Can Still Use Cyclic DMA

Cyclic DMA does not mean that the transfer must run forever.

For example, a client can:

Start Cyclic DMA
Process 100 Periods
Stop

or:

Start Cyclic DMA
Run Continuously for 10 Seconds
Stop

Both can be reasonable cyclic DMA use cases because the DMA stream remains continuous while it is active.

The finite count or duration is a client policy rather than the definition of the DMA transaction.


Periodically Triggered DMA Is Different

A requirement such as:

DMA Transfer
Idle N Seconds
DMA Transfer
Idle N Seconds
...

is usually not a typical cyclic DMA workload.

It is more naturally modeled as:

Timer / Scheduled Work
One-Shot DMA
Completion
Wait
Next One-Shot DMA

This distinguishes two different meanings of periodic behavior:

Cyclic DMA
    → continuous DMA stream
      repeatedly traversing a buffer

Periodic DMA Triggering
    → separate DMA transactions
      started at time intervals

Cyclic DMA Termination

The Day104 termination model applies directly to cyclic DMA.

The client should first prevent new client-side DMA work and then terminate the long-running transaction:

Client RUNNING
Client STOPPING
Prevent New DMA Work
Terminate Cyclic DMA

Termination should not be interpreted as forcing the cyclic transaction through normal successful completion.

Therefore:

Normal Completion
Termination

The exact controller-side descriptor and cookie handling during termination is provider-specific.


Period Callback Can Race With Termination

A period event may already have scheduled deferred callback processing when another execution context begins termination.

For example:

DMA / Callback Side             Client Side

Period Boundary
Callback Pending
                                  STOP
                           terminate_async()
Deferred Callback

Stopping DMA hardware does not automatically prove that all previously scheduled callback activity has quiesced.

Therefore:

DMA Hardware Stopped
Callback Quiescent

Synchronization Establishes the Cleanup Boundary

After asynchronous termination, the client may need:

dmaengine_synchronize(chan);

Conceptually:

Terminate
Synchronize
DMA-Related Asynchronous Activity Quiescent
Safe Cleanup

Alternatively, when the execution context allows synchronous waiting:

dmaengine_terminate_sync(chan);

can combine termination and synchronization.

This is especially important for cyclic DMA because period callbacks are intentionally generated repeatedly while the stream is active.

DMA buffers, callback parameters, and client state must remain valid until the relevant asynchronous activity has quiesced.


Reusable Diagram

The cyclic DMA lifecycle is summarized by:

docs/assets/diagrams/kernel-driver/dma/
├── dma-cyclic-lifecycle.drawio
└── dma-cyclic-lifecycle.svg

The diagram emphasizes:

  • One cyclic descriptor.
  • One cookie.
  • One long-running transaction.
  • Multiple period callbacks.
  • Continuous hardware progress.
  • Cyclic wrap.
  • Termination.
  • Synchronization.
  • Safe cleanup.

Important Observations

  • Cyclic DMA represents one long-running transaction rather than repeated one-shot transactions.
  • A cyclic descriptor is normally submitted once.
  • Individual periods are not independent DMA transactions.
  • Period callbacks report progress within the cyclic transaction.
  • Period completion does not normally complete the DMA cookie.
  • Cyclic DMA does not perform descriptor completion, reuse, and resubmission at every period.
  • The controller driver and DMA hardware implement the actual cyclic execution mechanism.
  • vchan_cyclic_callback() and vchan_cookie_complete() represent different lifecycle events.
  • Reaching vchan_complete() does not by itself imply normal transaction completion.
  • DMA hardware can continue into the next period while software processes a previous period callback.
  • RX cyclic DMA can be modeled as DMA producer and CPU consumer.
  • TX cyclic DMA can be modeled as CPU producer and DMA consumer.
  • Software must keep up with the cyclic stream to avoid overrun or underrun conditions.
  • Period callbacks should not be treated as unlimited historical event storage.
  • Residue may help determine cyclic DMA position, but its accuracy is controller-dependent.
  • A cyclic transaction can still have a finite client-defined count or duration.
  • Periodically starting independent DMA transactions is different from cyclic DMA.
  • Cyclic DMA normally requires explicit termination.
  • Hardware termination does not guarantee callback quiescence.
  • Synchronization is required before releasing resources that old cyclic DMA activity may still reference.

Lab

No Day105 lab was created.

The session focused on cyclic DMA semantics, period callbacks, DMA controller responsibilities, virt-dma source analysis, producer/consumer behavior, residue reporting, and termination lifetime.

A useful cyclic DMA lab requires a suitable DMA-capable peripheral and controller path that can demonstrate continuous cyclic operation and real period notifications. A synthetic userspace-style simulation would not provide enough additional value over the source and lifecycle analysis performed during this session.


Summary

Day105 extended the DMA transaction model from finite one-shot transfers to long-running cyclic operation.

The complete lifecycle is:

Prepare Cyclic Descriptor
Submit Once
Cookie Assigned
Issue
┌─────────────────────────────┐
│      Cyclic Running         │
│                             │
│ P0 → callback               │
│ P1 → callback               │
│ P2 → callback               │
│ P3 → callback               │
│  ↑                          │
│  └──────── wrap ────────────┘
└─────────────────────────────┘
Client STOPPING
Prevent New DMA Work
Terminate
Synchronize
Safe Cleanup

The most important distinction is:

A cyclic DMA period callback reports progress within a long-running transaction; it does not normally complete or retire that transaction.


Next Plan

Day106 will continue the DMA Engine learning path.

The next topic should build on the completed cyclic DMA, completion, termination, and synchronization models and move toward the next stage of DMA client-driver integration.