Skip to content

Commit d5c8aec

Browse files
lorenzo-stoakesakpm00
authored andcommitted
mm/mremap: initial refactor of move_vma()
Update move_vma() to use the threaded VRM object, de-duplicate code and separate into smaller functions to aid readability and debug-ability. This in turn allows further simplification of expand_vma() as we can simply thread VRM through the function. We also take the opportunity to abstract the account charging page count into the VRM in order that we can correctly thread this through the operation. We additionally do the same for tracking mm statistics - exec_vm, stack_vm, data_vm, and locked_vm. As part of this change, we slightly modify when locked pages statistics are counted for in mm_struct statistics. However this should cause no issues, as there is no chance of underflow, nor will any rlimit failures occur as a result. This is an intermediate step before a further refactoring of move_vma() in order to aid review. Link: https://lkml.kernel.org/r/ab611d6efae11bddab2db2b8bb3925b1d1954c7d.1741639347.git.lorenzo.stoakes@oracle.com Signed-off-by: Lorenzo Stoakes <[email protected]> Reviewed-by: Liam R. Howlett <[email protected]> Reviewed-by: Vlastimil Babka <[email protected]> Cc: Harry Yoo <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
1 parent 221bf5c commit d5c8aec

File tree

1 file changed

+122
-64
lines changed

1 file changed

+122
-64
lines changed

mm/mremap.c

Lines changed: 122 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ struct vma_remap_struct {
6868
bool mlocked; /* Was the VMA mlock()'d? */
6969
enum mremap_type remap_type; /* expand, shrink, etc. */
7070
bool mmap_locked; /* Is mm currently write-locked? */
71+
unsigned long charged; /* If VM_ACCOUNT, # pages to account. */
7172
};
7273

7374
static pud_t *get_old_pud(struct mm_struct *mm, unsigned long addr)
@@ -816,35 +817,88 @@ static unsigned long vrm_set_new_addr(struct vma_remap_struct *vrm)
816817
return 0;
817818
}
818819

