From: Qiliang Yuan <qiliang.yuan@linux.dev>
To: "Matthew Wilcox (Oracle)" <willy@infradead.org>,
Jan Kara <jack@suse.cz>,
Andrew Morton <akpm@linux-foundation.org>,
Vishal Moola <vishal.moola@gmail.com>,
fujunjie <fujunjie1@qq.com>
Cc: Roman Gushchin <roman.gushchin@linux.dev>,
Usama Arif <usama.arif@linux.dev>,
linux-fsdevel@vger.kernel.org, linux-mm@kvack.org,
linux-kernel@vger.kernel.org,
Qiliang Yuan <qiliang.yuan@linux.dev>,
stable@vger.kernel.org
Subject: [PATCH v2 0/3] mm/filemap: read ahead for mmap readers that move backwards
Date: Mon, 09 Nov 2026 05:34:10 -0500 [thread overview]
Message-ID: <20261109-bug-mm-mmap-readahead-descending-v2-0-abac52890a9c@linux.dev> (raw)
A reader that walks a file mapping towards lower offsets gets almost no
readahead from mmap faults when the mapping asks for it. glibc's
memmove() and memcpy() copy backwards when the destination lies above
the source, so this happens to loaders that copy large files out of a
mapping, such as LLM inference engines copying model weights out of a
safetensors mapping. Both MADV_HUGEPAGE and MADV_SEQUENTIAL read ahead
only towards higher offsets, so each fault of such a reader reads just
the part it is missing.
MADV_SEQUENTIAL was slow for this before 7.2 as well. MADV_HUGEPAGE
became much slower in 7.2: since commit 0b9c0aeba938 ("mm/filemap:
count only the faulting address as a mmap hit"), the PTEs mapped around
a fault no longer count as hits and PMD mappings never did, so a
backward reader's mmap_miss passes MMAP_LOTSAMISS after about a hundred
folios and readahead stops. Each page after that is read on its own.
Patch 1 moves the mmap hit check of filemap_map_pages() into helpers.
Patch 2 fixes the regression: on a descending VM_HUGEPAGE fault it also
reads the folio below, and it counts a PMD mapping of the faulting
address as a hit, as a PTE mapping is. Patch 3 reads the window that
ends at the fault on a descending VM_SEQ_READ fault.
Reading one byte from each page of a 2 GiB file, last page first, with
a cold page cache, read_ahead_kb 8192, in a 32 vCPU VM:
v7.4-rc2 patched
MADV_HUGEPAGE 42.23 s 0.58 s
MADV_SEQUENTIAL 52.42 s 0.96 s
The same walk took 0.53 s with MADV_HUGEPAGE just before
0b9c0aeba938.
Forward walks take the same time, and a fault on an uncached 2 MiB hole
between two cached ranges of a MADV_HUGEPAGE mapping is still mapped
with a PMD, 300 out of 300 times with and without the series. Megabytes
read and major faults for other access patterns over a 16 GiB file:
v7.4-rc2 patched
1% random, MADV_NORMAL 1002 MB, 39617 1013 MB, 39617
1% random, MADV_HUGEPAGE 1584 MB, 38760 1587 MB, 38756
1% stride 64 pages 10503 MB, 2 10496 MB, 2
5% random in 2 GiB memcg,
MADV_HUGEPAGE 2205 MB, 187416 2225 MB, 187402
The walk above, run as "revwalk FILE" for MADV_HUGEPAGE and "revwalk FILE
seq" for MADV_SEQUENTIAL, is below. I can turn it into a selftest if that
is wanted.
/* revwalk FILE [seq]: read each page of FILE, last page first */
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <unistd.h>
int main(int argc, char **argv)
{
long ps = sysconf(_SC_PAGESIZE);
int fd = open(argv[1], O_RDONLY);
struct rusage ru;
struct stat st;
size_t i;
char *p;
fstat(fd, &st);
posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED);
p = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
madvise(p, st.st_size, argc > 2 && !strcmp(argv[2], "seq") ?
MADV_SEQUENTIAL : MADV_HUGEPAGE);
for (i = st.st_size / ps; i > 0; i--)
(void)*(volatile char *)(p + (i - 1) * ps);
getrusage(RUSAGE_SELF, &ru);
printf("major faults: %ld\n", ru.ru_majflt);
return 0;
}
Signed-off-by: Qiliang Yuan <qiliang.yuan@linux.dev>
---
V1 -> V2:
- Split the mmap hit helpers out of the fix into patch 1
- Patch 2: take a fault as descending only when the folio below it is
not cached either, so a hole between two cached ranges is still read
as one large folio (Sashiko review)
- Rebase onto v7.4-rc2 and remeasure
- Send from my linux.dev address
v1: https://lore.kernel.org/r/20261022-bug-mm-mmap-readahead-descending-v1-0-6d08b34862f2@gmail.com
---
Qiliang Yuan (3):
mm/filemap: move the mmap hit check of filemap_map_pages() into helpers
mm/filemap: read ahead downwards on descending VM_HUGEPAGE faults
mm/filemap: extend VM_SEQ_READ readahead below descending faults
mm/filemap.c | 119 ++++++++++++++++++++++++++++++++++++++++++++---------------
1 file changed, 89 insertions(+), 30 deletions(-)
---
base-commit: 7475ea8b58616b33c04afb976cf0fbaad9dd63fb
change-id: 20261022-bug-mm-mmap-readahead-descending-9cab86703bbb
Best regards,
--
Qiliang Yuan <qiliang.yuan@linux.dev>
next reply other threads:[~2026-11-09 10:34 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-09 10:34 Qiliang Yuan [this message]
2026-11-09 10:34 ` [PATCH v2 1/3] mm/filemap: move the mmap hit check of filemap_map_pages() into helpers Qiliang Yuan
2026-11-09 10:34 ` [PATCH v2 2/3] mm/filemap: read ahead downwards on descending VM_HUGEPAGE faults Qiliang Yuan
2026-11-09 10:34 ` [PATCH v2 3/3] mm/filemap: extend VM_SEQ_READ readahead below descending faults Qiliang Yuan
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=20261109-bug-mm-mmap-readahead-descending-v2-0-abac52890a9c@linux.dev \
--to=qiliang.yuan@linux.dev \
--cc=akpm@linux-foundation.org \
--cc=fujunjie1@qq.com \
--cc=jack@suse.cz \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=roman.gushchin@linux.dev \
--cc=stable@vger.kernel.org \
--cc=usama.arif@linux.dev \
--cc=vishal.moola@gmail.com \
--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®