We can use the mmap insert pages functionality provided for use in mmap_prepare to insert the kcov pages as required. This does necessitate an allocation, but since it's in the mmap path this doesn't seem egregious. The allocation/freeing of the pages array is handled automatically by vma_desc_set_mixedmap_pages() and the mapping logic. Signed-off-by: Lorenzo Stoakes --- kernel/kcov.c | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/kernel/kcov.c b/kernel/kcov.c index 1d85597057e1..2bcf403e5f6f 100644 --- a/kernel/kcov.c +++ b/kernel/kcov.c @@ -484,31 +484,41 @@ void kcov_task_exit(struct task_struct *t) kcov_put(kcov); } -static int kcov_mmap(struct file *filep, struct vm_area_struct *vma) +static int kcov_mmap_error(int err) +{ + pr_warn_once("kcov: vm_insert_page() failed\n"); + return err; +} + +static int kcov_mmap_prepare(struct vm_area_desc *desc) { int res = 0; - struct kcov *kcov = vma->vm_file->private_data; - unsigned long size, off; - struct page *page; + struct kcov *kcov = desc->file->private_data; + unsigned long size, nr_pages, i; + struct page **pages; unsigned long flags; spin_lock_irqsave(&kcov->lock, flags); size = kcov->size * sizeof(unsigned long); - if (kcov->area == NULL || vma->vm_pgoff != 0 || - vma->vm_end - vma->vm_start != size) { + if (kcov->area == NULL || desc->pgoff != 0 || + vma_desc_size(desc) != size) { res = -EINVAL; goto exit; } spin_unlock_irqrestore(&kcov->lock, flags); - vm_flags_set(vma, VM_DONTEXPAND); - for (off = 0; off < size; off += PAGE_SIZE) { - page = vmalloc_to_page(kcov->area + off); - res = vm_insert_page(vma, vma->vm_start + off, page); - if (res) { - pr_warn_once("kcov: vm_insert_page() failed\n"); - return res; - } - } + + desc->vm_flags |= VM_DONTEXPAND; + nr_pages = size >> PAGE_SHIFT; + + pages = mmap_action_mixedmap_pages(&desc->action, desc->start, + nr_pages); + if (!pages) + return -ENOMEM; + + for (i = 0; i < nr_pages; i++) + pages[i] = vmalloc_to_page(kcov->area + i * PAGE_SIZE); + desc->action.error_hook = kcov_mmap_error; + return 0; exit: spin_unlock_irqrestore(&kcov->lock, flags); @@ -761,7 +771,7 @@ static const struct file_operations kcov_fops = { .open = kcov_open, .unlocked_ioctl = kcov_ioctl, .compat_ioctl = kcov_ioctl, - .mmap = kcov_mmap, + .mmap_prepare = kcov_mmap_prepare, .release = kcov_close, }; -- 2.51.0