It is expected that processes will pass dma-buf fd between drivers, and
only using the fd themselves for mmaping and synchronisation ioctls.
However, it may be convenient for an application to read/write into the
dma-buf, for instance a test case to check the internal
dma_buf->ops->kmap interface. There may also be a small advantage to
avoiding the mmap() for very simple/small operations.

Note in particular, synchronisation with the device is left to the
caller with an explicit DMA_BUF_IOCTL_SYNC, rather than done implicitly
inside the read/write, so that the user can avoid synchronisation if
they so choose.

"It is easier to add synchronisation later, than it is to take it away."

v2: Lots of little fixes, plus a real llseek() implements so that the
first basic little test cases work!

Testcase: igt/prime_rw
Signed-off-by: Chris Wilson <ch...@chris-wilson.co.uk>
Cc: Laura Abbott <labb...@redhat.com>
Cc: Sumit Semwal <sumit.sem...@linaro.org>
Cc: Daniel Vetter <daniel.vet...@ffwll.ch>
Cc: Sean Paul <seanp...@chromium.org>
Cc: Janusz Krzysztofik <janusz.krzyszto...@linux.intel.com>
Tested-by: Laura Abbott <labb...@redhat.com>
---
Dusting this off again as it would be nice as a user to operate on dmabuf
agnostic to the underyling driver. We have mmap, so naturally we would
like to have read/write as well!
---
 drivers/dma-buf/dma-buf.c | 108 ++++++++++++++++++++++++++++++++------
 1 file changed, 93 insertions(+), 15 deletions(-)

diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c
index 433d91d710e4..a19fc881a99c 100644
--- a/drivers/dma-buf/dma-buf.c
+++ b/drivers/dma-buf/dma-buf.c
@@ -135,28 +135,104 @@ static int dma_buf_mmap_internal(struct file *file, 
struct vm_area_struct *vma)
 
 static loff_t dma_buf_llseek(struct file *file, loff_t offset, int whence)
 {
-       struct dma_buf *dmabuf;
-       loff_t base;
+       struct dma_buf *dmabuf = file->private_data;
 
        if (!is_dma_buf_file(file))
                return -EBADF;
 
-       dmabuf = file->private_data;
+       return fixed_size_llseek(file, offset, whence, dmabuf->size);
+}
 
-       /* only support discovering the end of the buffer,
-          but also allow SEEK_SET to maintain the idiomatic
-          SEEK_END(0), SEEK_CUR(0) pattern */
-       if (whence == SEEK_END)
-               base = dmabuf->size;
-       else if (whence == SEEK_SET)
-               base = 0;
-       else
-               return -EINVAL;
+static ssize_t dma_buf_read(struct file *file,
+                           char __user *ubuf, size_t remain,
+                           loff_t *offset)
+{
+       struct dma_buf *dmabuf = file->private_data;
+       unsigned long idx;
+       unsigned int start;
+       size_t total;
 
-       if (offset != 0)
-               return -EINVAL;
+       if (!is_dma_buf_file(file))
+               return -EBADF;
+
+       total = 0;
+       idx = *offset >> PAGE_SHIFT;
+       start = offset_in_page(*offset);
+       while (remain) {
+               unsigned int len = min_t(size_t, remain, PAGE_SIZE - start);
+               unsigned int copied;
+               void *vaddr;
+
+               if (*offset >= dmabuf->size)
+                       return total;
+
+               vaddr = dma_buf_kmap(dmabuf, idx);
+               if (!vaddr)
+                       return total ?: -EIO;
+
+               copied = copy_to_user(ubuf, vaddr + start, len);
+               dma_buf_kunmap(dmabuf, idx, vaddr);
+
+               total += copied ?: len;
+               if (copied) {
+                       *offset += copied;
+                       return total ?: -EFAULT;
+               }
+
+               remain -= len;
+               *offset += len;
+               ubuf += len;
+               start = 0;
+               idx++;
+       }
+
+       return total;
+}
+
+static ssize_t dma_buf_write(struct file *file,
+                            const char __user *ubuf, size_t remain,
+                            loff_t *offset)
+{
+       struct dma_buf *dmabuf = file->private_data;
+       unsigned long idx;
+       unsigned int start;
+       size_t total;
+
+       if (!is_dma_buf_file(file))
+               return -EBADF;
+
+       total = 0;
+       idx = *offset >> PAGE_SHIFT;
+       start = offset_in_page(*offset);
+       while (remain) {
+               unsigned int len = min_t(size_t, remain, PAGE_SIZE - start);
+               unsigned int copied;
+               void *vaddr;
+
+               if (*offset >= dmabuf->size)
+                       return total;
+
+               vaddr = dma_buf_kmap(dmabuf, idx);
+               if (!vaddr)
+                       return total ?: -EIO;
+
+               copied = copy_from_user(vaddr + start, ubuf, len);
+               dma_buf_kunmap(dmabuf, idx, vaddr);
+
+               total += copied ?: len;
+               if (copied) {
+                       *offset += copied;
+                       return total ?: -EFAULT;
+               }
+
+               remain -= len;
+               *offset += len;
+               ubuf += len;
+               start = 0;
+               idx++;
+       }
 
-       return base + offset;
+       return total;
 }
 
 /**
@@ -413,6 +489,8 @@ static const struct file_operations dma_buf_fops = {
        .release        = dma_buf_release,
        .mmap           = dma_buf_mmap_internal,
        .llseek         = dma_buf_llseek,
+       .read           = dma_buf_read,
+       .write          = dma_buf_write,
        .poll           = dma_buf_poll,
        .unlocked_ioctl = dma_buf_ioctl,
 #ifdef CONFIG_COMPAT
-- 
2.23.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

Reply via email to