From: Leon Romanovsky leonro@nvidia.com
Nothing in DMA-buf declares whether a mapping may be moved or taken away. Nearly every callback on both sides is optional, so the answer follows from which ones are implemented and from whether dma_buf_pin() succeeds. Documentation/ says none of this, and the rules sit in the kdoc of dma_buf_ops.pin, dma_buf_attach_ops.invalidate_mappings and dma_buf_invalidate_mappings(), which a driver author has to know by name before finding them.
Describe the three flows an importer has to handle, the callbacks each one asks of both sides, and dma_buf_pin() as the runtime negotiation.
Signed-off-by: Leon Romanovsky leonro@nvidia.com --- Let's try this variant from importer POV.
Thanks
Changes in v2: - Split pci p2p documentation patch to separate series - Improved dma-buf documentation patch. - Link to v1: https://patch.msgid.link/20260825-document-dma-buf-v1-0-5ecfb3e1371c@nvidia.... --- Documentation/driver-api/dma-buf.rst | 6 ++++ drivers/dma-buf/dma-buf.c | 69 +++++++++++++++++++++++++++++++++++- 2 files changed, 74 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..aa47142cd9ed 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -684,7 +684,74 @@ 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 take away the backing + * storage after step 3 depends on what both sides implement. See the mapping + * lifetime negotiation section below. + */ + +/** + * DOC: mapping lifetime negotiation + * + * Nearly everything in DMA-buf is optional. No flag or enum says whether the + * exporter may move or take away the backing storage while an importer holds + * a mapping. Each side simply implements the callbacks it can offer, and + * dma_buf_pin() settles the result at runtime. + * + * Out of that optionality the importer sees three main flows. They are named + * from the importer's point of view, because each one demands a different + * capability of its hardware: + * + * - Pinned: the memory is never taken away. The importer offers no way + * to stop DMA. + * - Revoked: the storage never moves, but the exporter may take it away. The + * importer must be able to stop DMA, and may hit user visible errors while + * doing so. + * - Movable: the exporter may relocate the storage at any time. The importer + * must be able to pause DMA, and must raise no error while the storage is + * moving. + * + * An importer reaches its flow like this: + * + * 1. Attach with dma_buf_dynamic_attach(). Leaving out the optional + * &dma_buf_attach_ops.invalidate_mappings callback pins the buffer for as + * long as the attachment exists. + * 2. Call dma_buf_pin() under the reservation lock. + * 3. On failure, run the movable flow or give up. + * 4. On success, the flow is the revoked one if the optional + * &dma_buf_attach_ops.invalidate_mappings is implemented, and the pinned + * one if it is not. + * + * 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 the importer 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. The importer maps again before the + * next DMA. + * + * &dma_buf_ops.attach is the best place for an exporter to turn an importer + * away, because the importer can still fall back to another flow and attach + * again. */
/**
--- base-commit: 63367df6e7255067ad6a83abe0d2799dfa491876 change-id: 20260820-document-dma-buf-3f8b41e32f57
Best regards, -- Leon Romanovsky leonro@nvidia.com