On Sat, Oct 3, 2026 at 6:36 PM Lorenzo Stoakes (ARM) <[email protected]> wrote: > > Replace the deprecated .mmap hook with its replacement .mmap_prepare. As > part of this change, additionally take the approach of mapping pages upon > mmap rather than providing a fault handler. > > The page span cannot be mutated when an mmap mapping is in place, so this > is safe to do in advance (the MON_IOCT_RING_SIZE ioctl operation exits > -EBUSY if it's attempted, gated by the rp->mmap_active reference count). > > Utilise the newly introduced mmap_action_map_discontig_kernel_pages() to do > this, which allows for iteration over pages in mon_bin_discontig_get(). > > mon_bin_discontig_init() increments the rp->mmap_active reference count to > stabilise page spans. Should an error arise the core unmaps the VMA and > mon_bin_vma_close() drops the reference again. > > The vm_ops->close hook implemented in mon_bin_vma_close() will ensure > correct reference count arithmetic upon unmap (with mon_bin_vma_open() > accounting for splitting). > > The existing semantics are all retained, including not mapping past the > range of available pages, with a SIGBUS being raised in a userland process > that attempts to access past this point. > > Ultimately insert_page() is invoked to insert each page, which increments > the reference count on each mapped page. This mimics what was being done > previously, only we pre-map the entire range rather than doing so on > demand. > > The existing fault handler did nothing that required demand paging, and was > presumably implemented this way for historical reasons. > > One behavioural difference: pages are no longer faulted in on demand, so a > page discarded with MADV_DONTNEED is not repopulated and a subsequent > access raises SIGBUS, as with other pre-populated kernel mappings. > > Acked-by: Greg Kroah-Hartman <[email protected]> > Signed-off-by: Lorenzo Stoakes (ARM) <[email protected]>
Reviewed-by: Suren Baghdasaryan <[email protected]> > --- > drivers/usb/mon/mon_bin.c | 82 > ++++++++++++++++++++++++++++++----------------- > 1 file changed, 53 insertions(+), 29 deletions(-) > > diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c > index 687f6a8981f3..9d00b21a8153 100644 > --- a/drivers/usb/mon/mon_bin.c > +++ b/drivers/usb/mon/mon_bin.c > @@ -1219,6 +1219,15 @@ mon_bin_poll(struct file *file, struct > poll_table_struct *wait) > return mask; > } > > +static void __mon_bin_vma_open(struct mon_reader_bin *rp) > +{ > + unsigned long flags; > + > + spin_lock_irqsave(&rp->b_lock, flags); > + rp->mmap_active++; > + spin_unlock_irqrestore(&rp->b_lock, flags); > +} > + > /* > * open and close: just keep track of how many times the device is > * mapped, to use the proper memory allocation function. > @@ -1226,64 +1235,79 @@ mon_bin_poll(struct file *file, struct > poll_table_struct *wait) > static void mon_bin_vma_open(struct vm_area_struct *vma) > { > struct mon_reader_bin *rp = vma->vm_private_data; > - unsigned long flags; > > - spin_lock_irqsave(&rp->b_lock, flags); > - rp->mmap_active++; > - spin_unlock_irqrestore(&rp->b_lock, flags); > + __mon_bin_vma_open(rp); > } > > -static void mon_bin_vma_close(struct vm_area_struct *vma) > +static void __mon_bin_vma_close(struct mon_reader_bin *rp) > { > unsigned long flags; > > - struct mon_reader_bin *rp = vma->vm_private_data; > spin_lock_irqsave(&rp->b_lock, flags); > rp->mmap_active--; > spin_unlock_irqrestore(&rp->b_lock, flags); > } > > -/* > - * Map ring pages to user space. > - */ > -static vm_fault_t mon_bin_vma_fault(struct vm_fault *vmf) > +static void mon_bin_vma_close(struct vm_area_struct *vma) > { > - struct mon_reader_bin *rp = vmf->vma->vm_private_data; > + struct mon_reader_bin *rp = vma->vm_private_data; > + > + __mon_bin_vma_close(rp); > +} > + > +static const struct vm_operations_struct mon_bin_vm_ops = { > + .open = mon_bin_vma_open, > + .close = mon_bin_vma_close, > +}; > + > +static int mon_bin_discontig_init(void *vm_private_data, void **private) > +{ > + struct mon_reader_bin *rp = vm_private_data; > + > + /* Dropped by mon_bin_vma_close() on unmap, including on error. */ > + __mon_bin_vma_open(rp); > + return 0; > +} > + > +static int mon_bin_discontig_get(struct discontig_kernel_page_state *state) > +{ > + struct mon_reader_bin *rp = state->vm_private_data; > unsigned long offset, chunk_idx; > - struct page *pageptr; > unsigned long flags; > > spin_lock_irqsave(&rp->b_lock, flags); > - offset = vmf->pgoff << PAGE_SHIFT; > + > + offset = state->pgoff << PAGE_SHIFT; > if (offset >= rp->b_size) { > spin_unlock_irqrestore(&rp->b_lock, flags); > - return VM_FAULT_SIGBUS; > + discontig_kernel_map_abort(state); > + return 0; > } > chunk_idx = offset / CHUNK_SIZE; > - pageptr = rp->b_vec[chunk_idx].pg; > - get_page(pageptr); > - vmf->page = pageptr; > + discontig_kernel_map_page(state, rp->b_vec[chunk_idx].pg); > + > spin_unlock_irqrestore(&rp->b_lock, flags); > return 0; > } > > -static const struct vm_operations_struct mon_bin_vm_ops = { > - .open = mon_bin_vma_open, > - .close = mon_bin_vma_close, > - .fault = mon_bin_vma_fault, > +static const struct discontig_kernel_page_ops mon_discontig_ops = { > + .init = mon_bin_discontig_init, > + .get = mon_bin_discontig_get, > }; > > -static int mon_bin_mmap(struct file *filp, struct vm_area_struct *vma) > +static int mon_bin_mmap_prepare(struct vm_area_desc *desc) > { > - /* don't do anything here: "fault" will set up page table entries */ > - vma->vm_ops = &mon_bin_vm_ops; > + const struct file *filp = desc->file; > > - if (vma->vm_flags & VM_WRITE) > + if (vma_desc_test(desc, VMA_WRITE_BIT)) > return -EPERM; > > - vm_flags_mod(vma, VM_DONTEXPAND | VM_DONTDUMP, VM_MAYWRITE); > - vma->vm_private_data = filp->private_data; > - mon_bin_vma_open(vma); > + desc->vm_ops = &mon_bin_vm_ops; > + vma_desc_clear_flags(desc, VMA_MAYWRITE_BIT); > + vma_desc_set_flags(desc, VMA_DONTEXPAND_BIT, VMA_DONTDUMP_BIT); > + desc->private_data = filp->private_data; > + > + mmap_action_map_discontig_kernel_pages(desc, NULL, > &mon_discontig_ops); > return 0; > } > > @@ -1298,7 +1322,7 @@ static const struct file_operations mon_fops_binary = { > .compat_ioctl = mon_bin_compat_ioctl, > #endif > .release = mon_bin_release, > - .mmap = mon_bin_mmap, > + .mmap_prepare = mon_bin_mmap_prepare, > }; > > static int mon_bin_wait_event(struct file *file, struct mon_reader_bin *rp) > > -- > 2.55.0 >
