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 <[email protected]> >>> >>> 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 <[email protected]> >>> --- >>> 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 >>
