mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Seunghyun An <san55@wisc.edu>
To: linux-mm@kvack.org, Andrew Morton <akpm@linux-foundation.org>
Cc: Lorenzo Stoakes <ljs@kernel.org>,
	David Hildenbrand <david@kernel.org>,
	Vlastimil Babka <vbabka@kernel.org>,
	"Liam R . Howlett" <liam@infradead.org>,
	Jann Horn <jannh@google.com>, Pedro Falcato <pfalcato@suse.de>,
	Matthew Wilcox <willy@infradead.org>, Jan Kara <jack@suse.cz>,
	Dave Hansen <dave.hansen@linux.intel.com>,
	Suren Baghdasaryan <surenb@google.com>,
	linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org
Subject: [PATCH 0/2] mm: avoid redundant reads of the vm_refcnt cacheline in anonymous faults
Date: Mon,  9 Nov 2026 18:00:00 -0600	[thread overview]
Message-ID: <20261110000002.1834454-1-san55@wisc.edu> (raw)

On the per-VMA-lock fault path, each anonymous fault increments
vma->vm_refcnt in vma_start_read() and decrements it in vma_end_read().
When threads fault on the same VMA, that cacheline moves between CPUs
anyway. On current mainline, extra reads of the same line
happen in between:

 1. __folio_set_anon() reads __vm_anon_pgoff_hi (offset 132, next to
    vm_refcnt) via linear_anon_page_index(), since the anon pgoff series.
 2. __vmf_anon_prepare() -> vma_has_anon_rmap() runs vma_is_attached() and
    vma_assert_stabilised(), which load vm_refcnt (and the mmap_lock
    count) even with CONFIG_DEBUG_VM=n, on every anonymous fault that
    allocates a page or breaks CoW. This came with v7.4-rc1.

These reads can miss, and they also make the following inc/dec more
expensive. Patch 1 computes the anon index from vm_pgoff for anonymous
VMAs. Patch 2 makes the assertion in vma_has_anon_rmap() DEBUG_VM-only.

AMD EPYC 9634 (84 cores, 12 CCDs), base is mainline d48723711bae (after
v7.4-rc1), CONFIG_DEBUG_VM=n. Threads faulting on one shared anonymous
VMA (each its own slice, zapped with MADV_DONTNEED), faults/s, median of
3, all three kernels measured on the same day:

  CPUs                      base      +patch 1  +patch 2   gain
  1 CCD,   7 threads        2.360M    2.369M    2.425M     +2.8%
  7 CCDs,  1 thread each    1.738M    1.982M    2.215M    +27.4%
  12 CCDs, 1 thread each    2.288M    2.548M    2.949M    +28.9%

With one VMA per thread, or with processes, both patches together change
throughput by less than 2%. In every cell of this and the next table the
spread between runs (min to max) is about 5% or less.

pft (https://github.com/gormanm/pft), which was mmtests'
config-workload-pft-threads until it was dropped there in favour of the
will-it-scale malloc/brk tests, does the same thing without the zap: one
anonymous region split between the threads. Same machine and kernels,
faults per wall-clock second (pft's fault/wsec), median of 10 (of 5 for
the last row), "pft -a -m <4*t>G -t <t>" (pft needs a one-line fix to
accept a CPU mask that does not start at CPU 0):

  CPUs                      base      +patch 1  +patch 2   gain
  1 CCD,   7 threads        2.938M    2.955M    3.044M     +3.6%
  7 CCDs,  1 thread each    1.938M    2.215M    2.704M    +39.5%
  12 CCDs, 1 thread each    2.456M    2.673M    3.547M    +44.4%
  12 CCDs, 7 threads each   4.132M    4.863M    5.196M    +25.8%

With processes ("-n 7", 7 CCDs) the change is +0.7%. With all 84 cores,
threaded pft reached 4.132M against 5.243M for the same run with
processes (-n 84), 21% less; with both patches it is 5.196M against
5.282M, 1.6% less.

None of the will-it-scale tests mmtests runs now (page_fault*, malloc*,
brk*) has many threads faulting one anonymous VMA. page_fault1 threads
only shares a VMA when neighbouring mappings merge, and it is then
dominated by munmap write-locking that VMA (the threads were about 45%
idle here), so it does not resolve this difference (it also varies by up
to 10% between boots of the same kernel). malloc* and brk* take almost no
page faults.

A real-world instance of the pattern: OpenJDK with -XX:+AlwaysPreTouch
and G1 has the whole heap in one anonymous VMA and pretouches it with
ordinary write faults from all GC threads (108 here, not pinned). OpenJDK
25, -Xms32g -Xmx32g; time from exec to main() with -XX:+AlwaysPreTouch
minus the same without it, median of 10:

                    base      +patch 1  +patch 2
  G1 pretouch       1866 ms   1653 ms   1658 ms   (-11.1%)
  Parallel pretouch 1872 ms   1644 ms   1658 ms   (-11.4%)

Patch 2 adds nothing there; with patch 1 the JVM already faults at about
5.1M faults/s, close to what this machine sustains for 4K anonymous
faults from all CCDs in the other tests. This is a one-time startup cost;
once the heap is populated there are no such faults.

Tested with CONFIG_DEBUG_VM=y and CONFIG_PROVE_LOCKING=y: the mm selftests
(run_vmtests.sh -t "mmap mremap vma_merge rmap cow userfaultfd madv_guard
ksm migration thp") pass with no warnings, and so does a test that
soft-offlines anonymous pages faulted after mremap(), fork() and on a
MAP_PRIVATE /dev/zero mapping. LOCKDEP=y with DEBUG_VM=n, and RISC-V nommu
with and without DEBUG_VM, build without warnings.

Seunghyun An (2):
  mm/rmap: use vm_pgoff for anonymous VMAs in linear_anon_page_index()
  mm/vma: only assert VMA stability in vma_has_anon_rmap() with
    CONFIG_DEBUG_VM

 include/linux/pagemap.h         | 19 ++++++++++++++-----
 mm/vma.h                        |  8 +++++++-
 tools/testing/vma/include/dup.h | 13 ++++++++-----
 3 files changed, 29 insertions(+), 11 deletions(-)


base-commit: d48723711bae9b8cdc56719c448ba42fef28cbee
-- 
2.53.0


             reply	other threads:[~2026-11-10  0:01 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-10  0:00 Seunghyun An [this message]
2026-11-10  0:00 ` [PATCH 1/2] mm/rmap: use vm_pgoff for anonymous VMAs in linear_anon_page_index() Seunghyun An
2026-11-10  0:00 ` [PATCH 2/2] mm/vma: only assert VMA stability in vma_has_anon_rmap() with CONFIG_DEBUG_VM Seunghyun An

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=20261110000002.1834454-1-san55@wisc.edu \
    --to=san55@wisc.edu \
    --cc=akpm@linux-foundation.org \
    --cc=dave.hansen@linux.intel.com \
    --cc=david@kernel.org \
    --cc=jack@suse.cz \
    --cc=jannh@google.com \
    --cc=liam@infradead.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=ljs@kernel.org \
    --cc=pfalcato@suse.de \
    --cc=surenb@google.com \
    --cc=vbabka@kernel.org \
    --cc=willy@infradead.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®