819-
static unsigned long move_vma(struct vm_area_struct *vma,
820-
unsigned long old_addr, unsigned long old_len,
821-
unsigned long new_len, unsigned long new_addr,
822-
bool *mlocked, unsigned long flags,
823-
struct vm_userfaultfd_ctx *uf, struct list_head *uf_unmap)
820+
/*
821+
* Keep track of pages which have been added to the memory mapping. If the VMA
822+
* is accounted, also check to see if there is sufficient memory.
823+
*
824+
* Returns true on success, false if insufficient memory to charge.
825+
*/
826+
static bool vrm_charge(struct vma_remap_struct *vrm)
824827
{
825-
long to_account = new_len - old_len;
826-
struct mm_struct *mm = vma->vm_mm;
827-
struct vm_area_struct *new_vma;
828-
unsigned long vm_flags = vma->vm_flags;
829-
unsigned long new_pgoff;
830-
unsigned long moved_len;
831-
bool account_start = false;
832-
bool account_end = false;
833-
unsigned long hiwater_vm;
834-
int err = 0;
835-
bool need_rmap_locks;
836-
struct vma_iterator vmi;
828+
unsigned long charged;
829+
830+
if (!(vrm->vma->vm_flags & VM_ACCOUNT))
831+
return true;
832+
833+
/*
834+
* If we don't unmap the old mapping, then we account the entirety of
835+
* the length of the new one. Otherwise it's just the delta in size.
836+
*/
837+
if (vrm->flags & MREMAP_DONTUNMAP)
838+
charged = vrm->new_len >> PAGE_SHIFT;
839+
else
840+
charged = vrm->delta >> PAGE_SHIFT;
841+
842+
843+
/* This accounts 'charged' pages of memory. */
844+
if (security_vm_enough_memory_mm(current->mm, charged))
845+
return false;
846+
847+
vrm->charged = charged;
848+
return true;
849+
}
850+
851+
/*
852+
* an error has occurred so we will not be using vrm->charged memory. Unaccount
853+
* this memory if the VMA is accounted.
854+
*/
855+
static void vrm_uncharge(struct vma_remap_struct *vrm)
856+
{
857+
if (!(vrm->vma->vm_flags & VM_ACCOUNT))
858+
return;
859+
860+
vm_unacct_memory(vrm->charged);
861+
vrm->charged = 0;
862+
}
863+
864+
/*
865+
* Update mm exec_vm, stack_vm, data_vm, and locked_vm fields as needed to
866+
* account for 'bytes' memory used, and if locked, indicate this in the VRM so
867+
* we can handle this correctly later.
868+
*/
869+
static void vrm_stat_account(struct vma_remap_struct *vrm,
870+
unsigned long bytes)
871+
{
872+
unsigned long pages = bytes >> PAGE_SHIFT;
873+
struct mm_struct *mm = current->mm;
874+
struct vm_area_struct *vma = vrm->vma;
875+
876+
vm_stat_account(mm, vma->vm_flags, pages);
877+
if (vma->vm_flags & VM_LOCKED) {
878+
mm->locked_vm += pages;
879+
vrm->mlocked = true;
880+
}
881+
}
882+
883+
/*
884+
* Perform checks before attempting to write a VMA prior to it being
885+
* moved.
886+
*/
887+
static unsigned long prep_move_vma(struct vma_remap_struct *vrm,
888+
unsigned long *vm_flags_ptr)
889+
{
890+
unsigned long err = 0;
891+
struct vm_area_struct *vma = vrm->vma;
892+
unsigned long old_addr = vrm->addr;
893+
unsigned long old_len = vrm->old_len;
837894

838895
/*
839896
* We'd prefer to avoid failure later on in do_munmap:
840897
* which may split one vma into three before unmapping.
841898
*/
842-
if (mm->map_count >= sysctl_max_map_count - 3)
899+
if (current->mm->map_count >= sysctl_max_map_count - 3)
843900
return -ENOMEM;
844901

845-
if (unlikely(flags & MREMAP_DONTUNMAP))
846-
to_account = new_len;
847-
848902
if (vma->vm_ops && vma->vm_ops->may_split) {
849903
if (vma->vm_start != old_addr)
850904
err = vma->vm_ops->may_split(vma, old_addr);
@@ -862,22 +916,46 @@ static unsigned long move_vma(struct vm_area_struct *vma,
862916
* so KSM can come around to merge on vma and new_vma afterwards.
863917
*/
864918
err = ksm_madvise(vma, old_addr, old_addr + old_len,
865-
MADV_UNMERGEABLE, &vm_flags);
919+
MADV_UNMERGEABLE, vm_flags_ptr);
866920
if (err)
867921
return err;
868922

869-
if (vm_flags & VM_ACCOUNT) {
870-
if (security_vm_enough_memory_mm(mm, to_account >> PAGE_SHIFT))
871-
return -ENOMEM;
872-
}
923+
return 0;
924+
}
925+
926+
static unsigned long move_vma(struct vma_remap_struct *vrm)
927+
{
928+
struct mm_struct *mm = current->mm;
929+
struct vm_area_struct *vma = vrm->vma;
930+
struct vm_area_struct *new_vma;
931+
unsigned long vm_flags = vma->vm_flags;
932+
unsigned long old_addr = vrm->addr, new_addr = vrm->new_addr;
933+
unsigned long old_len = vrm->old_len, new_len = vrm->new_len;
934+
unsigned long new_pgoff;
935+
unsigned long moved_len;
936+
unsigned long account_start = false;
937+
unsigned long account_end = false;
938+
unsigned long hiwater_vm;
939+
int err;
940+
bool need_rmap_locks;
941+
struct vma_iterator vmi;
942+
943+
err = prep_move_vma(vrm, &vm_flags);
944+
if (err)
945+
return err;
946+
947+
/* If accounted, charge the number of bytes the operation will use. */
948+
if (!vrm_charge(vrm))
949+
return -ENOMEM;
873950

874951
vma_start_write(vma);
875952
new_pgoff = vma->vm_pgoff + ((old_addr - vma->vm_start) >> PAGE_SHIFT);
876-
new_vma = copy_vma(&vma, new_addr, new_len, new_pgoff,
953+
new_vma = copy_vma(&vrm->vma, new_addr, new_len, new_pgoff,
877954
&need_rmap_locks);
955+
/* This may have been updated. */
956+
vma = vrm->vma;
878957
if (!new_vma) {
879-
if (vm_flags & VM_ACCOUNT)
880-
vm_unacct_memory(to_account >> PAGE_SHIFT);
958+
vrm_uncharge(vrm);
881959
return -ENOMEM;
882960
}
883961

@@ -902,15 +980,15 @@ static unsigned long move_vma(struct vm_area_struct *vma,
902980
old_addr = new_addr;
903981
new_addr = err;
904982
} else {
905-
mremap_userfaultfd_prep(new_vma, uf);
983+
mremap_userfaultfd_prep(new_vma, vrm->uf);
906984
}
907985

908986
if (is_vm_hugetlb_page(vma)) {
909987
clear_vma_resv_huge_pages(vma);
910988
}
911989

912990
/* Conceal VM_ACCOUNT so old reservation is not undone */
913-
if (vm_flags & VM_ACCOUNT && !(flags & MREMAP_DONTUNMAP)) {
991+
if (vm_flags & VM_ACCOUNT && !(vrm->flags & MREMAP_DONTUNMAP)) {
914992
vm_flags_clear(vma, VM_ACCOUNT);
915993
if (vma->vm_start < old_addr)
916994
account_start = true;
@@ -928,13 +1006,12 @@ static unsigned long move_vma(struct vm_area_struct *vma,
9281006
* If this were a serious issue, we'd add a flag to do_munmap().
9291007
*/
9301008
hiwater_vm = mm->hiwater_vm;
931-
vm_stat_account(mm, vma->vm_flags, new_len >> PAGE_SHIFT);
9321009

9331010
/* Tell pfnmap has moved from this vma */
9341011
if (unlikely(vma->vm_flags & VM_PFNMAP))
9351012
untrack_pfn_clear(vma);
9361013

937-
if (unlikely(!err && (flags & MREMAP_DONTUNMAP))) {
1014+
if (unlikely(!err && (vrm->flags & MREMAP_DONTUNMAP))) {
9381015
/* We always clear VM_LOCKED[ONFAULT] on the old vma */
9391016
vm_flags_clear(vma, VM_LOCKED_MASK);
9401017

@@ -947,22 +1024,20 @@ static unsigned long move_vma(struct vm_area_struct *vma,
9471024
unlink_anon_vmas(vma);
9481025

9491026
/* Because we won't unmap we don't need to touch locked_vm */
1027+
vrm_stat_account(vrm, new_len);
9501028
return new_addr;
9511029
}
9521030

1031+
vrm_stat_account(vrm, new_len);
1032+
9531033
vma_iter_init(&vmi, mm, old_addr);
954-
if (do_vmi_munmap(&vmi, mm, old_addr, old_len, uf_unmap, false) < 0) {
1034+
if (do_vmi_munmap(&vmi, mm, old_addr, old_len, vrm->uf_unmap, false) < 0) {
9551035
/* OOM: unable to split vma, just get accounts right */
956-
if (vm_flags & VM_ACCOUNT && !(flags & MREMAP_DONTUNMAP))
1036+
if (vm_flags & VM_ACCOUNT && !(vrm->flags & MREMAP_DONTUNMAP))
9571037
vm_acct_memory(old_len >> PAGE_SHIFT);
9581038
account_start = account_end = false;
9591039
}
9601040

961-
if (vm_flags & VM_LOCKED) {
962-
mm->locked_vm += new_len >> PAGE_SHIFT;
963-
*mlocked = true;
964-
}
965-
9661041
mm->hiwater_vm = hiwater_vm;
9671042

9681043
/* Restore VM_ACCOUNT if one or two pieces of vma left */
@@ -1141,9 +1216,7 @@ static unsigned long mremap_to(struct vma_remap_struct *vrm)
11411216
if (err)
11421217
return err;
11431218

1144-
return move_vma(vrm->vma, vrm->addr, vrm->old_len, vrm->new_len,
1145-
vrm->new_addr, &vrm->mlocked, vrm->flags,
1146-
vrm->uf, vrm->uf_unmap);
1219+
return move_vma(vrm);
11471220
}
11481221

11491222
static int vma_expandable(struct vm_area_struct *vma, unsigned long delta)
@@ -1248,17 +1321,11 @@ static unsigned long check_mremap_params(struct vma_remap_struct *vrm)
12481321
static unsigned long expand_vma_in_place(struct vma_remap_struct *vrm)
12491322
{
12501323
struct mm_struct *mm = current->mm;
1251-
long pages = vrm->delta >> PAGE_SHIFT;
12521324
struct vm_area_struct *vma = vrm->vma;
12531325
VMA_ITERATOR(vmi, mm, vma->vm_end);
1254-
long charged = 0;
1255-
1256-
if (vma->vm_flags & VM_ACCOUNT) {
1257-
if (security_vm_enough_memory_mm(mm, pages))
1258-
return -ENOMEM;
12591326

1260-
charged = pages;
1261-
}
1327+
if (!vrm_charge(vrm))
1328+
return -ENOMEM;
12621329

12631330
/*
12641331
* Function vma_merge_extend() is called on the
@@ -1271,15 +1338,11 @@ static unsigned long expand_vma_in_place(struct vma_remap_struct *vrm)
12711338
*/
12721339
vma = vrm->vma = vma_merge_extend(&vmi, vma, vrm->delta);
12731340
if (!vma) {
1274-
vm_unacct_memory(charged);
1341+
vrm_uncharge(vrm);
12751342
return -ENOMEM;
12761343
}
12771344

1278-
vm_stat_account(mm, vma->vm_flags, pages);
1279-
if (vma->vm_flags & VM_LOCKED) {
1280-
mm->locked_vm += pages;
1281-
vrm->mlocked = true;
1282-
}
1345+
vrm_stat_account(vrm, vrm->delta);
12831346

12841347
return 0;
12851348
}
@@ -1319,11 +1382,7 @@ static bool align_hugetlb(struct vma_remap_struct *vrm)
13191382
static unsigned long expand_vma(struct vma_remap_struct *vrm)
13201383
{
13211384
unsigned long err;
1322-
struct vm_area_struct *vma = vrm->vma;
13231385
unsigned long addr = vrm->addr;
1324-
unsigned long old_len = vrm->old_len;
1325-
unsigned long new_len = vrm->new_len;
1326-
unsigned long flags = vrm->flags;
13271386

13281387
err = resize_is_valid(vrm);
13291388
if (err)
@@ -1356,16 +1415,15 @@ static unsigned long expand_vma(struct vma_remap_struct *vrm)
13561415
*/
13571416

13581417
/* We're not allowed to move the VMA, so error out. */
1359-
if (!(flags & MREMAP_MAYMOVE))
1418+
if (!(vrm->flags & MREMAP_MAYMOVE))
13601419
return -ENOMEM;
13611420

13621421
/* Find a new location to move the VMA to. */
13631422
err = vrm_set_new_addr(vrm);
13641423
if (err)
13651424
return err;
13661425

1367-
return move_vma(vma, addr, old_len, new_len, vrm->new_addr,
1368-
&vrm->mlocked, flags, vrm->uf, vrm->uf_unmap);
1426+
return move_vma(vrm);
13691427
}
13701428

13711429
/*

0 commit comments

Comments
 (0)