Day109 Lab — One-Shot DMA Client Lifecycle¶
Goal¶
Build a simplified one-shot RX DMA client simulator and use it to verify the ordering between:
- DMA buffer allocation
- DMA mapping
- descriptor preparation
- descriptor submission
- issue pending
- peripheral start
- DMA completion
- CPU synchronization
- DMA unmapping
- DMA termination
- buffer release
The lab focuses on one central safety rule:
A DMA mapping and its backing buffer must remain valid
while the DMA transaction may still access them.
The simulator intentionally models only the client-side lifecycle. It does not attempt to reproduce a complete DMA controller driver or DMA Engine implementation.
1. Simulator Architecture¶
The lab combines previously implemented simulation layers:
Test Application
│
▼
DMA Client Simulator
│
├── transaction state
├── peripheral state
└── completion callback
│
▼
DMA Mapping Simulator
│
├── CPU-visible view
├── device-visible view
└── ownership synchronization
│
▼
Kernel Memory Simulator
│
└── kzalloc() / kfree()
Each layer has a separate responsibility.
The kernel-memory simulator owns allocation lifetime.
The DMA-mapping simulator owns CPU/device visibility and mapping lifetime.
The DMA-client simulator owns the transaction and peripheral lifecycle.
2. Simplified DMA State Machine¶
The DMA client simulator uses the following states:
State Meaning¶
| State | Meaning |
|---|---|
SIM_DMA_IDLE |
No descriptor has been prepared |
SIM_DMA_PREPARED |
Descriptor contains the DMA address, length, and direction |
SIM_DMA_SUBMITTED |
Descriptor has been submitted |
SIM_DMA_ARMED |
Transaction has been issued and is ready for peripheral DMA requests |
SIM_DMA_ACTIVE |
Peripheral is enabled and the transfer is active |
SIM_DMA_COMPLETED |
Transfer completed normally |
SIM_DMA_TERMINATED |
Outstanding transfer was synchronously terminated |
The distinction between ARMED and ACTIVE is intentional:
Issuing the DMA transaction does not itself mean that the peripheral has started producing DMA requests.
3. Request Object¶
The simulated request tracks three different categories of state:
sim_dma_request
│
├── CPU buffer
├── buffer length
│
├── DMA address
├── mapping state
│
├── descriptor state
│
└── peripheral enabled state
This separation is important because:
For example, after normal DMA completion:
The transaction has finished, but the mapping may still exist until the client releases it.
4. Initialization¶
The caller allocates the RX buffer:
The request initialization API stores the caller-owned buffer:
The DMA client simulator does not own the backing allocation.
Therefore:
The request object only references the buffer.
5. DMA Mapping¶
The RX buffer is mapped using a streaming DMA_FROM_DEVICE mapping:
The successful mapping is stored in the request:
Descriptor preparation is rejected unless the mapping exists.
This enforces:
6. Descriptor Preparation¶
The simulated descriptor records:
For the RX path:
These describe the same physical data direction:
but belong to different API domains and should not be treated as interchangeable constants.
7. Submit and Issue¶
Submission performs:
Issue pending performs:
At this point the test dumps the request.
Expected state:
This verifies that the DMA transaction can be ready before the peripheral starts.
8. Peripheral Start¶
Starting the simulated peripheral requires:
The operation then performs:
The resulting state is:
The simulated DMA transaction may now access the mapping.
9. Device-Side RX Write¶
DMA completion is simulated using the device-side access API from the DMA-mapping simulator.
Conceptually:
The test does not directly write to the CPU-visible RX buffer.
This preserves the distinction between:
and allows the lab to exercise the synchronization boundary explicitly.
10. Completion Ordering¶
Normal completion follows:
The state transition occurs before callback invocation.
Therefore the callback observes:
This models the callback as notification of a completed transaction rather than the operation that causes the transaction to become complete.
11. Synchronization for CPU Access¶
After a DMA_FROM_DEVICE transfer, the simulator requires an explicit CPU synchronization step before the CPU consumes the received data.
The lab performs:
This is performed before destroying the mapping.
The sequence is therefore:
The lab intentionally exposes this boundary because the DMA-mapping simulator keeps CPU-visible and device-visible memory views separate.
12. Safe Unmapping¶
The DMA client simulator checks whether the current transaction state may still access the mapping.
The protected states are:
These states reject unmapping.
After:
or:
the DMA transaction itself no longer owns the mapping.
The mapping may then be released.
After successful unmapping:
13. Synchronous Termination Model¶
The lab provides a simplified:
The supported outstanding states are:
Termination performs:
After the function returns successfully, the simulator guarantees:
The lab intentionally does not implement a separate asynchronous termination state.
A more detailed model would require something similar to:
That behavior is outside the scope of this one-shot client lab.
14. Test Case 1 — Normal Completion¶
The first test exercises the complete RX happy path:
Allocate RX Buffer
↓
Initialize Request
↓
DMA Map
↓
Prepare
↓
Submit
↓
Issue Pending
↓
ARMED
↓
Start Peripheral
↓
ACTIVE
↓
Device Write
↓
COMPLETED
↓
Callback
↓
Sync for CPU
↓
Unmap
↓
Verify Data
↓
Free Buffer
The test data is generated in a simulated peripheral buffer and transferred through the device-visible DMA mapping.
The CPU verifies the RX buffer only after the ownership synchronization step.
Expected result:
[CASE 1] Normal completion
DMA state : ARMED
Mapping state : true
Peripheral enabled: false
[CALLBACK] DMA completed: state=COMPLETED
[PASS] CASE 1
This case verifies both the normal DMA lifecycle and RX data visibility.
15. Test Case 2 — Abort While DMA Is Active¶
The second test deliberately terminates an active transaction.
Before the error is injected:
The simulated error path is:
Expected result:
The test verifies that the mapping is released only after the DMA transaction has reached a synchronized termination point.
16. Test Case 3 — Invalid Cleanup Ordering¶
The third test deliberately violates the DMA ownership rule.
It first reaches:
and then attempts:
The operation must fail because DMA may still access the mapping.
Expected output:
[TEST] Attempting unsafe unmap while DMA is active
[DMA] dma may still access the mapping: state=ACTIVE
[TEST] Unsafe unmap correctly rejected
The test then performs the correct recovery sequence:
Expected output:
This is the central safety test of the lab.
17. Cleanup Pattern¶
The test implementation uses reverse-order cleanup labels.
Conceptually:
Failure cleanup unwinds in reverse:
The key ordering is:
The buffer must never be freed while the DMA transaction may still reference its mapping.
18. Expected Test Summary¶
A successful run should finish with all three cases passing:
=== Day109 One-Shot DMA Client Lab ===
[CASE 1] Normal completion
...
[PASS] CASE 1
[CASE 2] Abort while DMA active
...
[PASS] CASE 2
[CASE 3] Invalid cleanup
...
[DMA] dma may still access the mapping: state=ACTIVE
[TEST] Unsafe unmap correctly rejected
[TEST] Safe unmap succeeded after termination
[PASS] CASE 3
What This Lab Demonstrates¶
The lab connects several previously independent concepts:
Kernel Memory
+
DMA Mapping
+
DMA Descriptor Lifecycle
+
DMA Completion
+
DMA Termination
+
Peripheral Coordination
The most important result is not the simulated data transfer itself.
The important result is the ownership model:
Buffer allocated
│
▼
Mapping exists
│
▼
DMA may access mapping
│
▼
Complete or terminate
│
▼
DMA no longer accesses mapping
│
▼
Synchronize ownership if required
│
▼
Unmap
│
▼
Free buffer
A correct DMA client must preserve this ordering on both normal and error paths.
Key Takeaways¶
dma_async_issue_pending()makes a transaction ready but does not necessarily mean the peripheral has started transferring.- Peripheral start and DMA transaction state must be coordinated explicitly.
- DMA completion and DMA mapping lifetime are separate.
- A completion callback should observe an already-completed DMA transaction.
- RX data ownership must be synchronized before CPU consumption when required by the mapping model.
dmaengine_terminate_sync()provides a safe teardown point for outstanding DMA work.- A DMA mapping must not be released while DMA may still access it.
- A backing buffer must not be freed until its DMA mapping has been safely released.
- Normal completion and error cleanup must obey the same ownership rules.
Next Step¶
The one-shot client model provides the foundation for continuous acquisition.
The next evolution is:
One-Shot Buffer
↓
Persistent Mapping
↓
Cyclic / Repeated DMA
↓
Period or Block Completion
↓
Kernel Buffering
↓
Userspace Consumer
This introduces a different lifetime model in which DMA completion may indicate that a block is ready rather than that the overall stream has ended.