[Devel] [PATCH hci-8.1 v3 3/5] selftests/ve: check CPUID override right after entering a VE
Konstantin Khorenko
khorenko at virtuozzo.com
Tue Oct 6 23:44:34 MSK 2026
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)
---
.../testing/selftests/ve/ve_cpuid_override_test.c | 52 ++++++++++++++++------
1 file changed, 39 insertions(+), 13 deletions(-)
diff --git a/tools/testing/selftests/ve/ve_cpuid_override_test.c b/tools/testing/selftests/ve/ve_cpuid_override_test.c
index 0e3537099ddb2..9d2d5ddd32201 100644
--- a/tools/testing/selftests/ve/ve_cpuid_override_test.c
+++ b/tools/testing/selftests/ve/ve_cpuid_override_test.c
@@ -27,6 +27,7 @@
#define CPUID_OVERRIDE_PATH "/proc/vz/cpuid_override"
#define CPUID_LEAF 1
#define CPUID_TABLE_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_TABLE_MAX (2 * CPUID_WRITE_MAX)
@@ -57,13 +58,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
- * 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.
*/
struct child_ctx {
int result_pipe[2];
+ int stop;
};
static int cpuid_child(struct child_ctx *ctx)
@@ -74,7 +76,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);
@@ -155,7 +157,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)
@@ -175,11 +177,13 @@ static pid_t spawn_cpuid_child(struct __test_metadata *_metadata,
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));
+ if (ctx->stop) {
+ 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;
}
@@ -208,7 +212,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 +233,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 +248,26 @@ TEST_F(ve_cpuid_override, flushed_table_falls_back_to_hardware)
EXPECT_NE(r.edx & self->bit, 0);
}
+/*
+ * 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_cpuid_override(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
More information about the Devel
mailing list