From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail.openvz.org (mail.openvz.org [69.168.225.77]) by lore.virtuozzo.com (Postfix) with ESMTPS id 5542280474 for ; Wed, 28 Oct 2026 14:19:19 +0000 (UTC) Received: from mail.openvz.org (localhost [127.0.0.1]) by mail.openvz.org (8.14.4/8.14.4) with ESMTP id 69SEI4hw020629; Wed, 28 Oct 2026 17:18:05 +0300 DKIM-Filter: OpenDKIM Filter v2.11.0 mail.openvz.org 69SEI4hw020629 Authentication-Results: mail.openvz.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=virtuozzo.com header.i=@virtuozzo.com header.b="tSvX3xvO" Received: from mail-wr1-f72.google.com (mail-wr1-f72.google.com [209.85.221.72]) by mail.openvz.org (8.14.4/8.14.4) with ESMTP id 69SEI3F3020617 (version=TLSv1/SSLv3 cipher=AES128-GCM-SHA256 bits=128 verify=FAIL) for ; Wed, 28 Oct 2026 17:18:03 +0300 DKIM-Filter: OpenDKIM Filter v2.11.0 mail.openvz.org 69SEI3F3020617 Received: by mail-wr1-f72.google.com with SMTP id ffacd0b85a97d-48dfdc0f491so605955f8f.1 for ; Wed, 28 Oct 2026 07:18:03 -0700 (PDT) X-Forwarded-Encrypted: i=2; AKwUvBy1C8Wq8cBqhUpta/U2DcgMf5Ss9z3sLuOkRWmwaI8gaEAqNFZoaXFmN9jh0kMoFTOiNPFe5w==@openvz.org X-Gm-Message-State: AFq9FYLv5KLE8i6pNDIEM2qXho1WsFO5NrbwsrChfaMXRwlUCujxL8v2 fCVW3YdYqtiB6ls26xZZWvuvWKm6GZgQ8AO9B/vTTpAcsa8bhRRRJSoJy7a1113zqMSFEiSHR0y fsOMXc+ZEet6NBbx2aIsao7lGavMJo7Xy605fDETVoqoW2B39UxdYng== X-Gm-Gg: AYBFou21Ao9YUUbmS/blSACPTLh1Xi+79LX1PBgNLLdHVgSMzY9MXvWzcLoKKS5W/Wz ALC443DRdBI5IvtI0JxKMtIKDM8Usgi450j5QffdtuyBM9JscYVAz9vrjLDoM2ehGMi3K4A5nmb 0oGIs5UN5hAWZm+xTFzVoadmxXn88YUG0st2K7fAThjl2NogyDkYR1A8nh6ys3o2Qbjd0I8zpMh /zLWmvyZwrv0Egbdjd8BsOyXwFEkmr7DTAg9l3A9u8uuau3qA9i3HGfx2A9egLBid5H43rD0S/G 6/oGKA1yy39aprF8N1kOcmfHzXGfDcB1SL5Xj93Ct/icRw9Kgsu0KOr2UDaRYBc/coXVlQZ7OYf xap6prWqTe4IOpRioIg== X-Received: by 2002:a05:600c:3e0a:b0:4a0:1c0e:b057 with SMTP id 5b1f17b1804b1-4a9640f1fbdmr57152205e9.4.1793197083418; Wed, 28 Oct 2026 07:18:03 -0700 (PDT) X-Received: by 2002:a05:600c:3e0a:b0:4a0:1c0e:b057 with SMTP id 5b1f17b1804b1-4a9640f1fbdmr57151285e9.4.1793197082795; Wed, 28 Oct 2026 07:18:02 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1793197082; cv=none; d=google.com; s=arc-20260327; b=IbxT7gGIcjysFsHkQ62Wm3ox6rxhns8sgjKaCYLQsiLcIMk3tuqvSQ3CWgmvv7YdSz 8CGkb+7gGQ0iU03cxHXWKcJ8BrOkMUdHZdtUb2nSX9XbJbiZhxTRGCoYy5EiPIBV4XZC PfqGu387mM76uxm+Di/XCYf0CGIxVlYvbXeqDzkWowKa1CPNiV4IH7j4swMT4tk8doKW 5narZvdFDeiGEtBBGcP/K2o/dyrk6i2IzqPmmowbHlbSiYIpz9wCVtq03w594Kx78iAF exksaQu+CL7yhX2Izl7CyCNoELiyAEgRxxtjOUPwUduU6bujuWOyMGl6Hkfd5KJXVQ1Z VGSg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20260327; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:dkim-signature; bh=UFhKOnm9XET/2LvFv8F+r+Sk4EhveGpZRVsXDA7tBX4=; fh=XcqC0Cib/C4XsQyLUc3b6E9Ii37Wk0LS3yBNHj5szHI=; b=nMWRKEhmvzSPEe1+eeBzFRTaaO8BaNblKcCcd50J3DHwCv+dBTuzycIqA/6x3NW1PF +Ff6Zpcb3EpD5JMsoq1DLS1B5uRePz3VNhfuRtgxbVYM2zj0YoZ8Yz5H3dFOBy2yXeZF ajgB803ddhGn792ogkTQmAc3bMTWTGNE29NSo/AUp56Q1TLBbNgsc0SnE6agQm1rBmNs 37A9nI6rKz2CY6D3Q/1IB4xmwUmgijxZceCxDlRfj0lnldrL6DvmuY8oL4LOhAPAFVu+ pOXd91hPkxlCm98zS6KnJPKev/Ba5FPPA/dJazi50KHuOhXHl8zjjWzs/gERr+WMtmzC 5YEg==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@virtuozzo.com header.s=relay header.b=tSvX3xvO; spf=pass (google.com: domain of khorenko@virtuozzo.com designates 130.117.225.111 as permitted sender) smtp.mailfrom=khorenko@virtuozzo.com; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=virtuozzo.com Received: from relay.virtuozzo.com (relay.virtuozzo.com. [130.117.225.111]) by mx.google.com with ESMTPS id ffacd0b85a97d-48e3ed174a9si25912476f8f.380.2026.10.28.07.18.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 28 Oct 2026 07:18:02 -0700 (PDT) Received-SPF: pass (google.com: domain of khorenko@virtuozzo.com designates 130.117.225.111 as permitted sender) client-ip=130.117.225.111; Authentication-Results: mx.google.com; dkim=pass header.i=@virtuozzo.com header.s=relay header.b=tSvX3xvO; spf=pass (google.com: domain of khorenko@virtuozzo.com designates 130.117.225.111 as permitted sender) smtp.mailfrom=khorenko@virtuozzo.com; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=virtuozzo.com DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=virtuozzo.com; s=relay; h=Message-Id:Content-Type:MIME-Version:Subject:Date :From; bh=UFhKOnm9XET/2LvFv8F+r+Sk4EhveGpZRVsXDA7tBX4=; b=tSvX3xvOfGGBb4M3YFJ EGV3WJP8jVQal3xo7xax2n7Iu5adLFcd1WOt4+dwDRwcxzWaSvdxlCX0PJh4gtF7PbA9NGF/dpuld 54rYFsgGeIopVNFWAN2VJvsESr4GsdTT6brzHss9dS0mMdXiVml7qrhi5+6CGwMcANwAHWJhJ8lfh C64jzDdDDUvPNR7+F+kME5QSZetzf1fdcmmwcV47AT0K2yM3+pMOIwPViZAKIhJ4Bh6sq6+PSXyaA ESBeoEuobFKGEh3jMS3J9PkkAgtDCaIzG2jUep89NwA6GZmmATY1ysZyRU7Z0ujGQTvqlTKWlyMEL PV9FVKIngoS2rvw==; Received: from ch-demo-asa.virtuozzo.com ([130.117.225.8] helo=f0.sw.ru) by relay.virtuozzo.com with esmtp (Exim 4.96) (envelope-from ) id 1xM4Po-009nYa-0N; Wed, 28 Oct 2026 15:18:01 +0100 From: Konstantin Khorenko Date: Wed, 28 Oct 2026 15:17:59 +0100 MIME-Version: 1.0 Message-Id: <20261028-b4-prep-khorenko-vstor-151607-numa-migrate-order-v2-2-b973a82a5b0c@virtuozzo.com> References: <20261028-b4-prep-khorenko-vstor-151607-numa-migrate-order-v2-0-b973a82a5b0c@virtuozzo.com> In-Reply-To: <20261028-b4-prep-khorenko-vstor-151607-numa-migrate-order-v2-0-b973a82a5b0c@virtuozzo.com> To: Pavel Tikhomirov X-Mailer: b4 0.16.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=13727; i=khorenko@virtuozzo.com; h=from:subject:message-id; bh=7nBgZCgSk2B+ETq2VuVwHJzFKhetuucwWQBO34nC7bw=; b=owEBbQKS/ZANAwAKAVGWCkf5YVwqAcsmYgBq4gQZwIggUZc/yzS0jyzua+ymOg6i/OLVTVgBX oy7rSkfEm6JAjMEAAEKAB0WIQRWD23IPcXT0GO9CCxRlgpH+WFcKgUCauIEGQAKCRBRlgpH+WFc Kq3UEACSA/OqgDUpbPEmQGsCX7pIFBlQ9diNl1MzqFWlw8Jq7ax2nPFqfBo8V+kHSI21AAT2btF f7CDTY/nSfErbZOFsAvPXbM6nh5QaNeMgFeL910hGfNPAarwL58mpwaZu4X64qAMNvhsbTDYTw9 KrVK9AK6nkPqi7yxmfL+9xRgI59a9WYrCiUyw1iJdViWBTCxFetUsHVkUL9FX7FKebo8J7yzh4r mhaYknLQtsgvRzVoWhLRYbgp2fHaYbesafQnEJvcjHKHTxMudzKJnenUgaQA13RcvCBcW9el3VC N1472IZJydSSlKsovxE5skdWt72xgXN4Lw4fTNdktawERH/fWzppokJ57rMnArfMGVuhQW/F0zj woE5MpyvlJXXw8H/Iu+QigMdifHw/rUBlowpRcNbMEhLRtX99/XMfRLugpMskUV/JW0zgyrTUce BvwG1vhwXrh+U4BZuOcK0bpoFHxhGZOUXj1rw2iezQEPcu3dSsnfwvhiSqsW8KL5KuNeUyqG2dO Eykp9gTzwkVzb3oeS263e3uCwGydv3hlxD6nxuKy/IdkOqq2nWTV8OSdoXdh69/70AiP9Kmgulr bu8T0/Er28X5wVAQavej//WkpxCIX0skHQn8l81iUjV2vJiEemcwmeHlf5lS4q4kIgxj6EfrX34 brI71ZTePOUzgeg== X-Developer-Key: i=khorenko@virtuozzo.com; a=openpgp; fpr=560F6DC83DC5D3D063BD082C51960A47F9615C2A X-OZ-Fwd: true Cc: devel@openvz.org Subject: [Devel] [PATCH hci-8.1 v2 2/2] selftests/ve: check memory.numa_migrate keeps the folio order X-BeenThere: devel@openvz.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: OpenVZ development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: devel-bounces@openvz.org Errors-To: devel-bounces@openvz.org memcg_numa_migrate_new_folio() used to allocate a PMD-sized destination for every large folio. A page cache folio of a smaller order then ended up with a 512-page destination carrying its index and reference count. Only the source pages were copied into it, so a read of the folios that followed in the same 2 MB range was served from the uninitialised part of the destination, and the inode died in clear_inode() on eviction. The test writes a file on an XFS image, where 64K writes produce large folios, from a cgroup bound to one NUMA node, migrates the cgroup to a second node, reads the file back against the written pattern and checks through memory.numa_stat that something was migrated. The stat read is retried for a while, as memcg flushes its per-cpu counters lazily. On an unfixed kernel the comparison fails; the test then leaves the image mounted, because unlinking or unmounting would evict the inode and crash the node. The folio sizes are not taken on faith: before the migration the test maps the file and reads the compound flags of its pages through /proc/self/pagemap and /proc/kpageflags. A file without a large folio fails the test with a message, because it would pass without touching the bug. It needs root, two NUMA nodes with memory (numa=fake=2 is enough), cgroup2 with the memory and cpuset controllers, and mkfs.xfs, and skips otherwise. https://virtuozzo.atlassian.net/browse/VSTOR-151607 Feature: ve selftests Reviewed-by: Pavel Tikhomirov Reviewed-by: Vasileios Almpanis Signed-off-by: Konstantin Khorenko --- tools/testing/selftests/ve/Makefile | 1 + tools/testing/selftests/ve/ve_numa_migrate_test.c | 384 ++++++++++++++++++++++ 2 files changed, 385 insertions(+) diff --git a/tools/testing/selftests/ve/Makefile b/tools/testing/selftests/ve/Makefile index 51f9316beb99b..e96320a183844 100644 --- a/tools/testing/selftests/ve/Makefile +++ b/tools/testing/selftests/ve/Makefile @@ -6,6 +6,7 @@ TEST_GEN_PROGS += ve_ns_owner_test TEST_GEN_PROGS += ve_perms_test TEST_GEN_PROGS += ve_mount_accounting_test TEST_GEN_PROGS += ve_vtty_stress +TEST_GEN_PROGS += ve_numa_migrate_test # CPUID faulting is x86-64 only. ARCH says "x86" for 32-bit too and lib.mk # picks the clang target by its emptiness, so decide by the compiler. diff --git a/tools/testing/selftests/ve/ve_numa_migrate_test.c b/tools/testing/selftests/ve/ve_numa_migrate_test.c new file mode 100644 index 0000000000000..b4a60dba8063d --- /dev/null +++ b/tools/testing/selftests/ve/ve_numa_migrate_test.c @@ -0,0 +1,384 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * memory.numa_migrate must give a large page cache folio a destination of + * the same order. An oversized destination inherits the index and the + * reference count of the source, only the source pages are copied into it, + * reads of the neighbouring folios land in its uninitialised part, and the + * inode dies in clear_inode() on eviction. + * + * Write a file on an XFS image (XFS allocates large folios for 64K writes) + * from a cgroup bound to one NUMA node, migrate the cgroup to another node + * and check that the data reads back intact. + * + * Needs root, two NUMA nodes with memory (numa=fake=2 will do), cgroup2 + * with the memory and cpuset controllers, and mkfs.xfs. The folio sizes + * are read through /proc/self/pagemap and /proc/kpageflags before the + * migration: a file without large folios fails the test, as it would + * pass without touching the bug. + */ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../kselftest_harness.h" +#include "ve_selftest.h" + +#define CG_NAME "ve_numa_migrate_test" +#define DIR_TEMPLATE "/var/tmp/ve_numa_migrate.XXXXXX" +/* mkfs.xfs refuses anything smaller; the image is sparse. */ +#define IMG_SIZE (320UL << 20) +#define FILE_SIZE (4UL << 20) +#define CHUNK_SIZE (64UL << 10) +#define PAGE_SIZE 4096UL +#define PM_PFN_MASK ((1ULL << 55) - 1) +#define PM_PRESENT (1ULL << 63) +/* The periodic memcg stats flush runs every two seconds. */ +#define STAT_RETRIES 50 +#define STAT_RETRY_US 100000 + +FIXTURE(numa_migrate) +{ + int cgv2_fd; + int cg_fd; + int node_from; + int node_to; + char dir[64]; + char img[80]; + char mnt[80]; + int mounted; + int leave_mounted; + int data_fd; +}; + +/* First two node ids of a sysfs node list such as "0-1" or "0,2-3". */ +static int two_nodes_with_memory(int *from, int *to) +{ + char buf[256], *tok, *save; + int found = 0; + + if (read_file_at(AT_FDCWD, "/sys/devices/system/node/has_memory", + buf, sizeof(buf)) <= 0) + return -1; + + for (tok = strtok_r(buf, ",\n", &save); tok && found < 2; + tok = strtok_r(NULL, ",\n", &save)) { + int a, b, n; + + if (sscanf(tok, "%d-%d", &a, &b) != 2) { + if (sscanf(tok, "%d", &a) != 1) + return -1; + b = a; + } + for (n = a; n <= b && found < 2; n++) { + if (found == 0) + *from = n; + else + *to = n; + found++; + } + } + return found == 2 ? 0 : -1; +} + +static int run_cmd(const char *fmt, ...) +{ + char cmd[256]; + va_list ap; + int status; + + va_start(ap, fmt); + vsnprintf(cmd, sizeof(cmd), fmt, ap); + va_end(ap); + + status = system(cmd); + if (status < 0 || !WIFEXITED(status)) + return -1; + return WEXITSTATUS(status); +} + +/* Bytes of file pages the cgroup has on @node, from memory.numa_stat. */ +static int file_bytes_on_node(int cg_fd, int node, unsigned long long *out) +{ + static char buf[32768]; + char key[16], *p; + + if (read_file_at(cg_fd, "memory.numa_stat", buf, sizeof(buf)) <= 0) + return -1; + p = strstr(buf, "\nfile "); + if (!p) + return -1; + snprintf(key, sizeof(key), " N%d=", node); + p = strstr(p, key); + if (!p) + return -1; + *out = strtoull(p + strlen(key), NULL, 10); + return 0; +} + +/* + * Pages of @fd that sit in large folios, found through /proc/self/pagemap + * and /proc/kpageflags. Returns -1 when the interfaces are not usable. + */ +static long large_folio_pages(int fd, unsigned long size, long *folios) +{ + unsigned long nr = size / PAGE_SIZE, i; + long large = 0, heads = 0; + int pagemap, kpageflags; + unsigned char *map; + uint64_t *pm; + + pagemap = open("/proc/self/pagemap", O_RDONLY); + kpageflags = open("/proc/kpageflags", O_RDONLY); + pm = malloc(nr * sizeof(*pm)); + map = mmap(NULL, size, PROT_READ, MAP_SHARED | MAP_POPULATE, fd, 0); + if (pagemap < 0 || kpageflags < 0 || !pm || map == MAP_FAILED) { + large = -1; + goto out; + } + if (pread(pagemap, pm, nr * sizeof(*pm), + (unsigned long)map / PAGE_SIZE * sizeof(*pm)) != + (ssize_t)(nr * sizeof(*pm))) { + large = -1; + goto out; + } + for (i = 0; i < nr; i++) { + uint64_t flags, pfn = pm[i] & PM_PFN_MASK; + + if (!(pm[i] & PM_PRESENT) || !pfn || + pread(kpageflags, &flags, sizeof(flags), + pfn * sizeof(flags)) != sizeof(flags)) { + large = -1; + goto out; + } + if (flags & (1ULL << KPF_COMPOUND_HEAD)) + heads++; + if (flags & ((1ULL << KPF_COMPOUND_HEAD) | + (1ULL << KPF_COMPOUND_TAIL))) + large++; + } + *folios = heads; +out: + if (map != MAP_FAILED) + munmap(map, size); + free(pm); + if (pagemap >= 0) + close(pagemap); + if (kpageflags >= 0) + close(kpageflags); + return large; +} + +static void cleanup(FIXTURE_DATA(numa_migrate) *self) +{ + char path[PATH_MAX]; + + if (self->data_fd >= 0) { + close(self->data_fd); + self->data_fd = -1; + } + if (self->dir[0]) { + snprintf(path, sizeof(path), "%s/data", self->mnt); + unlink(path); + if (self->mounted) + umount(self->mnt); + unlink(self->img); + rmdir(self->mnt); + rmdir(self->dir); + self->dir[0] = '\0'; + } + if (self->cg_fd >= 0) { + close(self->cg_fd); + self->cg_fd = -1; + } + if (self->cgv2_fd >= 0) { + /* The test task may still sit in the cgroup after a failure. */ + write_u64_at(self->cgv2_fd, "cgroup.procs", getpid()); + unlinkat(self->cgv2_fd, CG_NAME, AT_REMOVEDIR); + close(self->cgv2_fd); + self->cgv2_fd = -1; + } +} + +/* + * Returns 0, or 1 with everything undone when the host cannot run the test. + * The harness runs no teardown after a failed setup, so the undo is here. + */ +static int prepare(FIXTURE_DATA(numa_migrate) *self, const char **why) +{ + int fd; + + self->cgv2_fd = mount_cg2_fd(); + if (self->cgv2_fd < 0) { + *why = "cannot mount cgroup2"; + goto fail; + } + if (write_file_at(self->cgv2_fd, "cgroup.subtree_control", + "+memory +cpuset")) { + *why = "cannot enable the memory and cpuset controllers"; + goto fail; + } + if (mkdirat(self->cgv2_fd, CG_NAME, 0755) && errno != EEXIST) { + *why = "cannot create the test cgroup"; + goto fail; + } + self->cg_fd = openat(self->cgv2_fd, CG_NAME, O_PATH | O_DIRECTORY); + if (self->cg_fd < 0) { + *why = "cannot open the test cgroup"; + goto fail; + } + if (faccessat(self->cg_fd, "memory.numa_migrate", W_OK, 0)) { + *why = "no memory.numa_migrate"; + goto fail; + } + if (write_u64_at(self->cg_fd, "cpuset.mems", self->node_from)) { + *why = "cannot bind the cgroup to a node"; + goto fail; + } + + snprintf(self->dir, sizeof(self->dir), DIR_TEMPLATE); + if (!mkdtemp(self->dir)) { + self->dir[0] = '\0'; + *why = "cannot create a temporary directory"; + goto fail; + } + snprintf(self->img, sizeof(self->img), "%s/img", self->dir); + snprintf(self->mnt, sizeof(self->mnt), "%s/mnt", self->dir); + if (mkdir(self->mnt, 0755)) { + *why = "cannot create the mount point"; + goto fail; + } + fd = open(self->img, O_CREAT | O_RDWR, 0600); + if (fd < 0) { + *why = "cannot create the image file"; + goto fail; + } + if (ftruncate(fd, IMG_SIZE)) { + close(fd); + *why = "cannot size the image file"; + goto fail; + } + close(fd); + if (run_cmd("mkfs.xfs -q -f %s", self->img)) { + *why = "mkfs.xfs failed"; + goto fail; + } + if (run_cmd("mount -o loop %s %s", self->img, self->mnt)) { + *why = "cannot loop-mount the XFS image"; + goto fail; + } + self->mounted = 1; + return 0; +fail: + cleanup(self); + return 1; +} + +FIXTURE_SETUP(numa_migrate) +{ + const char *why; + + self->cgv2_fd = -1; + self->cg_fd = -1; + self->data_fd = -1; + self->dir[0] = '\0'; + + if (geteuid()) + SKIP(return, "needs root"); + if (two_nodes_with_memory(&self->node_from, &self->node_to)) + SKIP(return, "needs two NUMA nodes with memory"); + if (access("/usr/sbin/mkfs.xfs", X_OK) && access("/sbin/mkfs.xfs", X_OK)) + SKIP(return, "needs mkfs.xfs"); + if (prepare(self, &why)) + SKIP(return, "%s", why); +} + +FIXTURE_TEARDOWN(numa_migrate) +{ + if (self->leave_mounted) { + TH_LOG("leaving %s mounted, eviction would hit BUG_ON(nrpages)", + self->mnt); + self->dir[0] = '\0'; + } + cleanup(self); +} + +TEST_F(numa_migrate, large_folio_order) +{ + unsigned long long on_to_before, on_to_after; + unsigned char *buf, *check; + char path[PATH_MAX], list[64]; + long large, folios; + unsigned long off; + int fd, i; + + ASSERT_EQ(write_u64_at(self->cg_fd, "cgroup.procs", getpid()), 0); + + buf = malloc(CHUNK_SIZE); + check = malloc(CHUNK_SIZE); + ASSERT_NE(buf, NULL); + ASSERT_NE(check, NULL); + + snprintf(path, sizeof(path), "%s/data", self->mnt); + fd = open(path, O_CREAT | O_RDWR | O_TRUNC, 0600); + ASSERT_GE(fd, 0); + self->data_fd = fd; + for (off = 0; off < FILE_SIZE; off += CHUNK_SIZE) { + memset(buf, (off / CHUNK_SIZE) & 0xff, CHUNK_SIZE); + ASSERT_EQ(pwrite(fd, buf, CHUNK_SIZE, off), (ssize_t)CHUNK_SIZE); + } + ASSERT_EQ(fsync(fd), 0); + + large = large_folio_pages(fd, FILE_SIZE, &folios); + ASSERT_GE(large, 0) TH_LOG("cannot read the page cache folio sizes"); + TH_LOG("%ld of %lu file pages are in %ld large folios", + large, FILE_SIZE / PAGE_SIZE, folios); + ASSERT_GT(large, 0) + TH_LOG("no large folio in the page cache, the bug is out of reach"); + + ASSERT_EQ(file_bytes_on_node(self->cg_fd, self->node_to, + &on_to_before), 0); + + /* From here on a failure means eviction would crash the kernel. */ + self->leave_mounted = 1; + snprintf(list, sizeof(list), "%d,%d", self->node_from, self->node_to); + ASSERT_EQ(write_file_at(self->cg_fd, "cpuset.mems", list), 0); + ASSERT_EQ(write_u64_at(self->cg_fd, "memory.numa_migrate", + self->node_to), 0); + + for (off = 0; off < FILE_SIZE; off += CHUNK_SIZE) { + memset(buf, (off / CHUNK_SIZE) & 0xff, CHUNK_SIZE); + ASSERT_EQ(pread(fd, check, CHUNK_SIZE, off), (ssize_t)CHUNK_SIZE); + ASSERT_EQ(memcmp(buf, check, CHUNK_SIZE), 0); + } + self->leave_mounted = 0; + + /* memory.numa_stat flushes per-cpu counters lazily, give it time. */ + for (i = 0; i < STAT_RETRIES; i++) { + ASSERT_EQ(file_bytes_on_node(self->cg_fd, self->node_to, + &on_to_after), 0); + if (on_to_after >= on_to_before + FILE_SIZE) + break; + usleep(STAT_RETRY_US); + } + TH_LOG("file bytes on node %d: %llu -> %llu", self->node_to, + on_to_before, on_to_after); + ASSERT_GE(on_to_after, on_to_before + FILE_SIZE) + TH_LOG("memory.numa_migrate moved nothing to node %d", self->node_to); + + free(buf); + free(check); +} + +TEST_HARNESS_MAIN -- 2.43.0 _______________________________________________ Devel mailing list Devel@openvz.org https://lists.openvz.org/mailman/listinfo/devel