Re: [PATCH v3 08/13] mm: add ability to take further action in vm_area_desc

Jason Gunthorpe
2025-09-16T17:28:41Z

On Tue, Sep 16, 2025 at 03:11:54PM +0100, Lorenzo Stoakes wrote:

> +/* What action should be taken after an .mmap_prepare call is complete? */
> +enum mmap_action_type {
> + MMAP_NOTHING, /* Mapping is complete, no further action. */
> + MMAP_REMAP_PFN, /* Remap PFN range. */

Seems like it would be a bit tider to include MMAP_IO_REMAP_PFN here
instead of having the is_io_remap bool.

> @@ -1155,15 +1155,18 @@ int __compat_vma_mmap_prepare(const struct file_operations *f_op,
> .vm_file = vma->vm_file,
> .vm_flags = vma->vm_flags,
> .page_prot = vma->vm_page_prot,
> +
> + .action.type = MMAP_NOTHING, /* Default */
> };
> int err;
>
> err = f_op->mmap_prepare(&desc);
> if (err)
> return err;
> - set_vma_from_desc(vma, &desc);
>
> - return 0;
> + mmap_action_prepare(&desc.action, &desc);
> + set_vma_from_desc(vma, &desc);
> + return mmap_action_complete(&desc.action, vma);
> }
> EXPORT_SYMBOL(__compat_vma_mmap_prepare);

A function called prepare that now calls complete has become a bit oddly named??

> +int mmap_action_complete(struct mmap_action *action,
> + struct vm_area_struct *vma)
> +{
> + int err = 0;
> +
> + switch (action->type) {
> + case MMAP_NOTHING:
> + break;
> + case MMAP_REMAP_PFN:
> + VM_WARN_ON_ONCE((vma->vm_flags & VM_REMAP_FLAGS) !=
> + VM_REMAP_FLAGS);

This is checked in remap_pfn_range_complete() IIRC? Probably not
needed here as well then.

> + if (action->remap.is_io_remap)
> + err = io_remap_pfn_range_complete(vma, action->remap.start,
> + action->remap.start_pfn, action->remap.size,
> + action->remap.pgprot);
> + else
> + err = remap_pfn_range_complete(vma, action->remap.start,
> + action->remap.start_pfn, action->remap.size,
> + action->remap.pgprot);
> + break;
> + }
> +
> + /*
> + * If an error occurs, unmap the VMA altogether and return an error. We
> + * only clear the newly allocated VMA, since this function is only
> + * invoked if we do NOT merge, so we only clean up the VMA we created.
> + */
> + if (err) {
> + const size_t len = vma_pages(vma) << PAGE_SHIFT;
> +
> + do_munmap(current->mm, vma->vm_start, len, NULL);
> +
> + if (action->error_hook) {
> + /* We may want to filter the error. */
> + err = action->error_hook(err);
> +
> + /* The caller should not clear the error. */
> + VM_WARN_ON_ONCE(!err);
> + }
> + return err;
> + }
> +
> + if (action->success_hook)
> + err = action->success_hook(vma);
> +
> + return err;

I would write this as

if (action->success_hook)
return action->success_hook(vma);

return 0;

Just for emphasis this is the success path.

> +int mmap_action_complete(struct mmap_action *action,
> + struct vm_area_struct *vma)
> +{
> + int err = 0;
> +
> + switch (action->type) {
> + case MMAP_NOTHING:
> + break;
> + case MMAP_REMAP_PFN:
> + WARN_ON_ONCE(1); /* nommu cannot handle this. */
> +
> + break;
> + }
> +
> + /*
> + * If an error occurs, unmap the VMA altogether and return an error. We
> + * only clear the newly allocated VMA, since this function is only
> + * invoked if we do NOT merge, so we only clean up the VMA we created.
> + */
> + if (err) {
> + const size_t len = vma_pages(vma) << PAGE_SHIFT;
> +
> + do_munmap(current->mm, vma->vm_start, len, NULL);
> +
> + if (action->error_hook) {
> + /* We may want to filter the error. */
> + err = action->error_hook(err);
> +
> + /* The caller should not clear the error. */
> + VM_WARN_ON_ONCE(!err);
> + }
> + return err;
> + }

err is never !0 here, so this should go to a later patch/series.

Also seems like this cleanup wants to be in a function that is not
protected by #ifdef nommu since the code is identical on both branches.

> + if (action->success_hook)
> + err = action->success_hook(vma);
> +
> + return 0;

return err, though prefer to match above, and probably this sequence
should be pulled into the same shared function as above too.

Jason