* [Devel] [PATCH hci-8.1 v4 0/5] x86/ve: CPUID override follow-ups: arm at once, prefixes, selftests
@ 2026-10-06 22:12 Konstantin Khorenko
2026-10-06 22:12 ` [Devel] [PATCH hci-8.1 v4 1/5] selftests/ve: check CPUID override emulation for VE tasks Konstantin Khorenko
` (4 more replies)
0 siblings, 5 replies; 11+ messages in thread
From: Konstantin Khorenko @ 2026-10-06 22:12 UTC (permalink / raw)
To: Pavel Tikhomirov, Vasileios Almpanis; +Cc: devel
VSTOR-148656, every Container start dying with SIGSEGV once
/proc/vz/cpuid_override is non-empty, is fixed in 22.8 by 07338fbae5a3
("x86/traps: emulate CPUID faults for user tasks in
exc_general_protection()"). Working on it turned up two more defects in
the same feature, and the fact that nothing had ever exercised it. This
series takes care of those. None of them is a 22.x regression, all have
been there since the vz9 port.
Patch 2: ve_setup_task() only sets TIF_CPUID_OVERRIDE, the MSR follows
at the next context switch. A task entering a VE through unshare() or
setns() executes CPUID natively until it is scheduled out and sees the
host values. set_cpuid_override() flips the MSR synchronously for the
current task, the way disable_cpuid() does.
Patch 4: check_cpuid_fault() compares two bytes at ip with 0f a2. A
CPUID with a legacy or REX prefix faults the same way, but the handler
sees the prefix and the task gets SIGSEGV. Use the instruction decoder,
as fixup_umip_exception() does, and advance ip by the decoded length.
Patches 1, 3, 5: selftests/ve/ve_cpuid_override_test. Each kernel patch
gets its own case. The test saves and restores the table around every
case and tears down in the parent, because the kselftest harness skips
a child-side teardown after a failed ASSERT.
Tested on sqvm2-4060, the node of the failing CI run (Broadwell VM with
cpuid_fault, cpupools-features 9.1.7, so the table is armed at every
Container start). With only the 22.8 fix, cases 3 and 4 fail the way
their patches describe: host values, and a child killed by signal 11.
With the whole series all four cases pass, repeatedly and on a
non-empty table, and the vzctl-pytest start cases from the original
ticket pass as well.
https://virtuozzo.atlassian.net/browse/VSTOR-148843
Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
Changes in v4:
- Patch 1: a child that exits before stopping is reaped by the ready
wait, so its pid is cleared there and the teardown cannot kill a
reused pid (Sashiko).
- Patch 1: spawn leaves the VE cgroup before any ASSERT can fire, so a
failed fork() no longer leaves the cgroup behind; a child killed
before it stops is logged; the teardown is described as running in
whichever process survives the ASSERT, the harness does not run it
in the parent on that path (independent review).
- Patch 1: use write_file_at() instead of a private copy, renumber the
child exit codes, say "at least" in the too-large table message.
- Patch 1: record the child pid right after fork(), so a child left
stopped by an ASSERT between fork() and the cgroup move is still
killed; say that a harness timeout leaves the test table in place
and that the second case proves nothing without the first
(independent review).
- Patch 1: name the fix the tests detect, describe the host-side and
fallback checks as the one masked bit they are, log errno when the
table read or the cgroup2 mount fails, rename the two table buffers
apart, -Wextra clean (independent review).
- Link to v3: http://lore.virtuozzo.com/20261006-b4-prep-khorenko-vstor-148656-cpuid-v3-0-16141b8ca07d@virtuozzo.com
Changes in v3:
- Patch 1: a saved table the kernel cannot take back in one write (a
page or more as printed) fails the setup instead of being silently
left behind by a failed restore; writes replace the table, so there
is no way to restore it in pieces (Sashiko).
- Patch 1: the child pid lives in the fixture and the parent-side
teardown kills a child that a failed ASSERT left stopped in the VE
cgroup, otherwise destroy_ve() fails with EBUSY (Sashiko).
- Patch 1: the Makefile keeps ARCH untouched and decides on a private
variable, lib.mk picks the clang target by ARCH being empty (Sashiko).
- Link to v2: http://lore.virtuozzo.com/20261006-b4-prep-khorenko-vstor-148656-cpuid-v2-0-f6d1192c7d60@virtuozzo.com
Changes in v2:
- Patch 1: the child stops itself with SIGSTOP after entering the VE
and the parent resumes it, so faulting is armed by a real context
switch; the pipe handshake, whose read could return without sleeping,
is gone (Sashiko).
- Patch 1: setup creates the VE cgroup last, so a failed ASSERT in
setup leaves nothing behind; a failed restore in teardown is logged
(Sashiko).
- Patch 1: drop the EAX comparison after the fallback to hardware, the
child is not pinned and hybrid CPUs may differ per core (Sashiko).
- Patch 1: build the test on x86 only, add the binary to .gitignore
(Sashiko).
- Patch 3: the no-reschedule case repeats the sequence, a tick can
still preempt the child in the window; with the handshake gone a
child that dies before reporting is reaped where the result is read
and its signal logged (Sashiko).
- Retested on sqvm2-2084 with the whole series: all cases pass and the
table is restored; on sqvm2-4060 with the stock kernel all cases fail
with the expected diagnostics.
- Link to v1: http://lore.virtuozzo.com/20261006-b4-prep-khorenko-vstor-148656-cpuid-v1-0-741609f4f03a@virtuozzo.com
---
Konstantin Khorenko (5):
selftests/ve: check CPUID override emulation for VE tasks
x86/ve: enable CPUID faulting at once when the current task enters a VE
selftests/ve: check CPUID override right after entering a VE
x86/traps: decode the faulting CPUID with the instruction decoder
selftests/ve: check CPUID override emulation for a prefixed CPUID
arch/x86/kernel/process.c | 12 +
arch/x86/kernel/traps.c | 16 +-
include/linux/cpuid_override.h | 3 +
kernel/ve/ve.c | 2 +-
tools/testing/selftests/ve/.gitignore | 1 +
tools/testing/selftests/ve/Makefile | 10 +
.../testing/selftests/ve/ve_cpuid_override_test.c | 302 +++++++++++++++++++++
7 files changed, 339 insertions(+), 7 deletions(-)
---
base-commit: f5d1fdcfffe101fe2bafa7df09d95d2e811c5c5e
change-id: 20261006-b4-prep-khorenko-vstor-148656-cpuid-a5c5993e1f83
Best regards,
--
Konstantin Khorenko <khorenko@virtuozzo.com>
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 11+ messages in thread
* [Devel] [PATCH hci-8.1 v4 1/5] selftests/ve: check CPUID override emulation for VE tasks
2026-10-06 22:12 [Devel] [PATCH hci-8.1 v4 0/5] x86/ve: CPUID override follow-ups: arm at once, prefixes, selftests Konstantin Khorenko
@ 2026-10-06 22:12 ` Konstantin Khorenko
2026-10-06 22:22 ` no-reply
2026-10-06 22:12 ` [Devel] [PATCH hci-8.1 v4 2/5] x86/ve: enable CPUID faulting at once when the current task enters a VE Konstantin Khorenko
` (3 subsequent siblings)
4 siblings, 1 reply; 11+ messages in thread
From: Konstantin Khorenko @ 2026-10-06 22:12 UTC (permalink / raw)
To: Pavel Tikhomirov, Vasileios Almpanis; +Cc: devel
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 must keep the hardware
bit. A second test flushes the table after the task was armed and
expects the hardware bit back, since faulting stays on for the task
and the emulation has to fall back to the real CPUID. On its own that
test would also pass on a kernel that never arms faulting; the first
one proves the arming.
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 commit 07338fbae5a3 ("x86/traps: emulate CPUID faults for user
tasks in exc_general_protection()") 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 harness skips FIXTURE_TEARDOWN once an ASSERT
has fired, so the cleanup is a FIXTURE_TEARDOWN_PARENT, which runs
whether or not one did; it also 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 <khorenko@virtuozzo.com>
---
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..f2883539bac3f
--- /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.
+ *
+ * The table is global: run this on a node without live Containers, a task
+ * entering a VE while a case runs is armed for life. A harness timeout
+ * kills the test without its teardown and leaves the test table in place
+ * and the VE cgroup behind.
+ */
+#define _GNU_SOURCE
+#include <sched.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/wait.h>
+
+#include "../kselftest_harness.h"
+#include "ve_selftest.h"
+
+#define CPUID_OVERRIDE_PATH "/proc/vz/cpuid_override"
+#define CPUID_LEAF 1
+#define CPUID_LINE_LEN 128
+/* The kernel takes a table back only in one write below a page. */
+#define CPUID_WRITE_MAX 4096
+#define CPUID_SAVED_MAX (CPUID_WRITE_MAX + 1)
+
+struct cpuid_regs {
+ unsigned int eax, ebx, ecx, edx;
+};
+
+/* vz builds these tests for x86-64 only; there ebx is not the PIC base. */
+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));
+}
+
+struct child_ctx {
+ int result_pipe[2];
+};
+
+/*
+ * 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.
+ */
+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 23;
+ return 0;
+}
+
+FIXTURE(ve_cpuid_override)
+{
+ int cgv2_fd;
+ int ctid;
+ struct cpuid_regs host;
+ unsigned int bit;
+ pid_t child;
+ char table[CPUID_LINE_LEN];
+ char saved[CPUID_SAVED_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) TH_LOG("%s: %s", CPUID_OVERRIDE_PATH, strerror(errno));
+ ASSERT_LT(len, CPUID_WRITE_MAX)
+ TH_LOG("a table of at least %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, 0u);
+
+ 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) TH_LOG("cgroup2 mount: %s", strerror(errno));
+ 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 after the test body in whichever process the harness picks, the test
+ * process itself after a failed ASSERT, so the table is restored whether or
+ * not one fired. The setup fd may be gone by then, hence the fresh mount.
+ */
+FIXTURE_TEARDOWN_PARENT(ve_cpuid_override)
+{
+ int cgv2_fd;
+
+ if (write_file_at(AT_FDCWD, CPUID_OVERRIDE_PATH,
+ 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();
+ if (pid == 0)
+ _exit(cpuid_child(ctx));
+ if (pid > 0)
+ self->child = pid;
+
+ /* Leave the VE cgroup before any ASSERT, or destroy_ve() gets EBUSY. */
+ ASSERT_GE(enter_cgroup(self->cgv2_fd, 0), 0);
+ ASSERT_GE(pid, 0);
+ close(ctx->result_pipe[1]);
+
+ ASSERT_GE(waitpid(pid, &status, WUNTRACED), 0);
+ if (!WIFSTOPPED(status)) {
+ /* Reaped: the pid may be reused, keep it from teardown. */
+ self->child = 0;
+ if (WIFEXITED(status))
+ TH_LOG("child exited with %d", WEXITSTATUS(status));
+ if (WIFSIGNALED(status))
+ TH_LOG("child killed by signal %d", WTERMSIG(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, (int)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_file_at(AT_FDCWD, CPUID_OVERRIDE_PATH, 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, 0u);
+}
+
+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_file_at(AT_FDCWD, CPUID_OVERRIDE_PATH, self->table), 0);
+
+ pid = spawn_cpuid_child(_metadata, self, &ctx);
+
+ ASSERT_EQ(write_file_at(AT_FDCWD, CPUID_OVERRIDE_PATH, "\n"), 0);
+ resume_cpuid_child(_metadata, pid);
+ collect_cpuid_child(_metadata, self, &ctx, &r);
+
+ EXPECT_NE(r.edx & self->bit, 0u);
+}
+
+TEST_HARNESS_MAIN
--
2.43.0
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 11+ messages in thread
* [Devel] [PATCH hci-8.1 v4 2/5] x86/ve: enable CPUID faulting at once when the current task enters a VE
2026-10-06 22:12 [Devel] [PATCH hci-8.1 v4 0/5] x86/ve: CPUID override follow-ups: arm at once, prefixes, selftests Konstantin Khorenko
2026-10-06 22:12 ` [Devel] [PATCH hci-8.1 v4 1/5] selftests/ve: check CPUID override emulation for VE tasks Konstantin Khorenko
@ 2026-10-06 22:12 ` Konstantin Khorenko
2026-10-06 22:17 ` no-reply
2026-10-06 22:12 ` [Devel] [PATCH hci-8.1 v4 3/5] selftests/ve: check CPUID override right after entering " Konstantin Khorenko
` (2 subsequent siblings)
4 siblings, 1 reply; 11+ messages in thread
From: Konstantin Khorenko @ 2026-10-06 22:12 UTC (permalink / raw)
To: Pavel Tikhomirov, Vasileios Almpanis; +Cc: devel
ve_setup_task() only sets TIF_CPUID_OVERRIDE. The MSR follows the flag
in __switch_to_xtra(), so a task that enters a VE through unshare() or
setns() keeps executing CPUID natively until it is scheduled out for the
first time and sees the host values instead of the override table.
Add set_cpuid_override(), which sets the flag and, like disable_cpuid(),
flips the CPU state synchronously when the task is the current one.
Tasks set up through clone() are not running yet and are still handled
at their first switch-in.
https://virtuozzo.atlassian.net/browse/VSTOR-148843
Feature: x86: cpu pools (cpuid override)
Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
arch/x86/kernel/process.c | 12 ++++++++++++
include/linux/cpuid_override.h | 3 +++
kernel/ve/ve.c | 2 +-
3 files changed, 16 insertions(+), 1 deletion(-)
diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c
index 28fc4168b6c9f..2673f2e51a833 100644
--- a/arch/x86/kernel/process.c
+++ b/arch/x86/kernel/process.c
@@ -23,6 +23,7 @@
#include <linux/utsname.h>
#include <linux/stackprotector.h>
#include <linux/cpuidle.h>
+#include <linux/cpuid_override.h>
#include <linux/acpi.h>
#include <linux/elf-randomize.h>
#include <linux/static_call.h>
@@ -381,6 +382,17 @@ static void enable_cpuid(void)
preempt_enable();
}
+#ifdef CONFIG_VE
+void set_cpuid_override(struct task_struct *tsk)
+{
+ preempt_disable();
+ if (!test_and_set_tsk_thread_flag(tsk, TIF_CPUID_OVERRIDE) &&
+ tsk == current)
+ set_cpuid_faulting(true);
+ preempt_enable();
+}
+#endif
+
static int get_cpuid_mode(void)
{
return !test_thread_flag(TIF_NOCPUID);
diff --git a/include/linux/cpuid_override.h b/include/linux/cpuid_override.h
index 1f5223728c9ba..83a955b628cdf 100644
--- a/include/linux/cpuid_override.h
+++ b/include/linux/cpuid_override.h
@@ -35,4 +35,7 @@ static inline bool cpuid_override_on(void)
{
return rcu_access_pointer(cpuid_override);
}
+
+struct task_struct;
+void set_cpuid_override(struct task_struct *tsk);
#endif
diff --git a/kernel/ve/ve.c b/kernel/ve/ve.c
index 579628a8a310a..8df881039c3c6 100644
--- a/kernel/ve/ve.c
+++ b/kernel/ve/ve.c
@@ -1857,7 +1857,7 @@ void ve_setup_task(struct task_struct *task, struct ve_struct *ve)
#ifdef CONFIG_X86
if (cpuid_override_on())
- set_tsk_thread_flag(task, TIF_CPUID_OVERRIDE);
+ set_cpuid_override(task);
#endif
}
EXPORT_SYMBOL(ve_setup_task);
--
2.43.0
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 11+ messages in thread
* [Devel] [PATCH hci-8.1 v4 3/5] selftests/ve: check CPUID override right after entering a VE
2026-10-06 22:12 [Devel] [PATCH hci-8.1 v4 0/5] x86/ve: CPUID override follow-ups: arm at once, prefixes, selftests Konstantin Khorenko
2026-10-06 22:12 ` [Devel] [PATCH hci-8.1 v4 1/5] selftests/ve: check CPUID override emulation for VE tasks Konstantin Khorenko
2026-10-06 22:12 ` [Devel] [PATCH hci-8.1 v4 2/5] x86/ve: enable CPUID faulting at once when the current task enters a VE Konstantin Khorenko
@ 2026-10-06 22:12 ` Konstantin Khorenko
2026-10-06 22:18 ` no-reply
2026-10-06 22:12 ` [Devel] [PATCH hci-8.1 v4 4/5] x86/traps: decode the faulting CPUID with the instruction decoder Konstantin Khorenko
2026-10-06 22:12 ` [Devel] [PATCH hci-8.1 v4 5/5] selftests/ve: check CPUID override emulation for a prefixed CPUID Konstantin Khorenko
4 siblings, 1 reply; 11+ messages in thread
From: Konstantin Khorenko @ 2026-10-06 22:12 UTC (permalink / raw)
To: Pavel Tikhomirov, Vasileios Almpanis; +Cc: devel
Add a case where the child executes CPUID straight after unshare(),
without stopping or blocking in between, and expects the table values.
Before the kernel change the override took effect only after the first
context switch, so the child saw the hardware values.
A tick can still preempt the child in that window, so the case runs
the sequence several times: a kernel that arms faulting only at the
switch has to get lucky every time.
https://virtuozzo.atlassian.net/browse/VSTOR-148843
Feature: x86: cpu pools (cpuid override)
Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
.../testing/selftests/ve/ve_cpuid_override_test.c | 62 ++++++++++++++++------
1 file changed, 45 insertions(+), 17 deletions(-)
diff --git a/tools/testing/selftests/ve/ve_cpuid_override_test.c b/tools/testing/selftests/ve/ve_cpuid_override_test.c
index f2883539bac3f..d940acc7ee792 100644
--- a/tools/testing/selftests/ve/ve_cpuid_override_test.c
+++ b/tools/testing/selftests/ve/ve_cpuid_override_test.c
@@ -31,6 +31,7 @@
#define CPUID_OVERRIDE_PATH "/proc/vz/cpuid_override"
#define CPUID_LEAF 1
#define CPUID_LINE_LEN 128
+#define NO_RESCHED_ROUNDS 20
/* The kernel takes a table back only in one write below a page. */
#define CPUID_WRITE_MAX 4096
#define CPUID_SAVED_MAX (CPUID_WRITE_MAX + 1)
@@ -49,14 +50,15 @@ static void do_cpuid(unsigned int leaf, struct cpuid_regs *r)
struct child_ctx {
int result_pipe[2];
+ int stop;
};
/*
* 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.
+ * moment, then reports its view of the leaf to the parent over a pipe. With
+ * @stop 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.
*/
static int cpuid_child(struct child_ctx *ctx)
{
@@ -66,7 +68,7 @@ static int cpuid_child(struct child_ctx *ctx)
if (unshare(CLONE_NEWVE) < 0)
return 21;
- if (raise(SIGSTOP))
+ if (ctx->stop && raise(SIGSTOP))
return 22;
do_cpuid(CPUID_LEAF, &r);
@@ -148,7 +150,7 @@ FIXTURE_TEARDOWN_PARENT(ve_cpuid_override)
close(cgv2_fd);
}
-/* Fork the child into the VE and return once it stands still there. */
+/* Fork the child into the VE; with @stop return once it stands still. */
static pid_t spawn_cpuid_child(struct __test_metadata *_metadata,
FIXTURE_DATA(ve_cpuid_override) * self,
struct child_ctx *ctx)
@@ -170,16 +172,20 @@ static pid_t spawn_cpuid_child(struct __test_metadata *_metadata,
ASSERT_GE(pid, 0);
close(ctx->result_pipe[1]);
- ASSERT_GE(waitpid(pid, &status, WUNTRACED), 0);
- if (!WIFSTOPPED(status)) {
- /* Reaped: the pid may be reused, keep it from teardown. */
- self->child = 0;
- if (WIFEXITED(status))
- TH_LOG("child exited with %d", WEXITSTATUS(status));
- if (WIFSIGNALED(status))
- TH_LOG("child killed by signal %d", WTERMSIG(status));
+ if (ctx->stop) {
+ ASSERT_GE(waitpid(pid, &status, WUNTRACED), 0);
+ if (!WIFSTOPPED(status)) {
+ /* Reaped: the pid may be reused, keep it from teardown. */
+ self->child = 0;
+ if (WIFEXITED(status))
+ TH_LOG("child exited with %d",
+ WEXITSTATUS(status));
+ if (WIFSIGNALED(status))
+ TH_LOG("child killed by signal %d",
+ WTERMSIG(status));
+ }
+ ASSERT_TRUE(WIFSTOPPED(status));
}
- ASSERT_TRUE(WIFSTOPPED(status));
return pid;
}
@@ -208,7 +214,7 @@ static void collect_cpuid_child(struct __test_metadata *_metadata,
TEST_F(ve_cpuid_override, ve_sees_table_host_sees_hardware)
{
- struct child_ctx ctx;
+ struct child_ctx ctx = { .stop = 1 };
struct cpuid_regs r;
pid_t pid;
@@ -229,7 +235,7 @@ TEST_F(ve_cpuid_override, ve_sees_table_host_sees_hardware)
TEST_F(ve_cpuid_override, flushed_table_falls_back_to_hardware)
{
- struct child_ctx ctx;
+ struct child_ctx ctx = { .stop = 1 };
struct cpuid_regs r;
pid_t pid;
@@ -244,4 +250,26 @@ TEST_F(ve_cpuid_override, flushed_table_falls_back_to_hardware)
EXPECT_NE(r.edx & self->bit, 0u);
}
+/*
+ * The child does not block between entering the VE and the CPUID, but a
+ * tick can still preempt it there. Repeat, so that a kernel which arms
+ * faulting only at the switch has to get lucky every time.
+ */
+TEST_F(ve_cpuid_override, armed_before_first_reschedule)
+{
+ struct cpuid_regs r;
+ int i;
+
+ ASSERT_EQ(write_file_at(AT_FDCWD, CPUID_OVERRIDE_PATH, self->table), 0);
+
+ for (i = 0; i < NO_RESCHED_ROUNDS; i++) {
+ struct child_ctx ctx = { .stop = 0 };
+
+ spawn_cpuid_child(_metadata, self, &ctx);
+ collect_cpuid_child(_metadata, self, &ctx, &r);
+ ASSERT_EQ(r.edx, self->host.edx & ~self->bit)
+ TH_LOG("round %d", i);
+ }
+}
+
TEST_HARNESS_MAIN
--
2.43.0
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 11+ messages in thread
* [Devel] [PATCH hci-8.1 v4 4/5] x86/traps: decode the faulting CPUID with the instruction decoder
2026-10-06 22:12 [Devel] [PATCH hci-8.1 v4 0/5] x86/ve: CPUID override follow-ups: arm at once, prefixes, selftests Konstantin Khorenko
` (2 preceding siblings ...)
2026-10-06 22:12 ` [Devel] [PATCH hci-8.1 v4 3/5] selftests/ve: check CPUID override right after entering " Konstantin Khorenko
@ 2026-10-06 22:12 ` Konstantin Khorenko
2026-10-06 22:19 ` no-reply
2026-10-06 22:12 ` [Devel] [PATCH hci-8.1 v4 5/5] selftests/ve: check CPUID override emulation for a prefixed CPUID Konstantin Khorenko
4 siblings, 1 reply; 11+ messages in thread
From: Konstantin Khorenko @ 2026-10-06 22:12 UTC (permalink / raw)
To: Pavel Tikhomirov, Vasileios Almpanis; +Cc: devel
check_cpuid_fault() reads two bytes at the faulting ip and emulates
only when they are 0f a2, the bare CPUID encoding.
CPUID has no operands, so the CPU accepts and ignores every legacy
and REX prefix in front of it. All of these are valid CPUID and raise
the same #GP(0) when CPUID faulting is on:
0f a2 cpuid
66 0f a2 data16 cpuid (operand-size override)
f3 0f a2 repz cpuid
2e 0f a2 cs cpuid (segment override)
48 0f a2 rex.W cpuid
Only the first one is recognised. For the others the handler reads
the prefix byte instead of the opcode, declines, and the task is
killed with SIGSEGV although the instruction is legal and the host
would have executed it.
Such encodings could come from hand-written and generated assembly, from
binary translators and JITs that emit prefixes for alignment or as
padding, and from any binary that disassembles as "data16 cpuid" rather
than "cpuid".
Fetch and decode the instruction with insn_fetch_from_user() and
insn_decode_from_regs(), as fixup_umip_exception() does, match the
opcode bytes and advance ip by the decoded length. The decoder derives
the linear ip from the code segment itself, so convert_ip_to_linear()
is not needed any more.
Fixes: 1033fe455434 ("arch/x86: introduce cpuid override")
https://virtuozzo.atlassian.net/browse/VSTOR-148843
Feature: x86: cpu pools (cpuid override)
Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
arch/x86/kernel/traps.c | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
index 8074387bf4f43..fa12da67adbf0 100644
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -633,8 +633,9 @@ static enum kernel_gp_hint get_kernel_gp_address(struct pt_regs *regs,
static int check_cpuid_fault(struct pt_regs *regs, long error_code)
{
- unsigned long addr;
- unsigned short opcode;
+ unsigned char buf[MAX_INSN_SIZE];
+ struct insn insn;
+ int nr_copied;
if (error_code != 0)
return 0;
@@ -642,16 +643,19 @@ static int check_cpuid_fault(struct pt_regs *regs, long error_code)
if (test_thread_flag(TIF_NOCPUID))
return 0;
- addr = convert_ip_to_linear(current, regs);
- if (get_user(opcode, (unsigned short __user *)addr))
+ nr_copied = insn_fetch_from_user(regs, buf);
+ if (nr_copied <= 0)
+ return 0;
+
+ if (!insn_decode_from_regs(&insn, regs, buf, nr_copied))
return 0;
- if (opcode != 0xa20f)
+ if (insn.opcode.bytes[0] != 0x0f || insn.opcode.bytes[1] != 0xa2)
return 0;
do_cpuid_fault(regs);
- regs->ip += 2;
+ regs->ip += insn.length;
return 1;
}
--
2.43.0
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 11+ messages in thread
* [Devel] [PATCH hci-8.1 v4 5/5] selftests/ve: check CPUID override emulation for a prefixed CPUID
2026-10-06 22:12 [Devel] [PATCH hci-8.1 v4 0/5] x86/ve: CPUID override follow-ups: arm at once, prefixes, selftests Konstantin Khorenko
` (3 preceding siblings ...)
2026-10-06 22:12 ` [Devel] [PATCH hci-8.1 v4 4/5] x86/traps: decode the faulting CPUID with the instruction decoder Konstantin Khorenko
@ 2026-10-06 22:12 ` Konstantin Khorenko
2026-10-06 22:19 ` no-reply
4 siblings, 1 reply; 11+ messages in thread
From: Konstantin Khorenko @ 2026-10-06 22:12 UTC (permalink / raw)
To: Pavel Tikhomirov, Vasileios Almpanis; +Cc: devel
Execute CPUID with an operand-size prefix, which the CPU ignores, and
expect the table values. Before the kernel change the #GP handler
matched only the bare opcode and the child died with SIGSEGV.
https://virtuozzo.atlassian.net/browse/VSTOR-148843
Feature: x86: cpu pools (cpuid override)
Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
.../testing/selftests/ve/ve_cpuid_override_test.c | 29 +++++++++++++++++++++-
1 file changed, 28 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/ve/ve_cpuid_override_test.c b/tools/testing/selftests/ve/ve_cpuid_override_test.c
index d940acc7ee792..a640f6b9f2f14 100644
--- a/tools/testing/selftests/ve/ve_cpuid_override_test.c
+++ b/tools/testing/selftests/ve/ve_cpuid_override_test.c
@@ -48,9 +48,18 @@ static void do_cpuid(unsigned int leaf, struct cpuid_regs *r)
: "a" (leaf), "c" (0));
}
+/* CPUID with an operand-size prefix, which the CPU accepts and ignores. */
+static void do_cpuid_prefixed(unsigned int leaf, struct cpuid_regs *r)
+{
+ asm volatile(".byte 0x66, 0x0f, 0xa2"
+ : "=a" (r->eax), "=b" (r->ebx), "=c" (r->ecx), "=d" (r->edx)
+ : "a" (leaf), "c" (0));
+}
+
struct child_ctx {
int result_pipe[2];
int stop;
+ int prefixed;
};
/*
@@ -71,7 +80,10 @@ static int cpuid_child(struct child_ctx *ctx)
if (ctx->stop && raise(SIGSTOP))
return 22;
- do_cpuid(CPUID_LEAF, &r);
+ if (ctx->prefixed)
+ do_cpuid_prefixed(CPUID_LEAF, &r);
+ else
+ do_cpuid(CPUID_LEAF, &r);
if (write(ctx->result_pipe[1], &r, sizeof(r)) != sizeof(r))
return 23;
@@ -272,4 +284,19 @@ TEST_F(ve_cpuid_override, armed_before_first_reschedule)
}
}
+TEST_F(ve_cpuid_override, prefixed_cpuid_is_emulated)
+{
+ struct child_ctx ctx = { .stop = 1, .prefixed = 1 };
+ struct cpuid_regs r;
+ pid_t pid;
+
+ ASSERT_EQ(write_file_at(AT_FDCWD, CPUID_OVERRIDE_PATH, 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.edx, self->host.edx & ~self->bit);
+}
+
TEST_HARNESS_MAIN
--
2.43.0
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [Devel] [PATCH hci-8.1 v4 2/5] x86/ve: enable CPUID faulting at once when the current task enters a VE
2026-10-06 22:12 ` [Devel] [PATCH hci-8.1 v4 2/5] x86/ve: enable CPUID faulting at once when the current task enters a VE Konstantin Khorenko
@ 2026-10-06 22:17 ` no-reply
0 siblings, 0 replies; 11+ messages in thread
From: no-reply @ 2026-10-06 22:17 UTC (permalink / raw)
To: Pavel Tikhomirov, Vasileios Almpanis, Konstantin Khorenko; +Cc: devel
> ve_setup_task() only sets TIF_CPUID_OVERRIDE. The MSR follows the flag
> in __switch_to_xtra(), so a task that enters a VE through unshare() or
> setns() keeps executing CPUID natively until it is scheduled out for the
> first time and sees the host values instead of the override table.
>
> Add set_cpuid_override(), which sets the flag and, like disable_cpuid(),
> flips the CPU state synchronously when the task is the current one.
> Tasks set up through clone() are not running yet and are still handled
> at their first switch-in.
>
> https://virtuozzo.atlassian.net/browse/VSTOR-148843
>
> Feature: x86: cpu pools (cpuid override)
> Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261007-b4-prep-khorenko-vstor-148656-cpuid-v4-0-bf16c29bea24@virtuozzo.com?part=2
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [Devel] [PATCH hci-8.1 v4 3/5] selftests/ve: check CPUID override right after entering a VE
2026-10-06 22:12 ` [Devel] [PATCH hci-8.1 v4 3/5] selftests/ve: check CPUID override right after entering " Konstantin Khorenko
@ 2026-10-06 22:18 ` no-reply
0 siblings, 0 replies; 11+ messages in thread
From: no-reply @ 2026-10-06 22:18 UTC (permalink / raw)
To: Pavel Tikhomirov, Konstantin Khorenko, Vasileios Almpanis; +Cc: devel
> Add a case where the child executes CPUID straight after unshare(),
> without stopping or blocking in between, and expects the table values.
> Before the kernel change the override took effect only after the first
> context switch, so the child saw the hardware values.
>
> A tick can still preempt the child in that window, so the case runs
> the sequence several times: a kernel that arms faulting only at the
> switch has to get lucky every time.
>
> https://virtuozzo.atlassian.net/browse/VSTOR-148843
>
> Feature: x86: cpu pools (cpuid override)
> Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261007-b4-prep-khorenko-vstor-148656-cpuid-v4-0-bf16c29bea24@virtuozzo.com?part=3
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [Devel] [PATCH hci-8.1 v4 4/5] x86/traps: decode the faulting CPUID with the instruction decoder
2026-10-06 22:12 ` [Devel] [PATCH hci-8.1 v4 4/5] x86/traps: decode the faulting CPUID with the instruction decoder Konstantin Khorenko
@ 2026-10-06 22:19 ` no-reply
0 siblings, 0 replies; 11+ messages in thread
From: no-reply @ 2026-10-06 22:19 UTC (permalink / raw)
To: Pavel Tikhomirov, Vasileios Almpanis, Konstantin Khorenko; +Cc: devel
> check_cpuid_fault() reads two bytes at the faulting ip and emulates
> only when they are 0f a2, the bare CPUID encoding.
>
> CPUID has no operands, so the CPU accepts and ignores every legacy
> and REX prefix in front of it. All of these are valid CPUID and raise
> the same #GP(0) when CPUID faulting is on:
>
> 0f a2 cpuid
> 66 0f a2 data16 cpuid (operand-size override)
> f3 0f a2 repz cpuid
> 2e 0f a2 cs cpuid (segment override)
> 48 0f a2 rex.W cpuid
>
> Only the first one is recognised. For the others the handler reads
> the prefix byte instead of the opcode, declines, and the task is
> [ ... ]
> Fixes: 1033fe455434 ("arch/x86: introduce cpuid override")
> https://virtuozzo.atlassian.net/browse/VSTOR-148843
>
> Feature: x86: cpu pools (cpuid override)
> Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261007-b4-prep-khorenko-vstor-148656-cpuid-v4-0-bf16c29bea24@virtuozzo.com?part=4
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [Devel] [PATCH hci-8.1 v4 5/5] selftests/ve: check CPUID override emulation for a prefixed CPUID
2026-10-06 22:12 ` [Devel] [PATCH hci-8.1 v4 5/5] selftests/ve: check CPUID override emulation for a prefixed CPUID Konstantin Khorenko
@ 2026-10-06 22:19 ` no-reply
0 siblings, 0 replies; 11+ messages in thread
From: no-reply @ 2026-10-06 22:19 UTC (permalink / raw)
To: Konstantin Khorenko, Vasileios Almpanis, Pavel Tikhomirov; +Cc: devel
> Execute CPUID with an operand-size prefix, which the CPU ignores, and
> expect the table values. Before the kernel change the #GP handler
> matched only the bare opcode and the child died with SIGSEGV.
>
> https://virtuozzo.atlassian.net/browse/VSTOR-148843
>
> Feature: x86: cpu pools (cpuid override)
> Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261007-b4-prep-khorenko-vstor-148656-cpuid-v4-0-bf16c29bea24@virtuozzo.com?part=5
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [Devel] [PATCH hci-8.1 v4 1/5] selftests/ve: check CPUID override emulation for VE tasks
2026-10-06 22:12 ` [Devel] [PATCH hci-8.1 v4 1/5] selftests/ve: check CPUID override emulation for VE tasks Konstantin Khorenko
@ 2026-10-06 22:22 ` no-reply
0 siblings, 0 replies; 11+ messages in thread
From: no-reply @ 2026-10-06 22:22 UTC (permalink / raw)
To: Konstantin Khorenko, Pavel Tikhomirov, Vasileios Almpanis; +Cc: devel
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] selftests/ve: stopped-child sync does not guarantee a context switch before SIGCONT
- [Low] selftests/ve: x86 gate admits 32-bit builds but do_cpuid() asm needs x86-64
--
commit 7b92cfc5a3a156b03d1e440c0f30bc0e6c9462a6
Author: Konstantin Khorenko <khorenko@virtuozzo.com>
selftests/ve: check CPUID override emulation for VE tasks
This adds a selftest that writes a one-entry /proc/vz/cpuid_override table
and compares the leaf 1 CPUID values seen by a task in a fresh VE with
those seen by the host. A second test flushes the table after the task was
armed and expects the hardware value back. The test is built on x86 only.
Link: https://virtuozzo.atlassian.net/browse/VSTOR-148843
> diff --git a/tools/testing/selftests/ve/Makefile b/tools/testing/selftests/ve/Makefile
> --- a/tools/testing/selftests/ve/Makefile
> +++ b/tools/testing/selftests/ve/Makefile
[ ... ]
> +# 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
[Severity: Low]
The selftests top-level Makefile maps both i386 and x86_64 to ARCH=x86, so
this gate also enables ve_cpuid_override_test for 32-bit x86 builds.
do_cpuid() in ve_cpuid_override_test.c uses an ebx output constraint, and
its own comment says it is x86-64 only because "there ebx is not the PIC
base":
asm volatile("cpuid"
: "=a" (r->eax), "=b" (r->ebx), "=c" (r->ecx), "=d" (r->edx)
: "a" (leaf), "c" (0));
Can this break the build of the whole ve selftests directory with ARCH=x86
on a 32-bit PIC or PIE toolchain, where GCC reports "can't find a register
in class BREG"? Should the gate exclude 32-bit x86?
> 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
> --- /dev/null
> +++ b/tools/testing/selftests/ve/ve_cpuid_override_test.c
[ ... ]
> + ASSERT_GE(waitpid(pid, &status, WUNTRACED), 0);
> + if (!WIFSTOPPED(status)) {
[ ... ]
> +static void resume_cpuid_child(struct __test_metadata *_metadata, pid_t pid)
> +{
> + ASSERT_EQ(kill(pid, SIGCONT), 0);
> +}
[Severity: Medium]
Does this synchronization guarantee a context switch before SIGCONT? The
waitpid(WUNTRACED) in spawn_cpuid_child() only guarantees that the stop was
reported to the parent, not that the child has been switched out.
In the kernel stop path, do_signal_stop() sets TASK_STOPPED and calls
do_notify_parent_cldstop() before it calls schedule():
cpuid_child()
unshare(CLONE_NEWVE);
raise(SIGSTOP);
do_signal_stop()
set_special_state(TASK_STOPPED);
do_notify_parent_cldstop(); <- parent's waitpid() can return here
schedule();
If the parent runs resume_cpuid_child() inside that window, SIGCONT sets
the child back to TASK_RUNNING. When the child then reaches schedule(), it
is no longer in a sleeping state and is not deactivated, so it can keep
running on the same CPU with no context switch.
The commit message says faulting is switched on "at its next context
switch" and that "a stopped task is off the CPU for sure". Without the
switch, faulting is never armed for the child.
In ve_sees_table_host_sees_hardware, the child would then see the hardware
CPUID and the EXPECT_EQ on edx would fail on a correct kernel.
In flushed_table_falls_back_to_hardware, the child would see the hardware
CPUID, which is the expected value, so the test would pass even if the
kernel never armed faulting.
The window is narrow, since the parent must wake and finish waitpid()
first, but it appears to be structurally possible. Is there something in
the test that waits for the child to actually schedule out before SIGCONT
is sent?
I did not find a later fix for this in the series. The follow-up patches
"x86/ve: enable CPUID faulting at once when the current task enters a VE"
and "selftests/ve: check CPUID override right after entering a VE" change
the kernel to arm at once and add a test without the stop. The final-state
test file still uses the same waitpid(WUNTRACED) followed by kill(SIGCONT)
synchronization for the stop-based tests.
--
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261007-b4-prep-khorenko-vstor-148656-cpuid-v4-0-bf16c29bea24@virtuozzo.com?part=1
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-10-06 22:23 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-06 22:12 [Devel] [PATCH hci-8.1 v4 0/5] x86/ve: CPUID override follow-ups: arm at once, prefixes, selftests Konstantin Khorenko
2026-10-06 22:12 ` [Devel] [PATCH hci-8.1 v4 1/5] selftests/ve: check CPUID override emulation for VE tasks Konstantin Khorenko
2026-10-06 22:22 ` no-reply
2026-10-06 22:12 ` [Devel] [PATCH hci-8.1 v4 2/5] x86/ve: enable CPUID faulting at once when the current task enters a VE Konstantin Khorenko
2026-10-06 22:17 ` no-reply
2026-10-06 22:12 ` [Devel] [PATCH hci-8.1 v4 3/5] selftests/ve: check CPUID override right after entering " Konstantin Khorenko
2026-10-06 22:18 ` no-reply
2026-10-06 22:12 ` [Devel] [PATCH hci-8.1 v4 4/5] x86/traps: decode the faulting CPUID with the instruction decoder Konstantin Khorenko
2026-10-06 22:19 ` no-reply
2026-10-06 22:12 ` [Devel] [PATCH hci-8.1 v4 5/5] selftests/ve: check CPUID override emulation for a prefixed CPUID Konstantin Khorenko
2026-10-06 22:19 ` no-reply
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox