Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

mm/mremap: perform some simple cleanups

Patch series "mm/mremap: permit mremap() move of multiple VMAs", v4.

Historically we've made it a uAPI requirement that mremap() may only
operate on a single VMA at a time.

For instances where VMAs need to be resized, this makes sense, as it
becomes very difficult to determine what a user actually wants should they
indicate a desire to expand or shrink the size of multiple VMAs (truncate?
Adjust sizes individually? Some other strategy?).

However, in instances where a user is moving VMAs, it is restrictive to
disallow this.

This is especially the case when anonymous mapping remap may or may not be
mergeable depending on whether VMAs have or have not been faulted due to
anon_vma assignment and folio index alignment with vma->vm_pgoff.

Often this can result in surprising impact where a moved region is faulted,
then moved back and a user fails to observe a merge from otherwise
compatible, adjacent VMAs.

This change allows such cases to work without the user having to be
cognizant of whether a prior mremap() move or other VMA operations has
resulted in VMA fragmentation.

In order to do this, this series performs a large amount of refactoring,
most pertinently - grouping sanity checks together, separately those that
check input parameters and those relating to VMAs.

we also simplify the post-mmap lock drop processing for uffd and mlock()'d
VMAs.

With this done, we can then fairly straightforwardly implement this
functionality.

This works exclusively for mremap() invocations which specify
MREMAP_FIXED. It is not compatible with VMAs which use userfaultfd, as the
notification of the userland fault handler would require us to drop the
mmap lock.

It is also not compatible with file-backed mappings with customised
get_unmapped_area() handlers as these may not honour MREMAP_FIXED.

The input and output addresses ranges must not overlap. We carefully
account for moves which would result in VMA iterator invalidation.

While there can be gaps between VMAs in the input range, there can be no
gap before the first VMA in the range.


This patch (of 10):

We const-ify the vrm flags parameter to indicate this will never change.

We rename resize_is_valid() to remap_is_valid(), as this function does not
only apply to cases where we resize, so it's simply confusing to refer to
that here.

We remove the BUG() from mremap_at(), as we should not BUG() unless we are
certain it'll result in system instability.

We rename vrm_charge() to vrm_calc_charge() to make it clear this simply
calculates the charged number of pages rather than actually adjusting any
state.

We update the comment for vrm_implies_new_addr() to explain that
MREMAP_DONTUNMAP does not require a set address, but will always be moved.

Additionally consistently use 'res' rather than 'ret' for result values.

No functional change intended.

Link: https://lkml.kernel.org/r/cover.1752770784.git.lorenzo.stoakes@oracle.com
Link: https://lkml.kernel.org/r/d35ad8ce6b2c33b2f2f4ef7ec415f04a35cba34f.1752770784.git.lorenzo.stoakes@oracle.com
Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
Reviewed-by: Vlastimil Babka <vbabka@suse.cz>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Christian Brauner <brauner@kernel.org>
Cc: Jan Kara <jack@suse.cz>
Cc: Jann Horn <jannh@google.com>
Cc: Liam Howlett <liam.howlett@oracle.com>
Cc: Peter Xu <peterx@redhat.com>
Cc: Rik van Riel <riel@surriel.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Lorenzo Stoakes and committed by
Andrew Morton
000c0691 cfea8921

