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 <jack@xxxxxxx> Cc: Mauro Carvalho Chehab <mchehab@xxxxxxxxxx> Cc: linux-media@xxxxxxxxxxxxxxx Cc: <stable@xxxxxxxxxxxxxxx> Signed-off-by: Dan Williams <dan.j.williams@xxxxxxxxx> --- 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;