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 7D42880777 for ; Tue, 6 Oct 2026 20:46:05 +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 696KiqoJ027844; Tue, 6 Oct 2026 23:44:54 +0300 DKIM-Filter: OpenDKIM Filter v2.11.0 mail.openvz.org 696KiqoJ027844 Authentication-Results: mail.openvz.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=virtuozzo.com header.i=@virtuozzo.com header.b="LZJp9u8y" Received: from mail-wm1-f69.google.com (mail-wm1-f69.google.com [209.85.128.69]) by mail.openvz.org (8.14.4/8.14.4) with ESMTP id 696KicXa027801 (version=TLSv1/SSLv3 cipher=AES128-GCM-SHA256 bits=128 verify=FAIL) for ; Tue, 6 Oct 2026 23:44:38 +0300 DKIM-Filter: OpenDKIM Filter v2.11.0 mail.openvz.org 696KicXa027801 Received: by mail-wm1-f69.google.com with SMTP id 5b1f17b1804b1-4a17574b5easo16119865e9.2 for ; Tue, 06 Oct 2026 13:44:38 -0700 (PDT) X-Forwarded-Encrypted: i=2; AKwUvBxZWoH2699G6UopdZTciiruXrj/GRV4rqmPRdjwU2ZwAUzLDj9kcbzmHlOVBNalt290hAwE4Q==@openvz.org X-Gm-Message-State: AFuF++mlH7BmYUcW0/xWIXBdVC165xtuzTXlbEeGCvveJMKD3cVbj0XW DYjOHkG0RyHj7KT10ate5p3rP4AndapetprpM4GVaB0QUogyAQlZpPMdfyiBqgpKntcCS89nn+X 5yKPTjDGVgbQdhVQ+UcJl1uKtDLm76+c/YvGFEsF352PnSskUGmiHyw== X-Gm-Gg: AYBFou00sxI5A4rTyKYZ/TLB1R4RMtFzbxZeScX7wlpF6rN/i5bttrZ9860AQx3NyGH x3N3G3yd3G+MDqS6LXgvdHD+Z6MuOipLknigg4a202sFQW1wmwDX5qWQvuC+IYFjYnyXIwMxxv+ GYGJyIqDemwk1vXkEuJtK3ZWHCOOMI6Q+wzeixGDnKYX5PZnOIn2SaWbRN4T7f9qh5X4jjaPmpv gWbE79CPvNZQH+uL4CdyeE5MMIn83oKaNZJALTCJl87kOwGVmIkPDk4iKjh+jxHpnMAyJE2tF4l 7UM97GJc5WWMuYclPL58iFal3+ONCzs4NsnJYc/xg28H6QwEgDNIkUOL9/qusnDon+dcJtjHgXW Z1fKsdhmAoMSXxVXL X-Received: by 2002:a05:600c:4687:b0:49f:dc71:e609 with SMTP id 5b1f17b1804b1-4a18065f300mr442515e9.20.1791319478099; Tue, 06 Oct 2026 13:44:38 -0700 (PDT) X-Received: by 2002:a05:600c:4687:b0:49f:dc71:e609 with SMTP id 5b1f17b1804b1-4a18065f300mr442255e9.20.1791319477500; Tue, 06 Oct 2026 13:44:37 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1791319477; cv=none; d=google.com; s=arc-20260327; b=JG6hoeIbo9xXYsHc2nqO0Tk1EuBU6Y0wUTUCsIDaDZ8OqR3tMcmr+oDbaqXvhgs7tj MkA6QzOEmfRtEFhF5ew6QVMtnf6S3qoEE3s0SzXYaaM7WYz+wM8yoN+kHWn7hpfpp/eh AoJHJ8Ky9pFapD4xZRdTkmrablxrJpq2Dje9w2MegU5gfYaqbcZuWv+7novxTrvfDKZW HyiCdgg+j2c3Uq9Fi4SKBWzgwNVoiMo0Q5WO14gM/sG4XsqzzYeEKwU4bNiv0U+vyDIp BdAq5cSHNS81S5n/NFB8RGjHG5ZqmksX7gyNfhtFuXm6Ex5SRtg4FGCH8kiuTKyw05M4 +tnA== 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=TUYZO+X1kxRw0w07t3d8g+QGbgCaCs16/1YlqC9uAus=; fh=bRkZfFa6377EgOVW8zBDri2gGIQqe/ZeiH+ijEM/ROI=; b=E2x+DQIshuc/NZ5SF0liYLANqIgj4L+B4sfCvdpe1O+dRqEE68CkmFtR+yYO/Wg1Eo RCrMtEsiT+23nAt+LN078+jhJu58SkbFP/43optPf+ajT9gRXgzNCjDqtzapxCE9DiIu jJodAg8XdFM1tEPbGYj2uex95Q8moGcuLwHoOyIRk6AJYVQfNpG44rhII6sTOuZpdVfz 8aj+MTnWnpgBUbuvizsB+lj8zOQAEz3ZC0JAH2EOeAJPimN5UofYKWz5RFsIUikHcCNf Urx179C8222JKgVlim/r2gUVkU8ReiGH4KNbRCQhELb92kl1M8oA9Q+JmM1rkLkE2tWJ Utfg==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@virtuozzo.com header.s=relay header.b=LZJp9u8y; 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 5b1f17b1804b1-4a178ca9076si80192785e9.11.2026.10.06.13.44.37 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 06 Oct 2026 13:44:37 -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=LZJp9u8y; 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=TUYZO+X1kxRw0w07t3d8g+QGbgCaCs16/1YlqC9uAus=; b=LZJp9u8ynx18a+9ui1L vwaIImRgsl7NCjo1WMar1bJCUc61aRGlxxlBuaksBh01JjweKu87WzPy+Sp3Mdh59VdjJ/yHC4XTa Bap5qDQmX5DEnIjC2J4qTNYBYAryI9nsTWHpVT1Ieu3XQ9dOvLbc3Xg8Nq9TQ0Yk/Q3PDIrXnl5pW NllSZcBr9vO4fWn+1hJa0HqPHnWOpCrzZaFoyf4twtYMN1h9ABrbmMBt8+Hyh8SsDGl8CsdEFz6l0 XQWWbaUjpuCE3cpCmIbJbEDygtj45GrN71wAu+U0cBWwLF3RWv0Bt24CP2DrKuhhdpGwyAkB3a0QC 8osomUyMdxLmp0A==; 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 1xEByN-002tkg-2n; Tue, 06 Oct 2026 22:44:26 +0200 From: Konstantin Khorenko Date: Tue, 06 Oct 2026 22:44:32 +0200 MIME-Version: 1.0 Message-Id: <20261006-b4-prep-khorenko-vstor-148656-cpuid-v3-1-16141b8ca07d@virtuozzo.com> References: <20261006-b4-prep-khorenko-vstor-148656-cpuid-v3-0-16141b8ca07d@virtuozzo.com> In-Reply-To: <20261006-b4-prep-khorenko-vstor-148656-cpuid-v3-0-16141b8ca07d@virtuozzo.com> To: Pavel Tikhomirov , Vasileios Almpanis X-Mailer: b4 0.16.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=9807; i=khorenko@virtuozzo.com; h=from:subject:message-id; bh=nxZlLBH+4bp5BIYsz0URyCwYJpbHkV60+64Aob99q+I=; b=owEBbQKS/ZANAwAKAVGWCkf5YVwqAcsmYgBqxV2y3oWnzqtbckCszOWPUqnOUrKLo4FjgiJCh jeMMp6NkbuJAjMEAAEKAB0WIQRWD23IPcXT0GO9CCxRlgpH+WFcKgUCasVdsgAKCRBRlgpH+WFc KmMrD/0V/53w5qk+4vROL6oXUjqbLmk68S1eF/To+qTHLIuEIeRoNjsRjknd00eYbLdDNNU70Ap 0dZKahsq6lORkXnpeFltTbvJHYSvs15ZKQRXUb0tzNYAx3s0Uf1jKXUR7QGzcQY3KSpg9PL+rMR NJA23NOsyTgXv4CNMB0gPEhjfpj4/+QQAigsxXrPb3hEKUnrEWl/Ub5BWFYc9gL3vqleWbXtqBJ TmIKlUmwF3xJVATM1PgejUY35uXlR6lGRaISZZMrhSL/nWTawxhgatotRtvwsgQBTb23ize+F8c 2jNxumckDtYYZ47OL2zO1+4tPJrdQXe7r23EJsJgj/Py4TsceaVFUPmEDf2bipSgM+226jAy/3u VuGfhpTwrZ47GUFipjC2dyoOuc2Q9ZE0RRgOgBIGSl7KFtYtHrHkY4wnE5mxa3Zbsu9xrrDfR5m mDrrb9jtKNAavSa6JPk8E7p6gIkO9NqFLXKjWo9kCEg/D0azgEJyMQpju0KrSO+tXyR8B9MztFy XXFElAPUvpl2E4Kxl4v/BT9F64dUZJh1snAG26RtilJFkeXDG22j6iMQq/N1AssDOhFrgd0IH4y 3Rl/NvHmZLlroewwO2EQy/xhknV/nEH4EMs0A0WcdJoZ8US8elbEWp9LXWFIkeV6cuM7ireRafg aIkOnbF0J30eqFQ== 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 v3 1/5] selftests/ve: check CPUID override emulation for VE tasks 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 Write a one-entry /proc/vz/cpuid_override table that clears one leaf 1 feature bit and compare what a task in a fresh VE and the host see: the VE task must get the table values, the host the hardware ones. A second test flushes the table after the task was armed and expects the hardware values again, since faulting stays on for the task and the emulation has to fall back to the real CPUID. The child stops itself with SIGSTOP after entering the VE and the parent resumes it before the CPUID: faulting is switched on for a task at its next context switch, not when the flag is set, and a stopped task is off the CPU for sure. For the second test the table is flushed while the child stands still. Without the fix both children die with SIGSEGV. The tests skip on CPUs without CPUID faulting and restore the previous table on exit; a table the kernel cannot take back in one write fails the setup instead. The teardown runs in the parent because the harness skips a child-side teardown once an ASSERT has fired, and it kills a child that such an ASSERT left stopped in the VE cgroup. The test is built on x86 only. https://virtuozzo.atlassian.net/browse/VSTOR-148843 Feature: x86: cpu pools (cpuid override) Signed-off-by: Konstantin Khorenko --- tools/testing/selftests/ve/.gitignore | 1 + tools/testing/selftests/ve/Makefile | 10 + .../testing/selftests/ve/ve_cpuid_override_test.c | 247 +++++++++++++++++++++ 3 files changed, 258 insertions(+) diff --git a/tools/testing/selftests/ve/.gitignore b/tools/testing/selftests/ve/.gitignore index 3df4d05888dce..bdc322c645d95 100644 --- a/tools/testing/selftests/ve/.gitignore +++ b/tools/testing/selftests/ve/.gitignore @@ -1,3 +1,4 @@ ve_ns_owner_test ve_perms_test ve_mount_accounting_test +ve_cpuid_override_test diff --git a/tools/testing/selftests/ve/Makefile b/tools/testing/selftests/ve/Makefile index c6efe7c4b4fba..d919c1c289c8b 100644 --- a/tools/testing/selftests/ve/Makefile +++ b/tools/testing/selftests/ve/Makefile @@ -6,4 +6,14 @@ TEST_GEN_PROGS += ve_ns_owner_test TEST_GEN_PROGS += ve_perms_test TEST_GEN_PROGS += ve_mount_accounting_test +# ARCH stays untouched: lib.mk picks the clang target by its emptiness. +ifeq ($(ARCH),) +ve_arch := $(shell uname -m 2>/dev/null || echo not) +else +ve_arch := $(ARCH) +endif +ifeq ($(ve_arch),$(filter $(ve_arch),x86 x86_64)) +TEST_GEN_PROGS += ve_cpuid_override_test +endif + include ../lib.mk diff --git a/tools/testing/selftests/ve/ve_cpuid_override_test.c b/tools/testing/selftests/ve/ve_cpuid_override_test.c new file mode 100644 index 0000000000000..0e3537099ddb2 --- /dev/null +++ b/tools/testing/selftests/ve/ve_cpuid_override_test.c @@ -0,0 +1,247 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * /proc/vz/cpuid_override selftests + * + * A non-empty override table turns CPUID faulting on for every task that + * enters a VE, and the #GP handler has to emulate the instruction for + * them. The tests check that a task in a VE sees the table values, that + * the host keeps seeing the hardware values, and that a task which was + * armed while the table was set falls back to the hardware once the + * table is flushed. A kernel that does not emulate the fault kills the + * child with SIGSEGV instead. + */ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../kselftest_harness.h" +#include "ve_selftest.h" + +#define CPUID_OVERRIDE_PATH "/proc/vz/cpuid_override" +#define CPUID_LEAF 1 +#define CPUID_TABLE_LEN 128 +/* The kernel takes a table back only in one write below a page. */ +#define CPUID_WRITE_MAX 4096 +#define CPUID_TABLE_MAX (2 * CPUID_WRITE_MAX) + +struct cpuid_regs { + unsigned int eax, ebx, ecx, edx; +}; + +/* x86_64 only, so ebx is not the PIC base and "=b" is safe. */ +static void do_cpuid(unsigned int leaf, struct cpuid_regs *r) +{ + asm volatile("cpuid" + : "=a" (r->eax), "=b" (r->ebx), "=c" (r->ecx), "=d" (r->edx) + : "a" (leaf), "c" (0)); +} + +static int write_cpuid_override(const char *table) +{ + int fd, ret; + + fd = open(CPUID_OVERRIDE_PATH, O_WRONLY); + if (fd < 0) + return -1; + ret = write(fd, table, strlen(table)); + close(fd); + return ret == (int)strlen(table) ? 0 : -1; +} + +/* + * The child enters a fresh VE, so it is armed with the table state of that + * moment, then reports its view of the leaf to the parent over a pipe. It + * stops itself in between: a stopped task is off the CPU, so the resume is + * a context switch, and the parent may change the table while the child + * stands still. + */ +struct child_ctx { + int result_pipe[2]; +}; + +static int cpuid_child(struct child_ctx *ctx) +{ + struct cpuid_regs r; + + close(ctx->result_pipe[0]); + + if (unshare(CLONE_NEWVE) < 0) + return 21; + if (raise(SIGSTOP)) + return 22; + + do_cpuid(CPUID_LEAF, &r); + + if (write(ctx->result_pipe[1], &r, sizeof(r)) != sizeof(r)) + return 24; + return 0; +} + +FIXTURE(ve_cpuid_override) +{ + int cgv2_fd; + int ctid; + struct cpuid_regs host; + unsigned int bit; + pid_t child; + char table[CPUID_TABLE_LEN]; + char saved[CPUID_TABLE_MAX]; +}; + +FIXTURE_SETUP(ve_cpuid_override) +{ + int fd, len; + + fd = open(CPUID_OVERRIDE_PATH, O_RDONLY); + if (fd < 0) + SKIP(return, "%s: %s", CPUID_OVERRIDE_PATH, strerror(errno)); + close(fd); + + len = read_file_at(AT_FDCWD, CPUID_OVERRIDE_PATH, self->saved, + sizeof(self->saved)); + ASSERT_GE(len, 0); + ASSERT_LT(len, CPUID_WRITE_MAX) + TH_LOG("a table of %d bytes cannot be restored", len); + + /* Mask one feature bit the host has; SSE2 when present. */ + do_cpuid(CPUID_LEAF, &self->host); + self->bit = 1u << 26; + if (!(self->host.edx & self->bit)) + self->bit = self->host.edx & -self->host.edx; + ASSERT_NE(self->bit, 0); + + snprintf(self->table, sizeof(self->table), + "0x%08x: 0x%08x 0x%08x 0x%08x 0x%08x\n", CPUID_LEAF, + self->host.eax, self->host.ebx, self->host.ecx, + self->host.edx & ~self->bit); + + /* Last, so that a failed ASSERT above leaves no VE cgroup behind. */ + self->cgv2_fd = mount_cg2_fd(); + ASSERT_GE(self->cgv2_fd, 0); + ASSERT_EQ(write_file_at(self->cgv2_fd, "cgroup.subtree_control", + VE_CONTROLLERS), 0); + self->ctid = make_ve(self->cgv2_fd, CTID_MIN); + ASSERT_GE(self->ctid, 0); +} + +/* + * Runs in the parent: a child-side teardown is skipped when an ASSERT fires, + * and the table must be restored even then. The fd from setup belongs to the + * child, so the cgroup2 tree is mounted again here. + */ +FIXTURE_TEARDOWN_PARENT(ve_cpuid_override) +{ + int cgv2_fd; + + if (write_cpuid_override(self->saved[0] ? self->saved : "\n")) + TH_LOG("could not restore %s: %s", CPUID_OVERRIDE_PATH, + strerror(errno)); + + /* A child left stopped by a failed ASSERT keeps the VE cgroup busy. */ + if (self->child > 0) + kill(self->child, SIGKILL); + + cgv2_fd = mount_cg2_fd(); + if (cgv2_fd < 0) + return; + destroy_ve(cgv2_fd, self->ctid); + close(cgv2_fd); +} + +/* Fork the child into the VE and return once it stands still there. */ +static pid_t spawn_cpuid_child(struct __test_metadata *_metadata, + FIXTURE_DATA(ve_cpuid_override) * self, + struct child_ctx *ctx) +{ + int status; + pid_t pid; + + ASSERT_EQ(pipe(ctx->result_pipe), 0); + ASSERT_GE(enter_cgroup(self->cgv2_fd, self->ctid), 0); + + pid = fork(); + ASSERT_GE(pid, 0); + if (pid == 0) + _exit(cpuid_child(ctx)); + self->child = pid; + + ASSERT_GE(enter_cgroup(self->cgv2_fd, 0), 0); + close(ctx->result_pipe[1]); + + ASSERT_GE(waitpid(pid, &status, WUNTRACED), 0); + if (WIFEXITED(status)) + TH_LOG("child exited with %d before stopping", + WEXITSTATUS(status)); + ASSERT_TRUE(WIFSTOPPED(status)); + return pid; +} + +static void resume_cpuid_child(struct __test_metadata *_metadata, pid_t pid) +{ + ASSERT_EQ(kill(pid, SIGCONT), 0); +} + +static void collect_cpuid_child(struct __test_metadata *_metadata, + FIXTURE_DATA(ve_cpuid_override) * self, + struct child_ctx *ctx, struct cpuid_regs *r) +{ + int status, len; + + len = read(ctx->result_pipe[0], r, sizeof(*r)); + ASSERT_GE(waitpid(self->child, &status, 0), 0); + self->child = 0; + if (WIFSIGNALED(status)) + TH_LOG("child killed by signal %d", WTERMSIG(status)); + ASSERT_TRUE(WIFEXITED(status)); + ASSERT_EQ(WEXITSTATUS(status), 0); + ASSERT_EQ(len, sizeof(*r)); + + close(ctx->result_pipe[0]); +} + +TEST_F(ve_cpuid_override, ve_sees_table_host_sees_hardware) +{ + struct child_ctx ctx; + struct cpuid_regs r; + pid_t pid; + + ASSERT_EQ(write_cpuid_override(self->table), 0); + + pid = spawn_cpuid_child(_metadata, self, &ctx); + resume_cpuid_child(_metadata, pid); + collect_cpuid_child(_metadata, self, &ctx, &r); + + EXPECT_EQ(r.eax, self->host.eax); + EXPECT_EQ(r.ebx, self->host.ebx); + EXPECT_EQ(r.ecx, self->host.ecx); + EXPECT_EQ(r.edx, self->host.edx & ~self->bit); + + do_cpuid(CPUID_LEAF, &r); + EXPECT_NE(r.edx & self->bit, 0); +} + +TEST_F(ve_cpuid_override, flushed_table_falls_back_to_hardware) +{ + struct child_ctx ctx; + struct cpuid_regs r; + pid_t pid; + + ASSERT_EQ(write_cpuid_override(self->table), 0); + + pid = spawn_cpuid_child(_metadata, self, &ctx); + + ASSERT_EQ(write_cpuid_override("\n"), 0); + resume_cpuid_child(_metadata, pid); + collect_cpuid_child(_metadata, self, &ctx, &r); + + EXPECT_NE(r.edx & self->bit, 0); +} + +TEST_HARNESS_MAIN -- 2.43.0 _______________________________________________ Devel mailing list Devel@openvz.org https://lists.openvz.org/mailman/listinfo/devel