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 E8C738017D for ; Tue, 6 Oct 2026 14:58:15 +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 696Euwdi025527; Tue, 6 Oct 2026 17:56:59 +0300 DKIM-Filter: OpenDKIM Filter v2.11.0 mail.openvz.org 696Euwdi025527 Authentication-Results: mail.openvz.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=virtuozzo.com header.i=@virtuozzo.com header.b="t95BEHaJ" 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 696EupH0025498 (version=TLSv1/SSLv3 cipher=AES128-GCM-SHA256 bits=128 verify=FAIL) for ; Tue, 6 Oct 2026 17:56:51 +0300 DKIM-Filter: OpenDKIM Filter v2.11.0 mail.openvz.org 696EupH0025498 Received: by mail-wr1-f71.google.com with SMTP id ffacd0b85a97d-48c511d9e97so1603523f8f.0 for ; Tue, 06 Oct 2026 07:56:51 -0700 (PDT) X-Forwarded-Encrypted: i=2; AKwUvBxhXsThWPCP6huY6V3ODkbylHbBEDf+Fbv7PZFjEDWtgySvueq74HcXv5f88PjUEyf+fJfj7w==@openvz.org X-Gm-Message-State: AFq9FYIPJ77HQ+43ab9XEr/6v3PSd9fOqkZ9pTp1zKvwGHlYClTsgvou DdlIlq4+FnnSmU7nEsXc0JtO/arUALcy9S1k61y5K14Q9/RbTgf59CPXVtGSaNQNhIvR/dXpy4k ls+C2TYvO6IN7/pjyiKbsURh8XKzEm7yUFCJTO1JKtqeZO/UsI0wazQ== X-Gm-Gg: AYBFou2FdUWFyFO+bDonKJ5KpOfILNNzAV7dUxDMrxObrw4ysQRi25s9LBNTrwdXCkw 8eKwoyePgSuifSFyLktehNS4vqFnUZMFBMekWOJF7RzT5lkpFK00aOJEC1NtjyIhssS2+s76gdW Kusgm+RMYO1/jxm5ZcBpIJWtfA2KPu74nRdN8hMjXMySwjlRK1J0VR6FL67yVGYIatDBoHb10q1 Xd1s51z85R0ddtwEbVdRPBKIEmM6U8T1eQJpIxjs+29t5vOcUQiS+rKeq1wD037zNDFyZxj/Xi8 Of9liq4vGUtqWCdz9FPPexbvFOdF66OHoDhe6waOuGqScgD9FiaUE98sIstY3m9A3OpCQVdBkqp s+bYT2YEuT3xUv4rQ X-Received: by 2002:a05:6000:2384:b0:48b:d9:cb52 with SMTP id ffacd0b85a97d-48c6d1787fcmr2724464f8f.27.1791298611510; Tue, 06 Oct 2026 07:56:51 -0700 (PDT) X-Received: by 2002:a05:6000:2384:b0:48b:d9:cb52 with SMTP id ffacd0b85a97d-48c6d1787fcmr2724404f8f.27.1791298610880; Tue, 06 Oct 2026 07:56:50 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1791298610; cv=none; d=google.com; s=arc-20260327; b=TTlYXTRxz6Jl95LcyqSiX/wbbp2BWFTaEoXw5/xSsydt3hX5pxnmY3kZlBKdyDq0PK 97G2c1qbveY1ecn8Vyv3bunOpvK57eMaG11pKY8/7Dvck80tsOt7AVEhQUmjGGBDGXOx BEfofdxwUJi6nk+nubnWKYRq8HwaJnNKrnX0gYzLI06+oG6etDRuQEAEg3J4jJYhlpQs jRySxazBWQe9RI8WMWE6cBTEu/N0P3HSKNT31Ud2ShJg4w5a1uzTRzRzDD9nY6ryAZ24 FCY9hOTFBjfNLxJN+pctdlF8Jmlap7db64I+jGCtAImfI2OVZ8v3oTn1oN6wc0VzzjZ8 6Y9w== 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=ffyw+c2sMXN+514A3qjoEHRi+Us50YdoeY8SnLyN5/k=; fh=bRkZfFa6377EgOVW8zBDri2gGIQqe/ZeiH+ijEM/ROI=; b=BZMZ842yf029jrLE8B1XIXLycQd/c0Ejj38a73gaufk6tj7Lv67hwuinZUPDZnCH3j HNuspEX9fhbGQHD6RehqnDdsc4ju/M4dsiy1RixfW1ulovLeP1a8tancVraqZKcOZKJN TobMhastYlJc4bNSgdAquHdOXSbuZOZyzJ1pa0LkGjC4YtAO5Fk1QehclVPn6bbySKCx cNB4wtwT4pbop2oaNFER/j5UM3OTDcR23ZfhfAqH7XnABSICM4kfb25wymcON+xQZKnR AumJbCqJehBn6Fr+jKMQpvqN+nCj9eunqYLcHJZSgnDi6Zu9MnIeUnUzKPALc3HXgLZR S5wQ==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@virtuozzo.com header.s=relay header.b=t95BEHaJ; 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-48b5b315031si25710454f8f.63.2026.10.06.07.56.50 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 06 Oct 2026 07:56:50 -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=t95BEHaJ; 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=ffyw+c2sMXN+514A3qjoEHRi+Us50YdoeY8SnLyN5/k=; b=t95BEHaJAdVAyBMjMMK 4E6bRtoekSM7WQboCaDjedIVAs0EQRmlguzTDndJ/4QT8mIF8giTbTgn6L4iyA6I6vIs2JhPemM9P Mi7RkHuROFlLrXa4uOtyDqq7iZe/oQGXNZfVvTmouEQqTj2TOlGbT6wg1IlJ2NidoXsJLTpK0hxrV SZDO+Q0RVL/U+2Og70EgRBUZDjng/jlqqH1VJFl6sTa0tnP8xTbmlm/lTyjHi9eagPDdpSsbbj716 CLwiUvtXAce/Kpk2e6j/u8scknma3L/CmVj/efT92ZTpYSbeRkHmv3u7fjxgqNyyRag9OtFAmir+4 rU+ba44c1nvf9qQ==; 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 1xE6Xp-00DosW-2C; Tue, 06 Oct 2026 16:56:40 +0200 From: Konstantin Khorenko Date: Tue, 06 Oct 2026 16:56:34 +0200 MIME-Version: 1.0 Message-Id: <20261006-b4-prep-khorenko-vstor-148656-cpuid-v1-1-741609f4f03a@virtuozzo.com> References: <20261006-b4-prep-khorenko-vstor-148656-cpuid-v1-0-741609f4f03a@virtuozzo.com> In-Reply-To: <20261006-b4-prep-khorenko-vstor-148656-cpuid-v1-0-741609f4f03a@virtuozzo.com> To: Pavel Tikhomirov , Vasileios Almpanis X-Mailer: b4 0.16.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=8084; i=khorenko@virtuozzo.com; h=from:subject:message-id; bh=Z9fOwr7gYsI6xjOAxNe4qXWcJ0/+sRuCVeckhMUVpcw=; b=owEBbQKS/ZANAwAKAVGWCkf5YVwqAcsmYgBqxQwwxa9d/To12RLoAhwjHPkiZdegx/pmrAC9g o060XJGKzyJAjMEAAEKAB0WIQRWD23IPcXT0GO9CCxRlgpH+WFcKgUCasUMMAAKCRBRlgpH+WFc KimWD/9L7ZGj9MqoN/nCjaSvN+I7M9PyEj1kUmBZ79knB0iqJUaIIM0lf1GrQ8799k7kAaUeqDc vDm+nM0UjBh2/lGQ6CMon94qbAIz6aPEcNs7xk34C8phDKFMfbaxxxCMK/my04aqlQS6HXqHFDZ 21f+vUZvJNApnsVYWiKrFAoXcAbYqPS4VSSKcItowwKMwOf2ELlFPpjVMBrS+0eXqv3jdsG3Lt3 pzR0NuAVmuhmMdnbtG9YoFWMLQqP3cusFrZOamABTcQf7zrfKZHBPuxlx2yTmL9/uixHFqpVWq2 J+SI7Nb+IeyWV7oDsQZxXe5y4Tx4J7B/gMNgcD+dNfSlqX3k0UTvrrFS2XDLa8wbfvk2EqGdrg2 N7mzGkz9SHfWfV2Me9xVakmXLg8Zq3Ixs6EFMV6cQ+SdJMz/gULAZWkQZ5lS+CiVOSo5IH8JvXi KFg7cGkDFhiW/h3LdTtmJCxVfQsvr2ST2HZGUdRMtKT44qWPgMEujA14wNnjTdeFdxlcm09/AKz TvQ9Y4Iz1VaZwMUP/6wU9nhRWzNEp2FGXAebjjOtTmqNjxxGyglVNSYSG72r4RzcDY6cqg9XzQb AW1nVF9b1EhHIqemip+huuBe+9X/JuIYGONo0750WmCjr0V7W7C9UzcYB9D8cB/S0N+9mrTlbkx aD5IioRQWojzJ5Q== 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 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 blocks before executing CPUID: faulting is switched on for a task at its next context switch, not when the flag is set. Without the fix both children die with SIGSEGV. The tests skip on CPUs without CPUID faulting and restore the previous table on exit. The teardown runs in the parent because the harness skips a child-side teardown once an ASSERT has fired. https://virtuozzo.atlassian.net/browse/VSTOR-148843 Feature: x86: cpu pools (cpuid override) Signed-off-by: Konstantin Khorenko --- tools/testing/selftests/ve/Makefile | 1 + .../testing/selftests/ve/ve_cpuid_override_test.c | 227 +++++++++++++++++++++ 2 files changed, 228 insertions(+) diff --git a/tools/testing/selftests/ve/Makefile b/tools/testing/selftests/ve/Makefile index c6efe7c4b4fba..e7932408b1305 100644 --- a/tools/testing/selftests/ve/Makefile +++ b/tools/testing/selftests/ve/Makefile @@ -5,5 +5,6 @@ CFLAGS += -g -Wall -O2 TEST_GEN_PROGS += ve_ns_owner_test TEST_GEN_PROGS += ve_perms_test TEST_GEN_PROGS += ve_mount_accounting_test +TEST_GEN_PROGS += ve_cpuid_override_test 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..c066d67d72a82 --- /dev/null +++ b/tools/testing/selftests/ve/ve_cpuid_override_test.c @@ -0,0 +1,227 @@ +// 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 +#define CPUID_TABLE_MAX 4096 + +struct cpuid_regs { + unsigned int eax, ebx, ecx, edx; +}; + +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 + * 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. + */ +struct child_ctx { + int result_pipe[2]; + int go_pipe[2]; +}; + +static int cpuid_child(struct child_ctx *ctx) +{ + struct cpuid_regs r; + char go; + + close(ctx->result_pipe[0]); + close(ctx->go_pipe[1]); + + 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; + + 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; + char table[CPUID_TABLE_LEN]; + char saved[CPUID_TABLE_MAX]; +}; + +FIXTURE_SETUP(ve_cpuid_override) +{ + int fd; + + fd = open(CPUID_OVERRIDE_PATH, O_RDONLY); + if (fd < 0) + SKIP(return, "%s: %s", CPUID_OVERRIDE_PATH, strerror(errno)); + close(fd); + + ASSERT_GE(read_file_at(AT_FDCWD, CPUID_OVERRIDE_PATH, self->saved, + sizeof(self->saved)), 0); + + 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); + + /* 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); +} + +/* + * 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; + + write_cpuid_override(self->saved[0] ? self->saved : "\n"); + + cgv2_fd = mount_cg2_fd(); + if (cgv2_fd < 0) + return; + destroy_ve(cgv2_fd, self->ctid); + close(cgv2_fd); +} + +static pid_t spawn_cpuid_child(struct __test_metadata *_metadata, + int cgv2_fd, int ctid, struct child_ctx *ctx) +{ + char ready; + pid_t pid; + + ASSERT_EQ(pipe(ctx->result_pipe), 0); + ASSERT_EQ(pipe(ctx->go_pipe), 0); + ASSERT_GE(enter_cgroup(cgv2_fd, ctid), 0); + + pid = fork(); + ASSERT_GE(pid, 0); + if (pid == 0) + _exit(cpuid_child(ctx)); + + ASSERT_GE(enter_cgroup(cgv2_fd, 0), 0); + close(ctx->result_pipe[1]); + close(ctx->go_pipe[0]); + + ASSERT_EQ(read(ctx->result_pipe[0], &ready, 1), 1); + ASSERT_EQ(ready, 'R'); + return pid; +} + +static void collect_cpuid_child(struct __test_metadata *_metadata, pid_t pid, + struct child_ctx *ctx, struct cpuid_regs *r) +{ + int status, len; + + len = read(ctx->result_pipe[0], r, sizeof(*r)); + ASSERT_GE(waitpid(pid, &status, 0), 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]); + close(ctx->go_pipe[1]); +} + +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->cgv2_fd, self->ctid, &ctx); + ASSERT_EQ(write(ctx.go_pipe[1], "G", 1), 1); + collect_cpuid_child(_metadata, pid, &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->cgv2_fd, self->ctid, &ctx); + + ASSERT_EQ(write_cpuid_override("\n"), 0); + ASSERT_EQ(write(ctx.go_pipe[1], "G", 1), 1); + collect_cpuid_child(_metadata, pid, &ctx, &r); + + EXPECT_EQ(r.eax, self->host.eax); + 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