Re: [Linaro-mm-sig] [PATCH 2/2] dma-buf: Document how exporters and importers agree on mapping lifetime
From: Christian König
Date: Mon Aug 31 2026 - 05:01:21 EST
Hi,
On 8/30/26 09:58, Leon Romanovsky wrote:
> On Thu, Aug 27, 2026 at 09:18:29PM +0200, Thomas Hellström wrote:
>> Hi,
>>
>> Some comments below:
>>
>> On Tue, 2026-08-25 at 09:28 +0300, Leon Romanovsky wrote:
>>> From: Leon Romanovsky <leonro@xxxxxxxxxx>
>>>
>>> Pinned, revoked and movable mappings are selected by which optional
>>> callbacks each side implements and by whether dma_buf_pin() succeeds,
>>> not by any flag or enum. Nothing in Documentation/ says so, and the
>>> rules are spread over the kdoc of dma_buf_ops.pin,
>>> dma_buf_attach_ops.invalidate_mappings and
>>> dma_buf_invalidate_mappings(),
>>> so a driver author has to know the symbol names before finding them.
>>>
>>> Name, per flow, the callbacks both sides have to implement to end up
>>> in
>>> it, describe dma_buf_pin() as the runtime negotiation, and record
>>> that
>>> the pin is what tells a revoke from a move.
>>>
>>> Signed-off-by: Leon Romanovsky <leonro@xxxxxxxxxx>
>>> ---
>>> Documentation/driver-api/dma-buf.rst | 6 +++
>>> drivers/dma-buf/dma-buf.c | 85
>>> +++++++++++++++++++++++++++++++++++-
>>> 2 files changed, 90 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/Documentation/driver-api/dma-buf.rst
>>> b/Documentation/driver-api/dma-buf.rst
>>> index 2f36c21d9948..39c201f38aa6 100644
>>> --- a/Documentation/driver-api/dma-buf.rst
>>> +++ b/Documentation/driver-api/dma-buf.rst
>>> @@ -113,6 +113,12 @@ Basic Operation and Device DMA Access
>>> .. kernel-doc:: drivers/dma-buf/dma-buf.c
>>> :doc: dma buf device access
>>>
>>> +Mapping Lifetime Negotiation
>>> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>>> +
>>> +.. kernel-doc:: drivers/dma-buf/dma-buf.c
>>> + :doc: mapping lifetime negotiation
>>> +
>>> CPU Access to DMA Buffer Objects
>>> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>>>
>>> diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c
>>> index d504c636dc29..30afec7365bc 100644
>>> --- a/drivers/dma-buf/dma-buf.c
>>> +++ b/drivers/dma-buf/dma-buf.c
>>> @@ -684,7 +684,90 @@ static struct file *dma_buf_getfile(size_t size,
>>> int flags)
>>> * reference acquired with dma_buf_get() by calling
>>> dma_buf_put().
>>> *
>>> * For the detailed semantics exporters are expected to implement
>>> see
>>> - * &dma_buf_ops.
>>> + * &dma_buf_ops. Whether the exporter may still move or destroy the
>>> backing
>>> + * storage after step 3 depends on what exporter and importer
>>> implement, see
>>> + * the mapping lifetime negotiation section below.
>>> + */
>>> +
>>> +/**
>>> + * DOC: mapping lifetime negotiation
>>> + *
>>> + * No flag or enum says whether the exporter may move or take away
>>> the backing
>>> + * storage while an importer holds a mapping. Each side implements a
>>> set of
>>> + * optional callbacks, and dma_buf_pin() settles the result at
>>> runtime. Three
>>> + * flows come out of it:
>>> + *
>>> + * - Pinned: the storage never moves and is never taken away.
>>> + * - Revoked: the storage never moves, but the exporter may take it
>>> away.
>>> + * - Movable: the exporter may relocate the storage at any time.
>>
>> Perhaps add "even temporarily to locations that are not available for
>> DMA".
>
> I don't know. "Not available for DMA" defeats the whole purpose of dma-buf,
> which is intended to expose DMA-capable memory to other peers. I imagine
> that "everything is optional dmabuf world" this is possible, but it
> looks to me like a partial version of revoked flow.
That is actually a very common use case.
For example what can happen is that the exporter moves a buffer to swap making it completely inaccessible to anybody.
As long as there is no mapping and the exporter can move the buffer back when a mapping is created that is something perfectly valid to do.
Regards,
Christian.
>
>>
>>> + *
>>> + * Every exporter implements &dma_buf_ops.map_dma_buf,
>>> + * &dma_buf_ops.unmap_dma_buf and &dma_buf_ops.release.
>>> dma_buf_export()
>>> + * rejects an exporter missing any of them.
>>> + *
>>> + * An importer reaches its flow like this:
>>> + *
>>> + * 1. Attach with dma_buf_dynamic_attach(). Leaving
>>> + * &dma_buf_attach_ops.invalidate_mappings NULL rules out
>>> everything but the
>>> + * pinned flow, because the importer can then never be told
>>> anything.
>>> + * 2. Call dma_buf_pin() under the reservation lock.
>>> + * 3. On failure run the movable flow, or give up.
>>> + * 4. On success the storage stays put. Whether the exporter may
>>> still take it
>>> + * away, which makes this the revoked flow instead of the pinned
>>> one, is the
>>> + * exporter's choice and is not reported back.
>>> + *
>>> + * dma_buf_attach() is the shorthand for an importer which only ever
>>> wants the
>>> + * pinned flow. It passes no &dma_buf_attach_ops, and DMA-buf then
>>> pins around
>>> + * every dma_buf_map_attachment() and waits for the
>>> DMA_RESV_USAGE_KERNEL
>>> + * fences on the importer's behalf. Peer to peer needs
>>> + * dma_buf_dynamic_attach(), because
>>> &dma_buf_attach_ops.allow_peer2peer lives
>>> + * in the attach ops.
>>> + *
>>> + * Pinned flow:
>>> + *
>>> + * - Exporter: implement &dma_buf_ops.pin and &dma_buf_ops.unpin to
>>> hold the
>>> + * storage still on request. An exporter whose storage never moves
>>> implements
>>> + * neither, and dma_buf_pin() then succeeds on its own. An
>>> exporter which
>>> + * refuses to be pinned implements &dma_buf_ops.pin and fails it.
>>> + * - Importer: nothing more. The mapping stays valid until it
>>> unmaps.
>>> + *
>>> + * Revoked flow:
>>> + *
>>> + * - Exporter: answer dma_buf_pin() as above. Call
>>> + * dma_buf_invalidate_mappings() when the storage goes away and
>>> fail
>>> + * &dma_buf_ops.map_dma_buf while it is gone. The two waits which
>>> complete a
>>> + * revocation are described in dma_buf_invalidate_mappings().
>>> + * - Importer: &dma_buf_attach_ops.invalidate_mappings has to unmap
>>> within
>>> + * bounded time and drop the pin.
>>> + *
>>> + * Movable flow:
>>> + *
>>> + * - Exporter: call dma_buf_invalidate_mappings() before each move,
>>> then wait
>>> + * for the &dma_buf.resv fences. &dma_buf_ops.pin and
>>> &dma_buf_ops.unpin play
>>> + * no part here.
>>> + * - Importer: hold no pin. &dma_buf_attach_ops.invalidate_mappings
>>> drops the
>>> + * cached mapping and has to lead to dma_buf_unmap_attachment()
>>> within
>>> + * bounded time.
>>
>> Hear I would want to see the exporter being allowed to force unmap the
>> dma mappings and reclaim thestorage when the fences mentioned above
>> have signaled, but the importer has not yet called
>> dma_buf_unmap_attachment(). That would allow importers to call
>> dma_buf_unmap_attachment() lazily, just before the next map_attachment,
>> which would allow simplifying importer implementations.
>
> How? It will move one piece of code as is to another place. In addition,
> both exporter and importer need to stop HW access to same region.
>
>> More of a related idea than something that needs fixing for this patch.
>>
>>> It need not stop the hardware, because access runs until the
>>> + * importer's &dma_buf.resv fences retire. Map again before the
>>> next DMA.
>>
>> A successful map will mean the exporter has placed the data in storage
>> compatible with what was agreed during attachment?
>
> Yes.
>
>>
>> Thanks,
>> Thomas
>>