]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
comedi: access buffer data page-by-page
authorIan Abbott <abbotti@mev.co.uk>
Tue, 15 Apr 2025 11:35:57 +0000 (12:35 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 25 Apr 2025 13:53:18 +0000 (15:53 +0200)
The aim is to get rid of the `prealloc_buf` member of `struct
comedi_async` and access the buffer contents on a page-by-page basis
using the addresses in the `virt_addr` member of `struct
comedi_buf_page`.  This will allow us to eliminate a `vmap()` that maps
the whole buffer.

Since the buffer pages have non-consecutive `virt_addr` addresses in
virtual memory (except for drivers using DMA), change the loops that
access buffer data to access it page-by-page.

Signed-off-by: Ian Abbott <abbotti@mev.co.uk>
Link: https://lore.kernel.org/r/20250415114008.5977-3-abbotti@mev.co.uk
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/comedi/comedi_buf.c
drivers/comedi/comedi_fops.c

index 393966c097405d16717d6712bd5673699f08407e..0d956dd40a2b36a55cdf639d5263addf5ab16bf1 100644 (file)
@@ -365,6 +365,7 @@ static unsigned int comedi_buf_munge(struct comedi_subdevice *s,
                                     unsigned int num_bytes)
 {
        struct comedi_async *async = s->async;
+       struct comedi_buf_page *buf_page_list = async->buf_map->page_list;
        unsigned int count = 0;
        const unsigned int num_sample_bytes = comedi_bytes_per_sample(s);
 
@@ -376,15 +377,16 @@ static unsigned int comedi_buf_munge(struct comedi_subdevice *s,
        /* don't munge partial samples */
        num_bytes -= num_bytes % num_sample_bytes;
        while (count < num_bytes) {
-               int block_size = num_bytes - count;
-               unsigned int buf_end;
-
-               buf_end = async->prealloc_bufsz - async->munge_ptr;
-               if (block_size > buf_end)
-                       block_size = buf_end;
+               /*
+                * Do not munge beyond page boundary.
+                * Note: prealloc_bufsz is a multiple of PAGE_SIZE.
+                */
+               unsigned int page = async->munge_ptr >> PAGE_SHIFT;
+               unsigned int offset = offset_in_page(async->munge_ptr);
+               unsigned int block_size =
+                            min(num_bytes - count, PAGE_SIZE - offset);
 
-               s->munge(s->device, s,
-                        async->prealloc_buf + async->munge_ptr,
+               s->munge(s->device, s, buf_page_list[page].virt_addr + offset,
                         block_size, async->munge_chan);
 
                /*
@@ -397,7 +399,8 @@ static unsigned int comedi_buf_munge(struct comedi_subdevice *s,
                async->munge_chan %= async->cmd.chanlist_len;
                async->munge_count += block_size;
                async->munge_ptr += block_size;
-               async->munge_ptr %= async->prealloc_bufsz;
+               if (async->munge_ptr == async->prealloc_bufsz)
+                       async->munge_ptr = 0;
                count += block_size;
        }
 
@@ -558,46 +561,52 @@ static void comedi_buf_memcpy_to(struct comedi_subdevice *s,
                                 const void *data, unsigned int num_bytes)
 {
        struct comedi_async *async = s->async;
+       struct comedi_buf_page *buf_page_list = async->buf_map->page_list;
        unsigned int write_ptr = async->buf_write_ptr;
 
        while (num_bytes) {
-               unsigned int block_size;
-
-               if (write_ptr + num_bytes > async->prealloc_bufsz)
-                       block_size = async->prealloc_bufsz - write_ptr;
-               else
-                       block_size = num_bytes;
+               /*
+                * Do not copy beyond page boundary.
+                * Note: prealloc_bufsz is a multiple of PAGE_SIZE.
+                */
+               unsigned int page = write_ptr >> PAGE_SHIFT;
+               unsigned int offset = offset_in_page(write_ptr);
+               unsigned int block_size = min(num_bytes, PAGE_SIZE - offset);
 
-               memcpy(async->prealloc_buf + write_ptr, data, block_size);
+               memcpy(buf_page_list[page].virt_addr + offset,
+                      data, block_size);
 
                data += block_size;
                num_bytes -= block_size;
-
-               write_ptr = 0;
+               write_ptr += block_size;
+               if (write_ptr == async->prealloc_bufsz)
+                       write_ptr = 0;
        }
 }
 
 static void comedi_buf_memcpy_from(struct comedi_subdevice *s,
                                   void *dest, unsigned int nbytes)
 {
-       void *src;
        struct comedi_async *async = s->async;
+       struct comedi_buf_page *buf_page_list = async->buf_map->page_list;
        unsigned int read_ptr = async->buf_read_ptr;
 
        while (nbytes) {
-               unsigned int block_size;
-
-               src = async->prealloc_buf + read_ptr;
-
-               if (nbytes >= async->prealloc_bufsz - read_ptr)
-                       block_size = async->prealloc_bufsz - read_ptr;
-               else
-                       block_size = nbytes;
+               /*
+                * Do not copy beyond page boundary.
+                * Note: prealloc_bufsz is a multiple of PAGE_SIZE.
+                */
+               unsigned int page = read_ptr >> PAGE_SHIFT;
+               unsigned int offset = offset_in_page(read_ptr);
+               unsigned int block_size = min(nbytes, PAGE_SIZE - offset);
 
-               memcpy(dest, src, block_size);
+               memcpy(dest, buf_page_list[page].virt_addr + offset,
+                      block_size);
                nbytes -= block_size;
                dest += block_size;
-               read_ptr = 0;
+               read_ptr += block_size;
+               if (read_ptr == async->prealloc_bufsz)
+                       read_ptr = 0;
        }
 }
 
index b9df9b19d4bd97cdbc7a594ae928a8c4821f871f..37cfef36c1ad6a9da8ce381e63251d30ad5146e3 100644 (file)
@@ -2475,6 +2475,62 @@ done:
        return mask;
 }
 
+static unsigned int comedi_buf_copy_to_user(struct comedi_subdevice *s,
+       void __user *dest, unsigned int src_offset, unsigned int n)
+{
+       struct comedi_buf_map *bm = s->async->buf_map;
+       struct comedi_buf_page *buf_page_list = bm->page_list;
+       unsigned int page = src_offset >> PAGE_SHIFT;
+       unsigned int offset = offset_in_page(src_offset);
+
+       while (n) {
+               unsigned int copy_amount = min(n, PAGE_SIZE - offset);
+               unsigned int uncopied;
+
+               uncopied = copy_to_user(dest, buf_page_list[page].virt_addr +
+                                       offset, copy_amount);
+               copy_amount -= uncopied;
+               n -= copy_amount;
+               if (uncopied)
+                       break;
+
+               dest += copy_amount;
+               page++;
+               if (page == bm->n_pages)
+                       page = 0;       /* buffer wraparound */
+               offset = 0;
+       }
+       return n;
+}
+
+static unsigned int comedi_buf_copy_from_user(struct comedi_subdevice *s,
+       unsigned int dst_offset, const void __user *src, unsigned int n)
+{
+       struct comedi_buf_map *bm = s->async->buf_map;
+       struct comedi_buf_page *buf_page_list = bm->page_list;
+       unsigned int page = dst_offset >> PAGE_SHIFT;
+       unsigned int offset = offset_in_page(dst_offset);
+
+       while (n) {
+               unsigned int copy_amount = min(n, PAGE_SIZE - offset);
+               unsigned int uncopied;
+
+               uncopied = copy_from_user(buf_page_list[page].virt_addr +
+                                         offset, src, copy_amount);
+               copy_amount -= uncopied;
+               n -= copy_amount;
+               if (uncopied)
+                       break;
+
+               src += copy_amount;
+               page++;
+               if (page == bm->n_pages)
+                       page = 0;       /* buffer wraparound */
+               offset = 0;
+       }
+       return n;
+}
+
 static ssize_t comedi_write(struct file *file, const char __user *buf,
                            size_t nbytes, loff_t *offset)
 {
@@ -2516,7 +2572,6 @@ static ssize_t comedi_write(struct file *file, const char __user *buf,
        add_wait_queue(&async->wait_head, &wait);
        while (count == 0 && !retval) {
                unsigned int runflags;
-               unsigned int wp, n1, n2;
 
                set_current_state(TASK_INTERRUPTIBLE);
 
@@ -2555,14 +2610,7 @@ static ssize_t comedi_write(struct file *file, const char __user *buf,
                }
 
                set_current_state(TASK_RUNNING);
-               wp = async->buf_write_ptr;
-               n1 = min(n, async->prealloc_bufsz - wp);
-               n2 = n - n1;
-               m = copy_from_user(async->prealloc_buf + wp, buf, n1);
-               if (m)
-                       m += n2;
-               else if (n2)
-                       m = copy_from_user(async->prealloc_buf, buf + n1, n2);
+               m = comedi_buf_copy_from_user(s, async->buf_write_ptr, buf, n);
                if (m) {
                        n -= m;
                        retval = -EFAULT;
@@ -2651,8 +2699,6 @@ static ssize_t comedi_read(struct file *file, char __user *buf, size_t nbytes,
 
        add_wait_queue(&async->wait_head, &wait);
        while (count == 0 && !retval) {
-               unsigned int rp, n1, n2;
-
                set_current_state(TASK_INTERRUPTIBLE);
 
                m = comedi_buf_read_n_available(s);
@@ -2689,14 +2735,7 @@ static ssize_t comedi_read(struct file *file, char __user *buf, size_t nbytes,
                }
 
                set_current_state(TASK_RUNNING);
-               rp = async->buf_read_ptr;
-               n1 = min(n, async->prealloc_bufsz - rp);
-               n2 = n - n1;
-               m = copy_to_user(buf, async->prealloc_buf + rp, n1);
-               if (m)
-                       m += n2;
-               else if (n2)
-                       m = copy_to_user(buf + n1, async->prealloc_buf, n2);
+               m = comedi_buf_copy_to_user(s, buf, async->buf_read_ptr, n);
                if (m) {
                        n -= m;
                        retval = -EFAULT;