+32 -23
+32 -23
mm/mremap.c
··· 52 52 unsigned long addr; /* User-specified address from which we remap. */ 53 53 unsigned long old_len; /* Length of range being remapped. */ 54 54 unsigned long new_len; /* Desired new length of mapping. */ 55 - unsigned long flags; /* user-specified MREMAP_* flags. */ 55 + const unsigned long flags; /* user-specified MREMAP_* flags. */ 56 56 unsigned long new_addr; /* Optionally, desired new address. */ 57 57 58 58 /* uffd state. */ ··· 909 909 return false; 910 910 } 911 911 912 - /* Do the mremap() flags require that the new_addr parameter be specified? */ 912 + /* 913 + * Will a new address definitely be assigned? This either if the user specifies 914 + * it via MREMAP_FIXED, or if MREMAP_DONTUNMAP is used, indicating we will 915 + * always detemrine a target address. 916 + */ 913 917 static bool vrm_implies_new_addr(struct vma_remap_struct *vrm) 914 918 { 915 919 return vrm->flags & (MREMAP_FIXED | MREMAP_DONTUNMAP); ··· 959 955 * 960 956 * Returns true on success, false if insufficient memory to charge. 961 957 */ 962 - static bool vrm_charge(struct vma_remap_struct *vrm) 958 + static bool vrm_calc_charge(struct vma_remap_struct *vrm) 963 959 { 964 960 unsigned long charged; 965 961 ··· 1264 1260 if (err) 1265 1261 return err; 1266 1262 1267 - /* If accounted, charge the number of bytes the operation will use. */ 1268 - if (!vrm_charge(vrm)) 1263 + /* 1264 + * If accounted, determine the number of bytes the operation will 1265 + * charge. 1266 + */ 1267 + if (!vrm_calc_charge(vrm)) 1269 1268 return -ENOMEM; 1270 1269 1271 1270 /* We don't want racing faults. */ ··· 1307 1300 } 1308 1301 1309 1302 /* 1310 - * resize_is_valid() - Ensure the vma can be resized to the new length at the give 1311 - * address. 1303 + * remap_is_valid() - Ensure the VMA can be moved or resized to the new length, 1304 + * at the given address. 1312 1305 * 1313 1306 * Return 0 on success, error otherwise. 1314 1307 */ 1315 - static int resize_is_valid(struct vma_remap_struct *vrm) 1308 + static int remap_is_valid(struct vma_remap_struct *vrm) 1316 1309 { 1317 1310 struct mm_struct *mm = current->mm; 1318 1311 struct vm_area_struct *vma = vrm->vma; ··· 1451 1444 vrm->old_len = vrm->new_len; 1452 1445 } 1453 1446 1454 - err = resize_is_valid(vrm); 1447 + err = remap_is_valid(vrm); 1455 1448 if (err) 1456 1449 return err; 1457 1450 ··· 1576 1569 struct vm_area_struct *vma = vrm->vma; 1577 1570 VMA_ITERATOR(vmi, mm, vma->vm_end); 1578 1571 1579 - if (!vrm_charge(vrm)) 1572 + if (!vrm_calc_charge(vrm)) 1580 1573 return -ENOMEM; 1581 1574 1582 1575 /* ··· 1637 1630 unsigned long err; 1638 1631 unsigned long addr = vrm->addr; 1639 1632 1640 - err = resize_is_valid(vrm); 1633 + err = remap_is_valid(vrm); 1641 1634 if (err) 1642 1635 return err; 1643 1636 ··· 1710 1703 return expand_vma(vrm); 1711 1704 } 1712 1705 1713 - BUG(); 1706 + /* Should not be possible. */ 1707 + WARN_ON_ONCE(1); 1708 + return -EINVAL; 1714 1709 } 1715 1710 1716 1711 static unsigned long do_mremap(struct vma_remap_struct *vrm) 1717 1712 { 1718 1713 struct mm_struct *mm = current->mm; 1719 1714 struct vm_area_struct *vma; 1720 - unsigned long ret; 1715 + unsigned long res; 1721 1716 1722 - ret = check_mremap_params(vrm); 1723 - if (ret) 1724 - return ret; 1717 + res = check_mremap_params(vrm); 1718 + if (res) 1719 + return res; 1725 1720 1726 1721 vrm->old_len = PAGE_ALIGN(vrm->old_len); 1727 1722 vrm->new_len = PAGE_ALIGN(vrm->new_len); ··· 1735 1726 1736 1727 vma = vrm->vma = vma_lookup(mm, vrm->addr); 1737 1728 if (!vma) { 1738 - ret = -EFAULT; 1729 + res = -EFAULT; 1739 1730 goto out; 1740 1731 } 1741 1732 1742 1733 /* If mseal()'d, mremap() is prohibited. */ 1743 1734 if (!can_modify_vma(vma)) { 1744 - ret = -EPERM; 1735 + res = -EPERM; 1745 1736 goto out; 1746 1737 } 1747 1738 1748 1739 /* Align to hugetlb page size, if required. */ 1749 1740 if (is_vm_hugetlb_page(vma) && !align_hugetlb(vrm)) { 1750 - ret = -EINVAL; 1741 + res = -EINVAL; 1751 1742 goto out; 1752 1743 } 1753 1744 1754 1745 vrm->remap_type = vrm_remap_type(vrm); 1755 1746 1756 1747 /* Actually execute mremap. */ 1757 - ret = vrm_implies_new_addr(vrm) ? mremap_to(vrm) : mremap_at(vrm); 1748 + res = vrm_implies_new_addr(vrm) ? mremap_to(vrm) : mremap_at(vrm); 1758 1749 1759 1750 out: 1760 1751 if (vrm->mmap_locked) { 1761 1752 mmap_write_unlock(mm); 1762 1753 vrm->mmap_locked = false; 1763 1754 1764 - if (!offset_in_page(ret) && vrm->mlocked && vrm->new_len > vrm->old_len) 1755 + if (!offset_in_page(res) && vrm->mlocked && vrm->new_len > vrm->old_len) 1765 1756 mm_populate(vrm->new_addr + vrm->old_len, vrm->delta); 1766 1757 } 1767 1758 1768 1759 userfaultfd_unmap_complete(mm, vrm->uf_unmap_early); 1769 - mremap_userfaultfd_complete(vrm->uf, vrm->addr, ret, vrm->old_len); 1760 + mremap_userfaultfd_complete(vrm->uf, vrm->addr, res, vrm->old_len); 1770 1761 userfaultfd_unmap_complete(mm, vrm->uf_unmap); 1771 1762 1772 - return ret; 1763 + return res; 1773 1764 } 1774 1765 1775 1766 /*