On Wed, Aug 26, 2026 at 01:41:51PM +0200, Christian König wrote: > On 8/25/26 08:28, 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. > > That's not quite correct. The backing store can still disappear if the > exporter is physically hot removed. > > In that case the exporter will still try to invalidate the mapping even if it > is pinned. A NULL invalidate_mapping callback is of course still never called > and so still optional.
But what does this mean for an importer that has no idea the exporter no longer exists? Will it crash? I think the revoke flow actually fixes this hot-remove case. > > > + * - Revoked: the storage never moves, but the exporter may take it away. > > + * - Movable: the exporter may relocate the storage at any time. > > + * > > > + * 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. > > I think that this is superfluous, the check in dma_buf_export() should make > sure that all exporters follow that. Sure, will change. > > > + * > > + * 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. > > I would rather write "Leaving out the optional > dma_buf_attach_ops.invalidate_mappings callback pins the buffer while the > attachment is present". > > > + * 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. > > That sentence sounds not really readable. I will try to rephrase in next version. > > > + * > > + * 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. > > That's also superfluous. Will change. > > > + * > > + * 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. It need not stop the hardware, because access runs > > until the > > + * importer's &dma_buf.resv fences retire. Map again before the next DMA. > > That is also not really correct. Those flows are not separated like this. How will you split them? > > > + * > > + * The pin tells a revoke from a move. > > + * &dma_buf_attach_ops.invalidate_mappings carries no reason, and both > > flows > > + * ask for the same unmap. An importer holding a pin can only be seeing a > > + * revoke, because the exporter promised not to move. An importer without > > a pin > > + * treats every call as a move and maps again. > > + * > > + * A revoke need not be forever. An exporter revoking around a temporary > > loss > > + * of access takes mappings again afterwards. Giving up for good is the > > + * importer's own choice, so an exporter must not wait for one to come > > back. > > + * > > > + * &dma_buf_ops.attach is the only place where an exporter can turn an > > importer > > + * away. An exporter which revokes rejects the importers for which > > + * dma_buf_attach_revocable() returns false. An exporter of memory without > > + * struct page rejects the importers which left > > + * &dma_buf_attach_ops.allow_peer2peer clear. > > That is also not correct. A mapping can be rejected later on as well. In API level yes, but I'm not sure that failure in map_dma_buf is equal logically to failure in attach. So, I won't call it "rejected later". > > Regards, > Christian. > > > + * > > + * Userspace sees none of this. The two drivers negotiate the flow between > > + * themselves, and the DMA-buf file descriptor shows no trace of the > > result. > > */ > > > > /** > > >
