]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
mm: Change failure of MAP_FIXED to restoring the gap on failure
authorLiam R. Howlett <Liam.Howlett@Oracle.com>
Mon, 19 Aug 2024 19:16:33 +0000 (15:16 -0400)
committerLiam R. Howlett <Liam.Howlett@Oracle.com>
Fri, 23 Aug 2024 12:46:02 +0000 (08:46 -0400)
Prior to call_mmap(), the vmas that will be replaced need to clear the
way for what may happen in the call_mmap().  This clean up work includes
clearing the ptes and calling the close() vm_ops.  Some users do more
setup than can be restored by calling the vm_ops open() function.  It is
safer to store the gap in the vma tree in these cases.

That is to say that the failure scenario that existed before the
MAP_FIXED gap exposure is restored as it is safer than trying to undo a
partial mapping.

Since abort_munmap_vmas() is only reattaching vmas with this change, the
function is renamed to reattach_vmas().

There is also a secondary failure that may occur if there is not enough
memory to store the gap.  In this case, the vmas are reattached and
resources freed.  If the system cannot complete the call_mmap() and
fails to allocate with GFP_KERNEL, then the system will print a warning
about the failure.

Signed-off-by: Liam R. Howlett <Liam.Howlett@Oracle.com>
Reviewed-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
mm/mmap.c
mm/vma.c
mm/vma.h

index 6550d9470d3afafd6ae91a2c816360b775a6ee53..217da37ef71de73a76131b51dd52c93b85913d93 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -1622,8 +1622,7 @@ unacct_error:
                vm_unacct_memory(charged);
 
 abort_munmap:
-       if (vms.nr_pages)
-               abort_munmap_vmas(&mas_detach, vms.closed_vm_ops);
+       vms_abort_munmap_vmas(&vms, &mas_detach);
        validate_mm(mm);
        return error;
 }
index 2f053d72cab6511e2580049d95989a59a1b8ee06..9379167a07e3114266687b6313bc4fc25803724e 100644 (file)
--- a/mm/vma.c
+++ b/mm/vma.c
@@ -883,7 +883,7 @@ userfaultfd_error:
 munmap_gather_failed:
 end_split_failed:
 modify_vma_failed:
-       abort_munmap_vmas(mas_detach, /* closed = */ false);
+       reattach_vmas(mas_detach);
 start_split_failed:
 map_count_exceeded:
        return error;
@@ -928,7 +928,7 @@ int do_vmi_align_munmap(struct vma_iterator *vmi, struct vm_area_struct *vma,
        return 0;
 
 clear_tree_failed:
-       abort_munmap_vmas(&mas_detach, /* closed = */ false);
+       reattach_vmas(&mas_detach);
 gather_failed:
        validate_mm(mm);
        return error;
index 756dd42a6ec4695b1effa115adf27c2292f50edb..f710812482a19306e1b0a2568e688f84bddb13e0 100644 (file)
--- a/mm/vma.h
+++ b/mm/vma.h
@@ -82,6 +82,22 @@ int vma_expand(struct vma_iterator *vmi, struct vm_area_struct *vma,
 int vma_shrink(struct vma_iterator *vmi, struct vm_area_struct *vma,
               unsigned long start, unsigned long end, pgoff_t pgoff);
 
+static inline int vma_iter_store_gfp(struct vma_iterator *vmi,
+                       struct vm_area_struct *vma, gfp_t gfp)
+
+{
+       if (vmi->mas.status != ma_start &&
+           ((vmi->mas.index > vma->vm_start) || (vmi->mas.last < vma->vm_start)))
+               vma_iter_invalidate(vmi);
+
+       __mas_set_range(&vmi->mas, vma->vm_start, vma->vm_end - 1);
+       mas_store_gfp(&vmi->mas, vma, gfp);
+       if (unlikely(mas_is_err(&vmi->mas)))
+               return -ENOMEM;
+
+       return 0;
+}
+
 /*
  * init_vma_munmap() - Initializer wrapper for vma_munmap_struct
  * @vms: The vma munmap struct
@@ -127,24 +143,58 @@ void vms_clean_up_area(struct vma_munmap_struct *vms,
                struct ma_state *mas_detach, bool mm_wr_locked);
 
 /*
- * abort_munmap_vmas - Undo any munmap work and free resources
+ * reattach_vmas() - Undo any munmap work and free resources
+ * @mas_detach: The maple state with the detached maple tree
  *
  * Reattach any detached vmas and free up the maple tree used to track the vmas.
  */
-static inline void abort_munmap_vmas(struct ma_state *mas_detach, bool closed)
+static inline void reattach_vmas(struct ma_state *mas_detach)
 {
        struct vm_area_struct *vma;
 
        mas_set(mas_detach, 0);
-       mas_for_each(mas_detach, vma, ULONG_MAX) {
+       mas_for_each(mas_detach, vma, ULONG_MAX)
                vma_mark_detached(vma, false);
-               if (closed && vma->vm_ops && vma->vm_ops->open)
-                       vma->vm_ops->open(vma);
-       }
 
        __mt_destroy(mas_detach->tree);
 }
 
+/*
+ * vms_abort_munmap_vmas() - Undo as much as possible from an aborted munmap()
+ * operation.
+ * @vms: The vma unmap structure
+ * @mas_detach: The maple state with the detached maple tree
+ *
+ * Reattach any detached vmas, free up the maple tree used to track the vmas.
+ * If that's not possible because the ptes are cleared (and vm_ops->closed() may
+ * have been called), then a NULL is written over the vmas and the vmas are
+ * removed (munmap() completed).
+ */
+static inline void vms_abort_munmap_vmas(struct vma_munmap_struct *vms,
+               struct ma_state *mas_detach)
+{
+       if (!vms->nr_pages)
+               return;
+
+       if (vms->clear_ptes)
+               return reattach_vmas(mas_detach);
+
+       /*
+        * Aborting cannot just call the vm_ops open() because they are often
+        * not symmetrical and state data has been lost.  Resort to the old
+        * failure method of leaving a gap where the MAP_FIXED mapping failed.
+        */
+       if (unlikely(vma_iter_store_gfp(vms->vmi, NULL, GFP_KERNEL))) {
+               pr_warn_once("%s: (%d) Unable to abort munmap() operation\n",
+                            current->comm, current->pid);
+               /* Leaving vmas detached and in-tree may hamper recovery */
+               reattach_vmas(mas_detach);
+       } else {
+               /* Clean up the insertion of unfortunate the gap */
+               vms_complete_munmap_vmas(vms, mas_detach);
+       }
+}
+
 int
 do_vmi_align_munmap(struct vma_iterator *vmi, struct vm_area_struct *vma,
                    struct mm_struct *mm, unsigned long start,
@@ -297,22 +347,6 @@ static inline struct vm_area_struct *vma_prev_limit(struct vma_iterator *vmi,
        return mas_prev(&vmi->mas, min);
 }
 
-static inline int vma_iter_store_gfp(struct vma_iterator *vmi,
-                       struct vm_area_struct *vma, gfp_t gfp)
-{
-       if (vmi->mas.status != ma_start &&
-           ((vmi->mas.index > vma->vm_start) || (vmi->mas.last < vma->vm_start)))
-               vma_iter_invalidate(vmi);
-
-       __mas_set_range(&vmi->mas, vma->vm_start, vma->vm_end - 1);
-       mas_store_gfp(&vmi->mas, vma, gfp);
-       if (unlikely(mas_is_err(&vmi->mas)))
-               return -ENOMEM;
-
-       return 0;
-}
-
-
 /*
  * These three helpers classifies VMAs for virtual memory accounting.
  */