From: Fred Griffoul <[email protected]>

dma_buf_invalidate_mappings() tells every importer that the whole
buffer changed. An exporter that changes one part of its memory cannot
say which bytes changed, so importers throw away mappings that are
still valid.

Add an exporter helper that invalidates a byte range, and an importer
callback that receives it. The callback means that the address, the
attributes or the backing of the range changed. If part of the range is
no longer backed, get_phys() returns -ENOENT for it. Importers must stop
using their old answer before the callback returns. Importers that do
not implement the callback still receive a whole-buffer invalidation.

Signed-off-by: Fred Griffoul <[email protected]>
---
 drivers/dma-buf/dma-buf.c | 30 ++++++++++++++++++++++++++++++
 include/linux/dma-buf.h   | 16 ++++++++++++++++
 2 files changed, 46 insertions(+)

diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c
index 66b85d53ed22..e7010163eb2f 100644
--- a/drivers/dma-buf/dma-buf.c
+++ b/drivers/dma-buf/dma-buf.c
@@ -1389,6 +1389,36 @@ void dma_buf_invalidate_mappings(struct dma_buf *dmabuf)
 }
 EXPORT_SYMBOL_NS_GPL(dma_buf_invalidate_mappings, "DMA_BUF");
 
+/**
+ * dma_buf_invalidate_mappings_range - notify attachments that a range changed
+ * @dmabuf: buffer whose layout changed
+ * @offset: first changed byte
+ * @length: number of changed bytes
+ *
+ * Importers with a ranged callback stop using their old mappings of the range
+ * before returning. Other importers receive the existing whole-buffer
+ * callback, which is correct but coarser. The reservation lock must be held.
+ */
+void dma_buf_invalidate_mappings_range(struct dma_buf *dmabuf,
+                                      unsigned long offset,
+                                      unsigned long length)
+{
+       struct dma_buf_attachment *attach;
+
+       dma_resv_assert_held(dmabuf->resv);
+       list_for_each_entry(attach, &dmabuf->attachments, node) {
+               const struct dma_buf_attach_ops *ops = attach->importer_ops;
+
+               if (!ops)
+                       continue;
+               if (ops->invalidate_mappings_range)
+                       ops->invalidate_mappings_range(attach, offset, length);
+               else if (ops->invalidate_mappings)
+                       ops->invalidate_mappings(attach);
+       }
+}
+EXPORT_SYMBOL_NS_GPL(dma_buf_invalidate_mappings_range, "DMA_BUF");
+
 /**
  * dma_buf_get_phys - describe the run that starts at an offset
  * @attach: attachment to query
diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h
index b223962e20c2..55c3fe60a0ba 100644
--- a/include/linux/dma-buf.h
+++ b/include/linux/dma-buf.h
@@ -485,6 +485,19 @@ struct dma_buf_attach_ops {
         * required behavior.
         */
        void (*invalidate_mappings)(struct dma_buf_attachment *attach);
+
+       /**
+        * @invalidate_mappings_range: [optional] a byte range changed
+        *
+        * The exporter changed the address, attributes or backing of
+        * [@offset, @offset + @length). The importer must stop using its old
+        * answer for that range before returning.
+        * Importers without this callback receive @invalidate_mappings for
+        * the whole buffer instead.
+        */
+       void (*invalidate_mappings_range)(struct dma_buf_attachment *attach,
+                                         unsigned long offset,
+                                         unsigned long length);
 };
 
 /**
@@ -600,6 +613,9 @@ struct sg_table *dma_buf_map_attachment(struct 
dma_buf_attachment *,
 void dma_buf_unmap_attachment(struct dma_buf_attachment *, struct sg_table *,
                                enum dma_data_direction);
 void dma_buf_invalidate_mappings(struct dma_buf *dma_buf);
+void dma_buf_invalidate_mappings_range(struct dma_buf *dma_buf,
+                                      unsigned long offset,
+                                      unsigned long length);
 bool dma_buf_attach_revocable(struct dma_buf_attachment *attach);
 /* bits 0-7: memory type (a value, not flags) */
 #define DMA_BUF_PHYS_ATTR_TYPE_MASK    GENMASK(7, 0)
-- 
2.47.3


Reply via email to