mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Nickolai Zeldovich <nickolai@csail.mit.edu>
To: Andrew Morton <akpm@linux-foundation.org>,
	Lorenzo Stoakes <ljs@kernel.org>
Cc: "Liam R. Howlett" <liam@infradead.org>,
	Vlastimil Babka <vbabka@kernel.org>, Jann Horn <jannh@google.com>,
	Pedro Falcato <pfalcato@suse.de>,
	linux-mm@kvack.org, linux-kernel@vger.kernel.org,
	Nickolai Zeldovich <nickolai@csail.mit.edu>
Subject: [PATCH v1] mm/mremap: do not charge a shrinking move twice
Date: Sat, 17 Oct 2026 19:35:26 -0400	[thread overview]
Message-ID: <20261017233527.797191-1-nickolai@csail.mit.edu> (raw)

Since v6.15 every mremap() that shrinks an accounted mapping and moves
it with MREMAP_MAYMOVE | MREMAP_FIXED leaves old_len - new_len bytes
charged to vm_committed_as for good.  An unprivileged process that
repeats such a call keeps raising Committed_AS in /proc/meminfo; with
vm.overcommit_memory=2 it reaches CommitLimit, after which every
accounted allocation in the system (private writable mmap(), brk(),
fork()) fails with ENOMEM until reboot.  A loop of 1000 calls moving a
2 MiB private anonymous mapping to a 1 MiB fixed destination raises
Committed_AS by 1 GiB.

mremap_to() first unmaps the tail of the source with shrink_vma(),
which uncharges it, and then sets old_len to new_len so that the move
looks like a same-size one.  vrm->delta, the absolute difference of the
two lengths computed in do_mremap(), is not updated, and
vrm_calc_charge() charges delta pages for the move.  For a move that
does not unmap the source the charge only has to cover the growth from
old_len to new_len, which here is zero; nothing ever releases the extra
charge.  Before the vma_remap_struct conversion move_vma() charged
new_len - old_len with the updated old_len.

Recompute delta after the shrink.

Tested with the program in the notes under QEMU (x86_64 defconfig):
Committed_AS grows by about 1 GiB per run without this patch and stays
flat with it.

Fixes: b714ccb02a76 ("mm/mremap: complete refactor of move_vma()")
Assisted-by: LLM
Signed-off-by: Nickolai Zeldovich <nickolai@csail.mit.edu>
---

Notes:
    Reproducer (any accounted private mapping; watch Committed_AS):
    
    #define _GNU_SOURCE
    #include <stdio.h>
    #include <sys/mman.h>
    
    static long committed_kb(void)
    {
    	char line[128];
    	long kb = -1;
    	FILE *f = fopen("/proc/meminfo", "r");
    
    	while (f && fgets(line, sizeof(line), f))
    		if (sscanf(line, "Committed_AS: %ld kB", &kb) == 1)
    			break;
    	if (f)
    		fclose(f);
    	return kb;
    }
    
    int main(void)
    {
    	const size_t old_len = 2 << 20, new_len = 1 << 20;
    	long before = committed_kb();
    
    	for (int i = 0; i < 1000; i++) {
    		char *src = mmap(NULL, old_len, PROT_READ | PROT_WRITE,
    				 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    		char *dst = mmap(NULL, new_len, PROT_NONE,
    				 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    
    		if (mremap(src, old_len, new_len, MREMAP_MAYMOVE | MREMAP_FIXED,
    			   dst) != dst)
    			return perror("mremap"), 1;
    		munmap(dst, new_len);
    	}
    	printf("leaked %ld kB\n", committed_kb() - before);
    	return 0;
    }
    
    $ gcc -O2 -o mremap-shrink-charge mremap-shrink-charge.c && ./mremap-shrink-charge
    before: leaked 1024000 kB   (every run; Committed_AS 3744 -> 1027744 -> 2051744 kB)
    after:  leaked 0 kB

 mm/mremap.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/mm/mremap.c b/mm/mremap.c
index 444f37d7cd28..2dd41c611fdd 100644
--- a/mm/mremap.c
+++ b/mm/mremap.c
@@ -1523,6 +1523,7 @@ static unsigned long mremap_to(struct vma_remap_struct *vrm)
 
 		/* Set up for the move now shrink has been executed. */
 		vrm->old_len = vrm->new_len;
+		vrm_set_delta(vrm);
 	}
 
 	/* MREMAP_DONTUNMAP expands by old_len since old_len == new_len */

base-commit: 70dd4ee72eb2d5793c62e71c14e3aba78e5a4e56
-- 
2.53.0


             reply	other threads:[~2026-10-17 23:35 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-17 23:35 Nickolai Zeldovich [this message]
2026-10-18  1:04 ` Andrew Morton
2026-10-18  1:50   ` [PATCH v2] " Nickolai Zeldovich

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261017233527.797191-1-nickolai@csail.mit.edu \
    --to=nickolai@csail.mit.edu \
    --cc=akpm@linux-foundation.org \
    --cc=jannh@google.com \
    --cc=liam@infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=ljs@kernel.org \
    --cc=pfalcato@suse.de \
    --cc=vbabka@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®