DMA-buf lets an exporter pin, move or revoke the backing storage under an importer, and which of the three applies is decided by the optional callbacks each side implements and by whether dma_buf_pin() succeeds. Nothing in Documentation/ describes that, and the single reference to the mechanism still names move_notify(), removed in v7.1.
Thanks
Signed-off-by: Leon Romanovsky leonro@nvidia.com --- Leon Romanovsky (2): PCI/P2PDMA: Update DMABUF lifecycle docs after move_notify() rename dma-buf: Document how exporters and importers agree on mapping lifetime
Documentation/driver-api/dma-buf.rst | 6 +++ Documentation/driver-api/pci/p2pdma.rst | 6 ++- drivers/dma-buf/dma-buf.c | 85 ++++++++++++++++++++++++++++++++- 3 files changed, 94 insertions(+), 3 deletions(-) --- base-commit: 8049741ac93acd3a590dac070e12571fddf0e294 change-id: 20260820-document-dma-buf-3f8b41e32f57
Best regards, -- Leon Romanovsky leonro@nvidia.com
From: Leon Romanovsky leonro@nvidia.com
Commit 95308225e5ba ("dma-buf: Rename dma_buf_move_notify() to dma_buf_invalidate_mappings()") left the DMABUF section of the P2PDMA documentation pointing at move_notify(), a symbol that no longer exists. Readers grepping for it find nothing, and this is the only place in Documentation/ describing the revocation requirement.
Name the current function and record that importers which cannot unmap within bounded time have to be rejected at attach time, which is what makes the synchronous unmap on remove() achievable.
Fixes: 95308225e5ba ("dma-buf: Rename dma_buf_move_notify() to dma_buf_invalidate_mappings()") Signed-off-by: Leon Romanovsky leonro@nvidia.com --- Documentation/driver-api/pci/p2pdma.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/Documentation/driver-api/pci/p2pdma.rst b/Documentation/driver-api/pci/p2pdma.rst index d3f406cca694..63cff9e4d2c9 100644 --- a/Documentation/driver-api/pci/p2pdma.rst +++ b/Documentation/driver-api/pci/p2pdma.rst @@ -167,9 +167,11 @@ In this case the initiator and target pci_devices are known and the P2P subsyste is used to determine the mapping type. The phys_addr_t-based DMA API is used to establish the dma_addr_t.
-Lifecycle is controlled by DMABUF move_notify(). When the exporting driver wants +Lifecycle is controlled by DMABUF revocation. When the exporting driver wants to remove() it must deliver an invalidation shutdown to all DMABUF importing -drivers through move_notify() and synchronously DMA unmap all the MMIO. +drivers through dma_buf_invalidate_mappings() and synchronously DMA unmap all +the MMIO. Importers unable to complete that unmap within bounded time have to +be rejected when they attach, which dma_buf_attach_revocable() checks for.
No importing driver can continue to have a DMA map to the MMIO after the exporting driver has destroyed its p2p_provider.
On 8/25/26 08:28, Leon Romanovsky wrote:
From: Leon Romanovsky leonro@nvidia.com
Commit 95308225e5ba ("dma-buf: Rename dma_buf_move_notify() to dma_buf_invalidate_mappings()") left the DMABUF section of the P2PDMA documentation pointing at move_notify(), a symbol that no longer exists. Readers grepping for it find nothing, and this is the only place in Documentation/ describing the revocation requirement.
Name the current function and record that importers which cannot unmap within bounded time have to be rejected at attach time, which is what makes the synchronous unmap on remove() achievable.
Fixes: 95308225e5ba ("dma-buf: Rename dma_buf_move_notify() to dma_buf_invalidate_mappings()") Signed-off-by: Leon Romanovsky leonro@nvidia.com
Reviewed-by: Christian König christian.koenig@amd.com
Documentation/driver-api/pci/p2pdma.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/Documentation/driver-api/pci/p2pdma.rst b/Documentation/driver-api/pci/p2pdma.rst index d3f406cca694..63cff9e4d2c9 100644 --- a/Documentation/driver-api/pci/p2pdma.rst +++ b/Documentation/driver-api/pci/p2pdma.rst @@ -167,9 +167,11 @@ In this case the initiator and target pci_devices are known and the P2P subsyste is used to determine the mapping type. The phys_addr_t-based DMA API is used to establish the dma_addr_t. -Lifecycle is controlled by DMABUF move_notify(). When the exporting driver wants +Lifecycle is controlled by DMABUF revocation. When the exporting driver wants to remove() it must deliver an invalidation shutdown to all DMABUF importing -drivers through move_notify() and synchronously DMA unmap all the MMIO. +drivers through dma_buf_invalidate_mappings() and synchronously DMA unmap all +the MMIO. Importers unable to complete that unmap within bounded time have to +be rejected when they attach, which dma_buf_attach_revocable() checks for. No importing driver can continue to have a DMA map to the MMIO after the exporting driver has destroyed its p2p_provider.
On Tue, Aug 25, 2026 at 09:28:01AM +0300, Leon Romanovsky wrote:
From: Leon Romanovsky leonro@nvidia.com
Commit 95308225e5ba ("dma-buf: Rename dma_buf_move_notify() to dma_buf_invalidate_mappings()") left the DMABUF section of the P2PDMA documentation pointing at move_notify(), a symbol that no longer exists. Readers grepping for it find nothing, and this is the only place in Documentation/ describing the revocation requirement.
Name the current function and record that importers which cannot unmap within bounded time have to be rejected at attach time, which is what makes the synchronous unmap on remove() achievable.
Fixes: 95308225e5ba ("dma-buf: Rename dma_buf_move_notify() to dma_buf_invalidate_mappings()") Signed-off-by: Leon Romanovsky leonro@nvidia.com
Acked-by: Bjorn Helgaas bhelgaas@google.com
It looks like 2/2 might be updated, and it probably makes sense to merge them both together, but let me know if you want me to take this.
Documentation/driver-api/pci/p2pdma.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/Documentation/driver-api/pci/p2pdma.rst b/Documentation/driver-api/pci/p2pdma.rst index d3f406cca694..63cff9e4d2c9 100644 --- a/Documentation/driver-api/pci/p2pdma.rst +++ b/Documentation/driver-api/pci/p2pdma.rst @@ -167,9 +167,11 @@ In this case the initiator and target pci_devices are known and the P2P subsyste is used to determine the mapping type. The phys_addr_t-based DMA API is used to establish the dma_addr_t. -Lifecycle is controlled by DMABUF move_notify(). When the exporting driver wants +Lifecycle is controlled by DMABUF revocation. When the exporting driver wants to remove() it must deliver an invalidation shutdown to all DMABUF importing -drivers through move_notify() and synchronously DMA unmap all the MMIO. +drivers through dma_buf_invalidate_mappings() and synchronously DMA unmap all +the MMIO. Importers unable to complete that unmap within bounded time have to +be rejected when they attach, which dma_buf_attach_revocable() checks for. No importing driver can continue to have a DMA map to the MMIO after the exporting driver has destroyed its p2p_provider.
-- 2.55.0
On Thu, Sep 03, 2026 at 03:34:31PM -0500, Bjorn Helgaas wrote:
On Tue, Aug 25, 2026 at 09:28:01AM +0300, Leon Romanovsky wrote:
From: Leon Romanovsky leonro@nvidia.com
Commit 95308225e5ba ("dma-buf: Rename dma_buf_move_notify() to dma_buf_invalidate_mappings()") left the DMABUF section of the P2PDMA documentation pointing at move_notify(), a symbol that no longer exists. Readers grepping for it find nothing, and this is the only place in Documentation/ describing the revocation requirement.
Name the current function and record that importers which cannot unmap within bounded time have to be rejected at attach time, which is what makes the synchronous unmap on remove() achievable.
Fixes: 95308225e5ba ("dma-buf: Rename dma_buf_move_notify() to dma_buf_invalidate_mappings()") Signed-off-by: Leon Romanovsky leonro@nvidia.com
Acked-by: Bjorn Helgaas bhelgaas@google.com
It looks like 2/2 might be updated, and it probably makes sense to merge them both together, but let me know if you want me to take this.
This PCI patch is completely standalone, so I sent it separately: https://lore.kernel.org/all/20260830-doc-p2p-move-v1-1-61a388620588@nvidia.c...
Can you please take it from there?
Thanks
Documentation/driver-api/pci/p2pdma.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/Documentation/driver-api/pci/p2pdma.rst b/Documentation/driver-api/pci/p2pdma.rst index d3f406cca694..63cff9e4d2c9 100644 --- a/Documentation/driver-api/pci/p2pdma.rst +++ b/Documentation/driver-api/pci/p2pdma.rst @@ -167,9 +167,11 @@ In this case the initiator and target pci_devices are known and the P2P subsyste is used to determine the mapping type. The phys_addr_t-based DMA API is used to establish the dma_addr_t. -Lifecycle is controlled by DMABUF move_notify(). When the exporting driver wants +Lifecycle is controlled by DMABUF revocation. When the exporting driver wants to remove() it must deliver an invalidation shutdown to all DMABUF importing -drivers through move_notify() and synchronously DMA unmap all the MMIO. +drivers through dma_buf_invalidate_mappings() and synchronously DMA unmap all +the MMIO. Importers unable to complete that unmap within bounded time have to +be rejected when they attach, which dma_buf_attach_revocable() checks for. No importing driver can continue to have a DMA map to the MMIO after the exporting driver has destroyed its p2p_provider.
-- 2.55.0
From: Leon Romanovsky leonro@nvidia.com
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@nvidia.com --- 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. + * + * 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. It need not stop the hardware, because access runs until the + * importer's &dma_buf.resv fences retire. Map again before the next DMA. + * + * 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. + * + * 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. */
/**
On 8/25/26 08:28, Leon Romanovsky wrote:
From: Leon Romanovsky leonro@nvidia.com
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@nvidia.com
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.
- 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.
- An importer reaches its flow like this:
- 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".
- Call dma_buf_pin() under the reservation lock.
- On failure run the movable flow, or give up.
- 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.
- 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.
- 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.
- 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.
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.
/**
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 leonro@nvidia.com
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@nvidia.com
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:
- 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".
- Call dma_buf_pin() under the reservation lock.
- On failure run the movable flow, or give up.
- 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.
/**
On 8/26/26 14:43, Leon Romanovsky wrote:
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 leonro@nvidia.com
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@nvidia.com
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.
No, that used to work even before we had the invalidation callback.
What happens on a hot remove is that the exporter goes away but the driver stack keeps PCIe BARs or system memory resources allocated until all importers have destroyed their mappings.
See the invalidation callback is only optional, importers can implement it to speed thing up on teardown but they don't have to. A NULL invalidation callback is perfectly valid.
- 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:
- 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".
- Call dma_buf_pin() under the reservation lock.
- On failure run the movable flow, or give up.
- 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?
Well you don't. Exporters and importers can have a much wider variety of use cases.
When the importer doesn't give an invalidation callback the framework will call pin/unpin when the attachment is mapped/unmapped. That is just a service of the framework to make importers simpler.
But it is perfectly possible that an importer which implements the invalidation callback calls pin/unpin manually later on. This for example happens on display scanout when an invalidation would cause garbage on the screen when the buffer is moved.
But even after an importer called pin it is possible that the invalidation callback is called in case of a hot remove. Using our example of display scanout once more it is probably better to stop displaying anything then keeping the resources allocated until userspace realizes that the export is not there any more.
The revoke flow is then basically just a special case of hot remove. The only difference is that userspace invokes it instead of an user pulling a cable.
- 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".
No, exactly that is wrong.
See failure on attachment means that the two device can't talk with each other at all. But that is relatively rare, the dma_buf_attach_revocable() case is the only use case which comes to my mind.
The more common case is that while mapping the attachment we find that the buffer location is not accessible (any more) by the attachment because of hot plug or simply resource contention.
IIRC -EBUSY is even documented as perfectly valid reason to fail a mapping.
Regards, Christian.
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.
/**
On Wed, Aug 26, 2026 at 03:06:04PM +0200, Christian König wrote:
On 8/26/26 14:43, Leon Romanovsky wrote:
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 leonro@nvidia.com
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@nvidia.com
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.
No, that used to work even before we had the invalidation callback.
What happens on a hot remove is that the exporter goes away but the driver stack keeps PCIe BARs or system memory resources allocated until all importers have destroyed their mappings.
See the invalidation callback is only optional, importers can implement it to speed thing up on teardown but they don't have to. A NULL invalidation callback is perfectly valid.
- 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:
- 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".
- Call dma_buf_pin() under the reservation lock.
- On failure run the movable flow, or give up.
- 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?
Well you don't. Exporters and importers can have a much wider variety of use cases.
When the importer doesn't give an invalidation callback the framework will call pin/unpin when the attachment is mapped/unmapped. That is just a service of the framework to make importers simpler.
But it is perfectly possible that an importer which implements the invalidation callback calls pin/unpin manually later on. This for example happens on display scanout when an invalidation would cause garbage on the screen when the buffer is moved.
But even after an importer called pin it is possible that the invalidation callback is called in case of a hot remove. Using our example of display scanout once more it is probably better to stop displaying anything then keeping the resources allocated until userspace realizes that the export is not there any more.
The revoke flow is then basically just a special case of hot remove. The only difference is that userspace invokes it instead of an user pulling a cable.
OK, let me add some context on how this split came about and why I am trying to document it.
Several people approached me offline because they need to implement an importer that supports both revoke and movable flows. These require completely different implementations in the driver internals.
So, to answer the question, we need to document how these flows are identified and how they differ.
Naturally, they asked AI first, but all frontier LLMs gave them completely incorrect answers.
With that goal in mind, could you please help document the dma-buf lifetime model? "Everything is optional" sounds great, but is quite misleading.
Thanks
- 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".
No, exactly that is wrong.
See failure on attachment means that the two device can't talk with each other at all. But that is relatively rare, the dma_buf_attach_revocable() case is the only use case which comes to my mind.
The more common case is that while mapping the attachment we find that the buffer location is not accessible (any more) by the attachment because of hot plug or simply resource contention.
IIRC -EBUSY is even documented as perfectly valid reason to fail a mapping.
Regards, Christian.
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.
/**
On 8/26/26 16:02, Leon Romanovsky wrote: ...
- 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?
Well you don't. Exporters and importers can have a much wider variety of use cases.
When the importer doesn't give an invalidation callback the framework will call pin/unpin when the attachment is mapped/unmapped. That is just a service of the framework to make importers simpler.
But it is perfectly possible that an importer which implements the invalidation callback calls pin/unpin manually later on. This for example happens on display scanout when an invalidation would cause garbage on the screen when the buffer is moved.
But even after an importer called pin it is possible that the invalidation callback is called in case of a hot remove. Using our example of display scanout once more it is probably better to stop displaying anything then keeping the resources allocated until userspace realizes that the export is not there any more.
The revoke flow is then basically just a special case of hot remove. The only difference is that userspace invokes it instead of an user pulling a cable.
OK, let me add some context on how this split came about and why I am trying to document it.
Several people approached me offline because they need to implement an importer that supports both revoke and movable flows. These require completely different implementations in the driver internals.
Thanks, that at least helps me understand the goal.
So, to answer the question, we need to document how these flows are identified and how they differ.
Mhm, that's the point I don't think they differ that much.
Completely revoking is just a special case of moving the backing store to an inaccessible place and not making it accessible again on the next map call.
Naturally, they asked AI first, but all frontier LLMs gave them completely incorrect answers.
With that goal in mind, could you please help document the dma-buf lifetime model? "Everything is optional" sounds great, but is quite misleading.
Oh, good question I have no idea how to approach that.
Regards, Christian.
Thanks
On Mon, Aug 31, 2026 at 11:22:24AM +0200, Christian König wrote:
So, to answer the question, we need to document how these flows are identified and how they differ.
Mhm, that's the point I don't think they differ that much.
For the importer there are clearly different things that imply different HW capabilities:
pinned means the importer doesn't provide any way to stop DMA
revocable means the importer must be able to stop DMA and possibly suffer user visble errors in the process
movable means the importer must be able to pause DMA and never suffer errors while the memory location is moving.
The whole reason we have these levels is because they reflect very real levels of HW support on the importer side. I think it is worth documenting it at least for the sake of people implementing importer HW to understand the rules
Yes you can use the APIs more flexilby, but not beyound the HW enforced limits above.
Completely revoking is just a special case of moving the backing store to an inaccessible place and not making it accessible again on the next map call.
In a very general abstract sense maybe, but that model doesn't match how the importer HW implementations work.
Jason
Hi,
Some comments below:
On Tue, 2026-08-25 at 09:28 +0300, Leon Romanovsky wrote:
From: Leon Romanovsky leonro@nvidia.com
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@nvidia.com
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".
- 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:
- 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.
- Call dma_buf_pin() under the reservation lock.
- On failure run the movable flow, or give up.
- 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. 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?
Thanks, Thomas
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@nvidia.com
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@nvidia.com
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.
- 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:
- 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.
- Call dma_buf_pin() under the reservation lock.
- On failure run the movable flow, or give up.
- 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
On Sun, 2026-08-30 at 10:58 +0300, 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@nvidia.com
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@nvidia.com
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.
Not permanently revoked. In practice this would be, for example, a GPU- exported dma-buf which is only available for p2p access which gets evicted, or a system memory exported dma-buf that gets hit by a shrinker and moved to swap. The exporter calls invalidate_mappings() to notify importers that storage is going away. Importers need to call map_attachment() to bring it back.
- 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:
- 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.
- Call dma_buf_pin() under the reservation lock.
- On failure run the movable flow, or give up.
- 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.
A typical GPU example: A GPU importer receives invalidate_mappings() on an imported dma-buf. The dma-buf is still accessed by the GPU and thus has a couple of dma-fences attached. The importer can't immediately call dma_buf_unmap_attachment(). In fact, typically the easiest thing for the importer is to call dma_buf_unmap_attachment() on the next gpu command submission immediately followed by a dma_buf_map_attachment(), but it can't guarantee that would happen within bounded time.
A well behaved importer would therefore currently have to schedule an async worker or similar to wait for its dma-fences to signal and then grab the resv lock from worker context and call dma_buf_unmap_attachment().
A well-behaved exporter on the other hand, would have to wait for all dma-fences to signal, then wait for all unmap_attachment() calls before reclaiming.
TBH I'm not sure what the best solution is here, but the importer flow doesn't fit well in a typical gpu command submission model IMO.
Thanks, Thomas
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
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@nvidia.com
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@nvidia.com
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:
- 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.
- Call dma_buf_pin() under the reservation lock.
- On failure run the movable flow, or give up.
- 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
linaro-mm-sig@lists.linaro.org