V4L2 memory registrations are incompatible with filesystem-dax that
needs the ability to revoke dma access to a mapping at will, or
otherwise allow the kernel to wait for completion of DMA. The
filesystem-dax implementation breaks the traditional solution of
truncate of active file backed mappings since there is no page-cache
page we can orphan to sustain ongoing DMA.

If v4l2 wants to support long lived DMA mappings it needs to arrange to
hold a file lease or use some other mechanism so that the kernel can
coordinate revoking DMA access when the filesystem needs to truncate
mappings.

Reported-by: Jan Kara <[email protected]>
Cc: Mauro Carvalho Chehab <[email protected]>
Cc: [email protected]
Cc: <[email protected]>
Signed-off-by: Dan Williams <[email protected]>
---
 drivers/media/v4l2-core/videobuf-dma-sg.c |   39 ++++++++++++++++++++++++++++-
 1 file changed, 37 insertions(+), 2 deletions(-)

diff --git a/drivers/media/v4l2-core/videobuf-dma-sg.c 
b/drivers/media/v4l2-core/videobuf-dma-sg.c
index 0b5c43f7e020..37a4ae61b2c0 100644
--- a/drivers/media/v4l2-core/videobuf-dma-sg.c
+++ b/drivers/media/v4l2-core/videobuf-dma-sg.c
@@ -155,8 +155,9 @@ static int videobuf_dma_init_user_locked(struct 
videobuf_dmabuf *dma,
                        int direction, unsigned long data, unsigned long size)
 {
        unsigned long first, last;
-       int err, rw = 0;
+       int err, rw = 0, i, nr_pages;
        unsigned int flags = FOLL_FORCE;
+       struct vm_area_struct **vmas = NULL;
 
        dma->direction = direction;
        switch (dma->direction) {
@@ -179,6 +180,16 @@ static int videobuf_dma_init_user_locked(struct 
videobuf_dmabuf *dma,
        if (NULL == dma->pages)
                return -ENOMEM;
 
+       if (IS_ENABLED(CONFIG_FS_DAX)) {
+               vmas = kmalloc(dma->nr_pages * sizeof(struct vm_area_struct *),
+                               GFP_KERNEL);
+               if (NULL == vmas) {
+                       kfree(dma->pages);
+                       dma->pages = NULL;
+                       return -ENOMEM;
+               }
+       }
+
        if (rw == READ)
                flags |= FOLL_WRITE;
 
@@ -186,7 +197,31 @@ static int videobuf_dma_init_user_locked(struct 
videobuf_dmabuf *dma,
                data, size, dma->nr_pages);
 
        err = get_user_pages(data & PAGE_MASK, dma->nr_pages,
-                            flags, dma->pages, NULL);
+                            flags, dma->pages, vmas);
+       nr_pages = err;
+
+       for (i = 0; vmas && i < nr_pages; i++) {
+               struct vm_area_struct *vma = vmas[i];
+               struct inode *inode;
+
+               if (!vma_is_dax(vma))
+                       continue;
+
+               /* device-dax is safe for long-lived v4l2 mappings... */
+               inode = file_inode(vma->vm_file);
+               if (inode->i_mode == S_IFCHR)
+                       continue;
+
+               /* ...filesystem-dax is not. */
+               err = -EOPNOTSUPP;
+               break;
+
+               /*
+                * FIXME: add a 'with lease' mechanism for v4l2 to
+                * obtain time bounded access to filesytem-dax mappings
+                */
+       }
+       kfree(vmas);
 
        if (err != dma->nr_pages) {
                dma->nr_pages = (err >= 0) ? err : 0;

Reply via email to