Scatter-Gather DMA: Keep Mapped Segment Counts Separate from Original Entries
Keep the number of original scatterlist entries separate from the number of DMA segments returned by mapping. Program hardware from the mapped representation, but pass the original entry count to the corresponding unmap and scatterlist synchronization calls. Reusing one variable for both counts hides a portability bug when the mapping layer merges entries.
Applicability: this article uses the Linux v6.12 streaming DMA API and a driver-owned scatterlist. The synthetic merge example describes an allowed mapping result, not a prediction for a particular IOMMU, architecture, or device. Hardware descriptor formats, segment limits, and submission ordering remain device-specific.

1. Identify the two representations
The original scatterlist describes the CPU-side memory pieces passed to dma_map_sg(). The mapped representation describes addresses and lengths usable by the selected DMA device. The v6.12 DMA mapping guide explains that mapping can merge entries and return fewer DMA segments. Zero indicates mapping failure.
Name the counts original_nents and mapped_nents. Save the mapping device, direction, list pointer, and mapping state in the same request object. A completion callback should not reconstruct them from a hardware descriptor count or from whichever global device pointer happens to be current.
The v6.12 scatterlist header also distinguishes nents and orig_nents in struct sg_table. When using a higher-level helper, inspect its precise ownership and count contract rather than assuming it has identical return semantics to raw dma_map_sg(). This article’s procedure keeps the raw API boundary explicit.
2. Walk a synthetic merged request
Suppose an original list contains four entries of 1024 bytes each. A hypothetical successful mapper returns two DMA segments, each 2048 bytes. The request still represents 4096 payload bytes. Its input count is four; its device-facing count is two.
Construct two hardware descriptors using sg_dma_address() and sg_dma_len() from the mapped traversal. Do not construct four descriptors from original lengths paired with mapped addresses. That mixture can produce duplicate, missing, or invalid device ranges even though the total application buffer looks correct.
After the device has stopped accessing the request, unmap with the original count of four. Passing two because two descriptors completed confuses hardware progress with the mapping API’s cleanup argument. A driver tested only where mapping returns the input count can conceal this mistake indefinitely.
3. Make count and length assertions independent
At map completion, record original count, mapped count, total requested bytes, and total mapped bytes. Require a positive mapped count before descriptor construction. For this ordinary list mapping, check that the mapped lengths cover the intended request without changing its byte total.
Next validate the device’s descriptor capacity and per-segment limits against the mapped result. Fewer segments do not automatically imply acceptable lengths: merging can produce a larger segment. The mapping setup and device constraints must agree. If the returned representation cannot be submitted, unwind the successful mapping before retiring the request.
Use a scatterlist iterator rather than pointer arithmetic that assumes every entry is stored in one flat array. Keep the descriptor count separate again if the device-specific layer subdivides a mapped segment. One mapped segment is not necessarily one application record or one hardware descriptor.
4. Preserve the mapping lifetime
The generic DMA API reference warns that mapping can alter scatterlist information and that an already mapped list must not simply be mapped again. Treat mapping as a state transition with one owner, not a harmless address-query function.
A useful request lifecycle is prepared, mapped, submitted, quiesced, and unmapped. An error before mapping succeeds has no successful mapping to release. An error after mapping but before submission still needs cleanup. A timeout after submission must first establish that the device can no longer access the memory.
Retain the original list and backing storage until their relevant lifetimes end. A timeout notification to the caller is not permission to free pages while a DMA engine may still use them. Prevent completion and cancellation from both unmapping the same request by assigning exactly one terminal cleanup owner.
If the mapping is deliberately retained while the CPU accesses data between transfers, use the direction-appropriate synchronization contract. The scatterlist synchronization calls use the original count, as documented in the mapping guide. This is an API ownership requirement, not an invitation to insert architecture-specific cache operations into a portable driver.
5. Test the count mismatch without relying on chance
Build a synthetic descriptor-construction test that supplies a four-entry input description and a two-segment mapped description. Assert two device-facing iterations, the expected total bytes, and an eventual unmap request carrying four. Keep synthetic addresses clearly labeled; never submit fabricated DMA addresses to hardware.
Also exercise a mapping that returns the original count, a zero return, and a successful mapping rejected by descriptor-capacity validation. These cases catch code that tests only a negative return or leaks a mapping when descriptor preparation fails.
For lifecycle testing, script completion before cancellation, cancellation before completion, and a late notification after the request has already been retired. The test should detect double cleanup and reuse of a retired generation. A generation check supplements valid object lifetime; it cannot make a freed pointer safe to dereference.
6. Qualify the actual platform separately
On a recoverable development target, capture count pairs across representative buffer layouts and the deployed IOMMU configuration. Do not claim that fragmentation forces a particular merge pattern. Record whatever the mapping layer actually returns and ensure the driver accepts all legal cases within the device’s constraints.
Retain request identity, mapping direction, count pair, descriptor count, and cleanup reason in bounded diagnostics. Avoid logging sensitive payload data or treating raw addresses as an application interface. Compare error paths with the normal completion path so resource leaks are visible before long-duration testing.
No Linux DMA mapping or hardware transfer was executed for this article. Its deliverable is the two-count request model, synthetic merged-segment fixture, and lifetime assertions. Acceptance requires correct mapped traversal and original-count cleanup on the selected kernel and device, including failure and cancellation paths.