All of lore.kernel.org
 help / color / mirror / Atom feed
* [BUG/RFC] mm/madvise: MADV_WILLNEED skips swapped file-backed COW pages
@ 2026-08-31 18:24 Mike Kaplinskiy
  2026-09-03  7:05 ` zhaozhengzhuo
  2026-09-03 19:03 ` [BUG/RFC] mm/madvise: MADV_WILLNEED skips swapped file-backed COW pages Lorenzo Stoakes (ARM)
  0 siblings, 2 replies; 7+ messages in thread
From: Mike Kaplinskiy @ 2026-08-31 18:24 UTC (permalink / raw)
  To: linux-mm; +Cc: linux-kernel, akpm, liam, ljs, david, vbabka, jannh

[-- Attachment #1: Type: text/plain, Size: 874 bytes --]

Hi,

I'm seeing a strange behavior when using MADV_WILLNEED to schedule
swap page-in. It seems MADV_WILLNEED does not schedule swap reads for
swapped-out COW pages in an ordinary file-backed MAP_PRIVATE mapping.

I reproduced this on Linux 7.0.14 on aarch64, but I think this code
hasn't changed in a while. mm/madvise.c:madvise_willneed walks swap
PTEs only when vma->vm_file is NULL, and sends other file-backed
mappings to vfs_fadvise(POSIX_FADV_WILLNEED). This skips the case of
MAP_PRIVATE mappings with changes, which frequently happens for
libraries/binaries with relocations and/or writable globals.

The code is at the top of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/mm/madvise.c#n282
.

Minimal reproducer attached. Would there be interest in changing this
path to prefetch private copies in addition to the readahead?

Thanks,
Mike

[-- Attachment #2: madvise-willneed-cow-repro.c --]
[-- Type: text/x-csrc, Size: 1766 bytes --]

#define _GNU_SOURCE
#include <assert.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>

#define SIZE (64UL << 20)

static size_t swap_kib(void *mapping)
{
	FILE *f = fopen("/proc/self/smaps", "r");
	char line[256], perms[5];
	unsigned long start, end, value = 0;
	int ours = 0;

	assert(f);
	while (fgets(line, sizeof(line), f)) {
		if (sscanf(line, "%lx-%lx %4s", &start, &end, perms) == 3)
			ours = start == (unsigned long)mapping;
		else if (ours && sscanf(line, "Swap: %lu kB", &value) == 1)
			break;
	}
	fclose(f);
	return value;
}

static unsigned long pswpin(void)
{
	FILE *f = fopen("/proc/vmstat", "r");
	char name[32];
	unsigned long value = 0;

	assert(f);
	while (fscanf(f, "%31s %lu", name, &value) == 2)
		if (!strcmp(name, "pswpin"))
			break;
	fclose(f);
	return value;
}

int main(void)
{
	char path[] = "/tmp/madvise-cow-XXXXXX";
	int fd = mkstemp(path);
	char *p;
	unsigned long before;
	volatile unsigned long sum = 0;

	assert(fd >= 0 && !ftruncate(fd, SIZE));
	p = mmap(NULL, SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
	assert(p != MAP_FAILED);
	unlink(path);

	for (size_t i = 0; i < SIZE; i += getpagesize())
		p[i] = i / getpagesize() + 1; /* create private COW pages */
	assert(!madvise(p, SIZE, MADV_PAGEOUT));
	usleep(500000);
	printf("after PAGEOUT:  Swap=%zu kB\n", swap_kib(p));

	before = pswpin();
	assert(!madvise(p, SIZE, MADV_WILLNEED));
	sleep(2); /* WILLNEED is asynchronous */
	printf("after WILLNEED: Swap=%zu kB, pswpin=+%lu\n",
	       swap_kib(p), pswpin() - before);

	before = pswpin();
	for (size_t i = 0; i < SIZE; i += getpagesize())
		sum += p[i];
	printf("after touch:    pswpin=+%lu (sum=%lu)\n",
	       pswpin() - before, sum);
}

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-09-05 11:39 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-31 18:24 [BUG/RFC] mm/madvise: MADV_WILLNEED skips swapped file-backed COW pages Mike Kaplinskiy
2026-09-03  7:05 ` zhaozhengzhuo
2026-09-03 17:03   ` Mike Kaplinskiy
2026-09-04 10:09     ` [RFC PATCH v1] mm/madvise: prefetch private file COW swap entries zhaozhengzhuo
2026-09-04 14:52       ` Lorenzo Stoakes (ARM)
2026-09-05 11:37         ` zhaozhengzhuo
2026-09-03 19:03 ` [BUG/RFC] mm/madvise: MADV_WILLNEED skips swapped file-backed COW pages Lorenzo Stoakes (ARM)

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.