[Devel] [PATCH hci-8.1 3/5] selftests/ve: check CPUID override right after entering a VE

Konstantin Khorenko khorenko at virtuozzo.com
Tue Oct 6 17:56:36 MSK 2026


Add a case where the child executes CPUID straight after unshare(),
without blocking or waking anyone 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.

https://virtuozzo.atlassian.net/browse/VSTOR-148843

Feature: x86: cpu pools (cpuid override)
Signed-off-by: Konstantin Khorenko <khorenko at virtuozzo.com>
---
 .../testing/selftests/ve/ve_cpuid_override_test.c  | 42 ++++++++++++++++------
 1 file changed, 32 insertions(+), 10 deletions(-)

diff --git a/tools/testing/selftests/ve/ve_cpuid_override_test.c b/tools/testing/selftests/ve/ve_cpuid_override_test.c
index c066d67d72a82..be2b808f1c77e 100644
--- a/tools/testing/selftests/ve/ve_cpuid_override_test.c
+++ b/tools/testing/selftests/ve/ve_cpuid_override_test.c
@@ -54,13 +54,14 @@ static int write_cpuid_override(const char *table)
 
 /*
  * 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
- * blocks between the two steps: faulting is switched on for the task at its
- * next context switch, and the parent may change the table meanwhile.
+ * moment, then reports its view of the leaf to the parent over a pipe. With
+ * @wait_go it blocks between the two steps, which lets the parent change
+ * the table meanwhile and forces a context switch before the CPUID.
  */
 struct child_ctx {
 	int result_pipe[2];
 	int go_pipe[2];
+	int wait_go;
 };
 
 static int cpuid_child(struct child_ctx *ctx)
@@ -73,12 +74,18 @@ static int cpuid_child(struct child_ctx *ctx)
 
 	if (unshare(CLONE_NEWVE) < 0)
 		return 21;
-	if (write(ctx->result_pipe[1], "R", 1) != 1)
-		return 22;
-	if (read(ctx->go_pipe[0], &go, 1) != 1)
-		return 23;
-
+	if (ctx->wait_go) {
+		if (write(ctx->result_pipe[1], "R", 1) != 1)
+			return 22;
+		if (read(ctx->go_pipe[0], &go, 1) != 1)
+			return 23;
+	}
+
+	/* Without wait_go nothing wakes the parent before the CPUID. */
 	do_cpuid(CPUID_LEAF, &r);
+
+	if (!ctx->wait_go && write(ctx->result_pipe[1], "R", 1) != 1)
+		return 22;
 	if (write(ctx->result_pipe[1], &r, sizeof(r)) != sizeof(r))
 		return 24;
 	return 0;
@@ -187,7 +194,7 @@ static void collect_cpuid_child(struct __test_metadata *_metadata, pid_t pid,
 
 TEST_F(ve_cpuid_override, ve_sees_table_host_sees_hardware)
 {
-	struct child_ctx ctx;
+	struct child_ctx ctx = { .wait_go = 1 };
 	struct cpuid_regs r;
 	pid_t pid;
 
@@ -208,7 +215,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 = { .wait_go = 1 };
 	struct cpuid_regs r;
 	pid_t pid;
 
@@ -224,4 +231,19 @@ TEST_F(ve_cpuid_override, flushed_table_falls_back_to_hardware)
 	EXPECT_NE(r.edx & self->bit, 0);
 }
 
+/* No context switch between entering the VE and the CPUID. */
+TEST_F(ve_cpuid_override, armed_before_first_reschedule)
+{
+	struct child_ctx ctx = { .wait_go = 0 };
+	struct cpuid_regs r;
+	pid_t pid;
+
+	ASSERT_EQ(write_cpuid_override(self->table), 0);
+
+	pid = spawn_cpuid_child(_metadata, self->cgv2_fd, self->ctid, &ctx);
+	collect_cpuid_child(_metadata, pid, &ctx, &r);
+
+	EXPECT_EQ(r.edx, self->host.edx & ~self->bit);
+}
+
 TEST_HARNESS_MAIN

-- 
2.43.0



More information about the Devel mailing list