From 255364f7b8a0fee3fb642b3e1521e943dd67bfb3 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Thu, 20 Apr 2017 19:05:14 +0100 Subject: staging: comedi: support vm_access_process for mmap'd buffer If a process that has mmap'd a COMEDI buffer is being run under a debugger such as GDB, the buffer contents are inaccessible from the debugger. Support the `access()` VM operation to allow the buffer contents to be accessed by another process. Signed-off-by: Ian Abbott Signed-off-by: Greg Kroah-Hartman --- drivers/staging/comedi/comedi_buf.c | 24 ++++++++++++++++++++++++ drivers/staging/comedi/comedi_fops.c | 15 +++++++++++++++ drivers/staging/comedi/comedi_internal.h | 2 ++ 3 files changed, 41 insertions(+) diff --git a/drivers/staging/comedi/comedi_buf.c b/drivers/staging/comedi/comedi_buf.c index 1e1df89b5018..8e9b30b26810 100644 --- a/drivers/staging/comedi/comedi_buf.c +++ b/drivers/staging/comedi/comedi_buf.c @@ -161,6 +161,30 @@ int comedi_buf_map_put(struct comedi_buf_map *bm) return 1; } +/* helper for "access" vm operation */ +int comedi_buf_map_access(struct comedi_buf_map *bm, unsigned long offset, + void *buf, int len, int write) +{ + unsigned int pgoff = offset & ~PAGE_MASK; + unsigned long pg = offset >> PAGE_SHIFT; + int done = 0; + + while (done < len && pg < bm->n_pages) { + int l = min_t(int, len - done, PAGE_SIZE - pgoff); + void *b = bm->page_list[pg].virt_addr + pgoff; + + if (write) + memcpy(b, buf, l); + else + memcpy(buf, b, l); + buf += l; + done += l; + pg++; + pgoff = 0; + } + return done; +} + /* returns s->async->buf_map and increments its kref refcount */ struct comedi_buf_map * comedi_buf_map_from_subdev_get(struct comedi_subdevice *s) diff --git a/drivers/staging/comedi/comedi_fops.c b/drivers/staging/comedi/comedi_fops.c index 92d864fc08ac..f191c2a75732 100644 --- a/drivers/staging/comedi/comedi_fops.c +++ b/drivers/staging/comedi/comedi_fops.c @@ -2165,9 +2165,24 @@ static void comedi_vm_close(struct vm_area_struct *area) comedi_buf_map_put(bm); } +static int comedi_vm_access(struct vm_area_struct *vma, unsigned long addr, + void *buf, int len, int write) +{ + struct comedi_buf_map *bm = vma->vm_private_data; + unsigned long offset = + addr - vma->vm_start + (vma->vm_pgoff << PAGE_SHIFT); + + if (len < 0) + return -EINVAL; + if (len > vma->vm_end - addr) + len = vma->vm_end - addr; + return comedi_buf_map_access(bm, offset, buf, len, write); +} + static const struct vm_operations_struct comedi_vm_ops = { .open = comedi_vm_open, .close = comedi_vm_close, + .access = comedi_vm_access, }; static int comedi_mmap(struct file *file, struct vm_area_struct *vma) diff --git a/drivers/staging/comedi/comedi_internal.h b/drivers/staging/comedi/comedi_internal.h index 534415e331b6..6246f4a78ca6 100644 --- a/drivers/staging/comedi/comedi_internal.h +++ b/drivers/staging/comedi/comedi_internal.h @@ -29,6 +29,8 @@ void comedi_buf_reset(struct comedi_subdevice *s); bool comedi_buf_is_mmapped(struct comedi_subdevice *s); void comedi_buf_map_get(struct comedi_buf_map *bm); int comedi_buf_map_put(struct comedi_buf_map *bm); +int comedi_buf_map_access(struct comedi_buf_map *bm, unsigned long offset, + void *buf, int len, int write); struct comedi_buf_map *comedi_buf_map_from_subdev_get( struct comedi_subdevice *s); unsigned int comedi_buf_write_n_available(struct comedi_subdevice *s); -- cgit