From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail.openvz.org (unknown [69.168.225.77]) by lore.virtuozzo.com (Postfix) with ESMTPS id 28B728045D for ; Wed, 21 Oct 2026 16:59:13 +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 69LGvwcM023079; Wed, 21 Oct 2026 19:57:59 +0300 DKIM-Filter: OpenDKIM Filter v2.11.0 mail.openvz.org 69LGvwcM023079 Authentication-Results: mail.openvz.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=virtuozzo.com header.i=@virtuozzo.com header.b="UtVjYlU0" Received: from mail-wr1-f71.google.com (mail-wr1-f71.google.com [209.85.221.71]) by mail.openvz.org (8.14.4/8.14.4) with ESMTP id 69LGvNnY022967 (version=TLSv1/SSLv3 cipher=AES128-GCM-SHA256 bits=128 verify=FAIL) for ; Wed, 21 Oct 2026 19:57:23 +0300 DKIM-Filter: OpenDKIM Filter v2.11.0 mail.openvz.org 69LGvNnY022967 Received: by mail-wr1-f71.google.com with SMTP id ffacd0b85a97d-48e1d839ddaso656435f8f.3 for ; Wed, 21 Oct 2026 09:57:23 -0700 (PDT) X-Gm-Message-State: AFq9FYJeSdovfEFUQh5o5MrnOSR78AQEG00dn6lpMU1BUM/Nc/F7ban6 Rx4/QFDiUwHwU2CSDOWEIEvBf8y6t9jT/8yvHeFVfFZg9gkwbmJn+Us7LQjBcXmc21JdD8dWI5w /+MXVqBs+Qz6EkaNspuWPr2BQ2L051F+W8eBROui4Iynj5SrGBBh0nQ== X-Gm-Gg: AYBFou3kq1VItC4RoO5hiQ5lymqkgR4erdIZNFLsO+G+4voJwsMcrJi7aebI5HAIyMI PfD1z+h7nHzbG8/MGh+auBvkRUaVmpd2lgAbv2n8Fnr3BRrlfkzcCDNr+C9LCNtAG3v8dTVleUY tNDYLjDmVWtiFduh0By/Kc3QkJit+RJniGEmbG61SNVYNjsWBln047gy2tX5UVezHNYfYPq2P5+ Y5UftgBFThOcQKmEFN/e72i5Vqj4nrWBw3H/o1l6VjlOdpGDyIKLSwoDsz+AEChO8Mb6bLI4RwW GvDupB/L7htXs8zDfTV1AlyC/IwgirDd23Qvb629PmMWK1ivELRPeuOkl9j8lAwZYqBIbaj10qH fwWck+Pw5UiMVUH8t X-Received: by 2002:a5d:64ed:0:b0:48d:f3a5:4563 with SMTP id ffacd0b85a97d-48e2649390cmr5295682f8f.20.1792601843446; Wed, 21 Oct 2026 09:57:23 -0700 (PDT) X-Received: by 2002:a5d:64ed:0:b0:48d:f3a5:4563 with SMTP id ffacd0b85a97d-48e2649390cmr5295639f8f.20.1792601842876; Wed, 21 Oct 2026 09:57:22 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1792601842; cv=none; d=google.com; s=arc-20260327; b=rFB9PMk8IaUsi5iY6EL25G3XEyx1X94oE+WOkl+7gglTaAKu3u2nFb85GDhf6+kUa3 G69SaKsZ3SC4ueZ/+51tX7Q6KD9a8LZ32gf3zd4Vaf5uZlgHRUxTXc+rfYcar6g8jF1Z hP35VBqnyBZmHkxq+uX4xJpAmK8jzxioKrFm+UNLasiG1BPiK6VK3cpBFvYum+YOJBEb WN8YHMfD9vuNPuL+wWKA2ceyPi5w0TBQsrdi1Q0mmAersmny+Y45bGhbdLjUeGOfmWMZ JJ7Y+r5QKi/mBgcCD9dRaT0wcAC1mbKqBhgIMQuYI3zp1bLiuhhfekptMdCjZKk/ANwt QbIg== 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=DiIiFmzlRNukCNYuT5BaCle0vCKqc9lgo1RpAl8+5Fw=; fh=ORqO450YHYjnAvQVnHrQRoSXc/Ljts4WdUx12SqEe+w=; b=eG789yLWHFOCcA0ufOFYXNrL4UnCri5X2d+eDH4ZBfiP6nYOA7sib2zCKF/Rp936lw zu3vjz62XRsYu90j/svCEGiaXAheqy/i6hIllIrBBLreHM8sqpHx5i6E91EQNcSDzepd hqZdZ7g10P6TThmVXyxMOjVYIEaNuBBPJNYCeGIjXVh/dnPmrHbnLyoN2MPWcr4KhkCJ P86o1eEZpNhUbl5AN+0EFrmNPIVc2l9IyVIaAVOetKbIS/nuOPgCCFN5SrVGuqAqfENO CcjvMqErWbtTSE3wSivXpQXKnrY9qgITYdrL4kGdazrstDVUzmz7IimkZ61ulIbdSh94 Wxkw==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@virtuozzo.com header.s=relay header.b=UtVjYlU0; 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-48e25870fd8si5757236f8f.288.2026.10.21.09.57.22 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 21 Oct 2026 09:57:22 -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=UtVjYlU0; 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=DiIiFmzlRNukCNYuT5BaCle0vCKqc9lgo1RpAl8+5Fw=; b=UtVjYlU09dPWbQ7rBmK OJ7bXmhW7Y+X8dgOot02MfjpbM3uUkUVD9KLBLC+j0AYoaK2CCtI2C0j8GGUxvnhcI82FUbucHvTS vJ8RdGPvULgWXfZufWPgYZEllgulX1pQuRci0P0iVkyncQETXpiGj1m2CQxkf4PPtI56JFcEv/soh Tut6szBXHnq+kbfX0sujBRLVdDSJckw1x2WhrhzjUyoOQn26NnCJzIThCYX7wFZcAugMDZZKMwbY5 IPgP6Z/6ayvzmOaFMbIPP+sCUgYoNBNmiPzCNoLRtfTL0YuRHdA7DIzTtfjamJrZKdsIXbCp0kDR+ wPEcaB9Akub3ghQ==; 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 1xJZZK-008y7H-2Z; Wed, 21 Oct 2026 18:57:21 +0200 From: Konstantin Khorenko Date: Wed, 21 Oct 2026 18:57:21 +0200 MIME-Version: 1.0 Message-Id: <20261021-b4-prep-khorenko-vstor-149847-5-doexec-selftests-v2-8-97dbb5c6e0ab@virtuozzo.com> References: <20261021-b4-prep-khorenko-vstor-149847-5-doexec-selftests-v2-0-97dbb5c6e0ab@virtuozzo.com> In-Reply-To: <20261021-b4-prep-khorenko-vstor-149847-5-doexec-selftests-v2-0-97dbb5c6e0ab@virtuozzo.com> To: Andrey Drobyshev , Pavel Tikhomirov , Vasileios Almpanis X-Mailer: b4 0.16.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=8463; i=khorenko@virtuozzo.com; h=from:subject:message-id; bh=ybBVp2r2CmYjju2y59mzdzCcNTSMuM5hcivp3CL0eWg=; b=owEBbQKS/ZANAwAKAVGWCkf5YVwqAcsmYgBq2O7vHkILYikGY1TZpLolq5UIeFGho3AQhKcbZ /1jKYPhYWaJAjMEAAEKAB0WIQRWD23IPcXT0GO9CCxRlgpH+WFcKgUCatju7wAKCRBRlgpH+WFc KkoyEACPuBkFGvtRX+Xfowjm5K0FKbB2iItujeKXvg57mbBuobFoEcBVqUArCuQ3u+Z6fKn6hOf G04Lrf9w0W+GZDAJss/RxQerTMkGhKn88CeS/epPTSENxsmkTwC5rsNpiQvmoH6r0x+K9WdeRJ2 g9ZZlrCOX+Q7T8A5PDbpThbO0UfE2yf43gGAgKH0kZy4C7ClSlZTKgeB6YJuG3+wdzoq88IUHrX fz5OOpxrnn5CCOTYzf4YbdhFP3ttd8w4NhqaYjqmHDkvY1aO6OJ/Aw5/l4G05EtqBm5hqFGxA69 oMjV018lcNQ29StfUy3hp8eXuhxUEq41N63GiL5QFOTcqv7hJlkLGRhPquUBTL33E7qj13py3+f GsOBF6KeNRuWkKfeUdm4b+NxsmLdbtqeNWSHwr3V65MFicNQELnSbsZbcM2RxfScqF3xe0zpo0x xtAXu7MD3q90q5yzC52gGhdESRTz+p8nCNEBBiegyibSRXBZbucwrv4hcvYugRIoRx4kq9MWsva I96B2I6yVRAr36Kwb569QktoHglUesB8APJVKLPmnywGiMr/XJ3obOhv9TR497XhaH04PkhnNmb BpUYg9gJ5+4jTKf1sKCi6ufyr8n3mkNIOBIg4GDit0HkUKbYrEZjcjXRM7atkw3W6Vvn3Ta5dxk UIITffl5Gb60RpA== 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 8/9] selftests/mm: run repeated live updates through MADV_DOEXEC 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 Each doexec case preserves memory across one or two execs. A QEMU live update repeats: the guest runs, QEMU execs itself keeping guest RAM, DMA pins and device BARs, the guest runs on, and the next update follows. Add a case that goes through a number of such generations. Guest RAM is backed by THP, part of it has a long-term pin from gup_test, and BAR 0 of the DOEXEC_VFIO_DEVICE device is mapped when one is named. In every generation the new image checks what the previous one wrote, through the CPU and through the pin. Then several threads write a new pattern into their stripes of the RAM and the BAR, the image marks both again and execs with an idle thread still running, as QEMU does with its paused vCPU threads. The last generation drops the pin. Without gup_test (release kernels) or without a VFIO device the case runs with the parts that are there. https://virtuozzo.atlassian.net/browse/VSTOR-150234 Feature: oracle/mm: MADV_DOEXEC madvise() flag Assisted-by: LLM Signed-off-by: Konstantin Khorenko --- tools/testing/selftests/mm/doexec.c | 178 ++++++++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) diff --git a/tools/testing/selftests/mm/doexec.c b/tools/testing/selftests/mm/doexec.c index 25005e98fdd1d..7e73a98e4ec2a 100644 --- a/tools/testing/selftests/mm/doexec.c +++ b/tools/testing/selftests/mm/doexec.c @@ -2118,6 +2118,170 @@ static int vhost_kthread_exec(void) #endif } +#define CHAIN_GENERATIONS 16 +#define CHAIN_THREADS 4 + +/* What a live-updated QEMU hands to its next image: guest RAM, a DMA pin, a BAR. */ +struct chain { + char *ram, *bar; + size_t size, pin_size, bar_size; + int pin, gen; +}; + +struct chain_stripe { + struct chain *chain; + int index; +}; + +static unsigned char chain_byte(int gen) +{ + return 0x20 + gen; +} + +static uint32_t chain_word(int gen, size_t page) +{ + return 0xc0000000 | gen << 16 | page; +} + +/* One vCPU of the guest: rewrites its stripe of the RAM and of the BAR. */ +static void *chain_writer(void *arg) +{ + struct chain_stripe *stripe = arg; + struct chain *c = stripe->chain; + size_t page = sysconf(_SC_PAGESIZE), len = c->size / CHAIN_THREADS, i; + + memset(c->ram + stripe->index * len, chain_byte(c->gen), len); + for (i = stripe->index; i < c->bar_size / page; i += CHAIN_THREADS) + __atomic_store_n((uint32_t *)(c->bar + i * page), chain_word(c->gen, i), + __ATOMIC_RELAXED); + return NULL; +} + +static int chain_check(struct chain *c, int gen) +{ + size_t page = sysconf(_SC_PAGESIZE), i; + + if (c->pin >= 0 && check_pin(c->pin, c->ram, c->pin_size, chain_byte(gen))) + return fail("pinned part of the RAM differs"); + if (check_bytes(c->ram, c->size, chain_byte(gen))) + return fail("RAM contents lost"); + if (c->bar_size && (!smaps_has_flag(c->bar, " pf ") || !smaps_has_flag(c->bar, " sh "))) + return fail("BAR not a shared PFN map"); + for (i = 0; i < c->bar_size / page; i++) + if (__atomic_load_n((uint32_t *)(c->bar + i * page), __ATOMIC_RELAXED) != + chain_word(gen, i)) + return fail("BAR contents lost"); + return KSFT_PASS; +} + +static int chain_exec(struct chain *c) +{ + char args[7][32]; + int pipe_fd[2]; + pthread_t thread; + + if (madvise(c->ram, c->size, MADV_DOEXEC)) + return fail_errno("MADV_DOEXEC"); + if (c->bar_size && madvise(c->bar, c->bar_size, MADV_DOEXEC)) + return fail_errno("MADV_DOEXEC on the BAR"); + snprintf(args[0], 32, "%llu", (unsigned long long)(uintptr_t)c->ram); + snprintf(args[1], 32, "%zu", c->size); + snprintf(args[2], 32, "%d", c->pin); + snprintf(args[3], 32, "%zu", c->pin_size); + snprintf(args[4], 32, "%llu", (unsigned long long)(uintptr_t)c->bar); + snprintf(args[5], 32, "%zu", c->bar_size); + snprintf(args[6], 32, "%d", c->gen); + /* Like a paused vCPU thread, it is still there when QEMU execs. */ + if (pipe(pipe_fd) || pthread_create(&thread, NULL, blocked_thread, &pipe_fd[0])) + return fail("idle thread"); + execl("/proc/self/exe", "doexec", "--chain", args[0], args[1], args[2], args[3], + args[4], args[5], args[6], NULL); + return fail_errno("exec"); +} + +/* One generation: check what the last one left, work, then update again. */ +static int after_chain(struct chain *c) +{ + struct chain_stripe stripes[CHAIN_THREADS]; + pthread_t threads[CHAIN_THREADS]; + int i; + + if (chain_check(c, c->gen - 1)) + return KSFT_FAIL; + if (c->gen == CHAIN_GENERATIONS) + return c->pin >= 0 && ioctl(c->pin, PIN_LONGTERM_TEST_STOP) ? + fail_errno("PIN_LONGTERM_TEST_STOP") : KSFT_PASS; + for (i = 0; i < CHAIN_THREADS; i++) { + stripes[i] = (struct chain_stripe) { c, i }; + if (pthread_create(&threads[i], NULL, chain_writer, &stripes[i])) + return fail("pthread_create failed"); + } + for (i = 0; i < CHAIN_THREADS; i++) + pthread_join(threads[i], NULL); + if (chain_check(c, c->gen)) + return KSFT_FAIL; + c->gen++; + return chain_exec(c); +} + +/* Guest RAM with THP, a long-term pin on it and a VFIO BAR if one is named. */ +static int live_update_chain(void) +{ + struct vfio_region_info info = { + .argsz = sizeof(info), + .index = VFIO_PCI_BAR0_REGION_INDEX, + }; + size_t page = sysconf(_SC_PAGESIZE), huge = huge_page_size(), i; + const char *bdf = getenv("DOEXEC_VFIO_DEVICE"); + struct chain c = { .pin = -1 }; + struct pin_longterm_test pin; + char *area; + int dev; + + c.size = huge ? 4 * huge : 64 * page; + area = mmap(NULL, 2 * c.size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (area == MAP_FAILED) + return fail_errno("mmap"); + c.ram = (char *)(((uintptr_t)area + c.size - 1) & ~(c.size - 1)); + if (huge && madvise(c.ram, c.size, MADV_HUGEPAGE)) + return fail_errno("MADV_HUGEPAGE"); + memset(c.ram, chain_byte(0), c.size); + if (madvise(c.ram, c.size, MADV_DOEXEC)) + return unsupported(); + c.pin = open("/sys/kernel/debug/gup_test", O_RDWR); + if (c.pin >= 0) { + c.pin_size = huge ? huge : 16 * page; + pin = (struct pin_longterm_test) { + .addr = (uintptr_t)c.ram, + .size = c.pin_size, + .flags = PIN_LONGTERM_TEST_FLAG_USE_WRITE, + }; + if (ioctl(c.pin, PIN_LONGTERM_TEST_START, &pin)) + return fail_errno("PIN_LONGTERM_TEST_START"); + } + if (bdf) { + dev = vfio_device_fd(bdf); + if (dev < 0 || ioctl(dev, VFIO_DEVICE_GET_REGION_INFO, &info)) { + ksft_print_msg("VFIO device %s: %s\n", bdf, strerror(errno)); + return KSFT_FAIL; + } + if (!(info.flags & VFIO_REGION_INFO_FLAG_MMAP) || info.size < page) { + ksft_print_msg("VFIO device %s: BAR 0 cannot be mapped\n", bdf); + return KSFT_FAIL; + } + c.bar_size = info.size < (4ul << 20) ? info.size : 4ul << 20; + c.bar = mmap(NULL, c.bar_size, PROT_READ | PROT_WRITE, MAP_SHARED, dev, + info.offset); + if (c.bar == MAP_FAILED) + return fail_errno("mmap of the BAR"); + for (i = 0; i < c.bar_size / page; i++) + __atomic_store_n((uint32_t *)(c.bar + i * page), chain_word(0, i), + __ATOMIC_RELAXED); + } + c.gen = 1; + return chain_exec(&c); +} + struct more_case { const char *name; int (*fn)(void); @@ -2173,6 +2337,7 @@ static const struct more_case more_cases[] = { { "exec permission back in a fork child and the new image", mayexec_exec }, { "/proc/pid/mem write stalled across exec: lands in the old mm", old_mm_write_exec }, { "vhost owner in kthread mode: preserving exec refused", vhost_kthread_exec }, + { "repeated live updates: RAM, DMA pin and BAR kept", live_update_chain }, }; int main(int argc, char **argv) @@ -2199,6 +2364,19 @@ int main(int argc, char **argv) strtoull(argv[3], NULL, 10)); if (argc == 2 && !strcmp(argv[1], "--euid")) return geteuid() ? KSFT_SKIP : KSFT_PASS; + if (argc == 9 && !strcmp(argv[1], "--chain")) { + struct chain c = { + .ram = (char *)(uintptr_t)strtoull(argv[2], NULL, 10), + .size = strtoull(argv[3], NULL, 10), + .pin = atoi(argv[4]), + .pin_size = strtoull(argv[5], NULL, 10), + .bar = (char *)(uintptr_t)strtoull(argv[6], NULL, 10), + .bar_size = strtoull(argv[7], NULL, 10), + .gen = atoi(argv[8]), + }; + + return after_chain(&c); + } if (argc == 3 && !strcmp(argv[1], "--pfnmap-gone")) return after_pfnmap_gone((char *)(uintptr_t)strtoull(argv[2], NULL, 10)); if (argc == 4 && !strcmp(argv[1], "--pfnmap")) -- 2.43.0 _______________________________________________ Devel mailing list Devel@openvz.org https://lists.openvz.org/mailman/listinfo/devel