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 F026680726 for ; Tue, 6 Oct 2026 17:20:46 +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 696HJQC5026648; Tue, 6 Oct 2026 20:19:27 +0300 DKIM-Filter: OpenDKIM Filter v2.11.0 mail.openvz.org 696HJQC5026648 Authentication-Results: mail.openvz.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=virtuozzo.com header.i=@virtuozzo.com header.b="b1XYQV7i" Received: from mail-wr1-f69.google.com (mail-wr1-f69.google.com [209.85.221.69]) by mail.openvz.org (8.14.4/8.14.4) with ESMTP id 696HJJPB026618 (version=TLSv1/SSLv3 cipher=AES128-GCM-SHA256 bits=128 verify=FAIL) for ; Tue, 6 Oct 2026 20:19:19 +0300 DKIM-Filter: OpenDKIM Filter v2.11.0 mail.openvz.org 696HJJPB026618 Received: by mail-wr1-f69.google.com with SMTP id ffacd0b85a97d-48c69bdeaacso861705f8f.0 for ; Tue, 06 Oct 2026 10:19:19 -0700 (PDT) X-Forwarded-Encrypted: i=2; AKwUvBwBhBm5L811K5PRtnt70DWwr7Mi+sO16AA9LPwJoojfJhMhplyza/GNsRMmNvYz+z+uqdCxhw==@openvz.org X-Gm-Message-State: AFq9FYIAAmz96Bx0dPF9SkqQ+INfYtdC+2TFePRlQ4RIA/kR9TftBi5K NnC8m0ylbrDnjNj6xXfsrzcEqum5YsT06fwIVQc3cuFSGfcQMMPeJbppzdOSYqBL1wFxDqgUSAY N28KCmkC0AQZb24ChqJxGtdUeVMcg5ztyuFgjuPT4b/adjxHhA+QEvA== X-Gm-Gg: AYBFou0T3SwhmOFUcNjdrHtIJU/QzYsikJrhBTchpdpUvszhyv/N5p9P5PYRTvLefpp S722TxJktItrYG1PBB17Bf3o9Hv6vk8SDOiFUdMoYdz9J5A4bJXEZuVSEHW0kZ9gwrs0sujdqkW fkizfgQKGNDl4aXIB5lc8h5ugY5+r16TxHa/mAcFLazz8X0VV1F3GUETtn88ksjxYzLXmRID1+u Z8N8yPz/2gEZeYUUELPSTVCfvRqZfnYwJ+x5CiqmnSUULFvvK4xU39OA90yowXgEA0zBt3Nh4wm R/fAMSB+DIY2z4GVbM5n1Q/d9Dm/tSgSrfaGK8YFOw1xqh5PhEaBHn8g9XzLD+iLTeVeYP3uH/1 SrITNkN/vsYO3AEQ+qA== X-Received: by 2002:a05:6000:40ce:b0:48c:466e:9d08 with SMTP id ffacd0b85a97d-48c7289e920mr217381f8f.43.1791307158837; Tue, 06 Oct 2026 10:19:18 -0700 (PDT) X-Received: by 2002:a05:6000:40ce:b0:48c:466e:9d08 with SMTP id ffacd0b85a97d-48c7289e920mr217353f8f.43.1791307158326; Tue, 06 Oct 2026 10:19:18 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1791307158; cv=none; d=google.com; s=arc-20260327; b=fiAC+XIWu0hGbw/K7cLiRa5PwNXbVloztV0SisHy9ZxVZGnok3C0oG0inmkF4EcEUw AtFuDCm7ht6Hi8Dcu8UerV5WInBql/t0fVbFexCWDJZCZ/dKIRIwhP5t3dZnXAkE+pnq PSFKQWgtsDxjQQKFHIMFvj1mwGXGwQLSLXsvJ6DEXfT2mYl/MLdgd2bA6FI3u4hdSryA 7GJQto7z5jV+NWQkIYcnbN8TG6FhgPRZECim5JboGGaleg4eX0A8nJ1H/73F6eNlMO2n STSjPWULHFHx3hMcH2+ne+9AcAnKQdjbsdytrYDQuXwRY+UrF5EijUFIg0tkzXLeSA9e kZ+w== 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=wgOtMNN69ceYo31W9jAndwO8MKMCTRxbPgXzOn9ua+s=; fh=bRkZfFa6377EgOVW8zBDri2gGIQqe/ZeiH+ijEM/ROI=; b=lKsBmy985qUyigKNQgDnYZdbm7qmZvc/vvZln5Y56Fo2dATRkxhFiC58hrLWUuz2+U uPju5U4m2zfM28arWRweFFdrZhrwOnIivAZMDMFfeebOhQt7juFP1YQCzGML4HisiSgI pcflq8wa5s/Wp3ywBQa3zTKnJiTdbcMUg8zdQT7YVWEofNkjENmG2Tm9xh1U10pxgLlM jnSSxmmr7wdMV5m7nhe4XMtM57x1gxaK+tLdykk9sDdHH5pr2m8H4CUP71Eu3NwlFebL hgvRkv+Xpa63H8U91MQTtbyU6v6k0rGCaf7EJIZvhlWw0hX2e9AAZMQ20pKyaIhooCIf 0g3A==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@virtuozzo.com header.s=relay header.b=b1XYQV7i; 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-48c72467832si378132f8f.53.2026.10.06.10.19.18 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 06 Oct 2026 10:19:18 -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=b1XYQV7i; 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=wgOtMNN69ceYo31W9jAndwO8MKMCTRxbPgXzOn9ua+s=; b=b1XYQV7iU58BP4/0fDu Wea96t56GPBPKUdSW5GSmIEZ3vK3vYUDjueBeXY0jRB/s9dkDgMnILdU6gbjlePXcjlR7tqpD5Fkr G8Jd0SuGZM5MPDl+HxUCmER/lCDnJwqo8MAODreRa+2lFEqFVQwg7HuiJ9bnR8z9/UJIe39Z0CDNX gSSVPx73jZTWy8qwlOdGEbs7EUxxXXnThuBGN+J+vFSZAkWokUR1ihiRn19Ro3iW7aP9sFinPQaTh SkaOEMYS5OGhlOBCLNOzpWk5RH6wLzAfJ5wCzMK9tV2MUXJgVsiixd0EDJ1L8iHbZhyks9ufkmPiI iA7T3/SXhwhnzfA==; 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 1xE8lg-00GXnn-3B; Tue, 06 Oct 2026 19:19:07 +0200 From: Konstantin Khorenko Date: Tue, 06 Oct 2026 19:19:10 +0200 MIME-Version: 1.0 Message-Id: <20261006-b4-prep-khorenko-vstor-148656-cpuid-v2-1-f6d1192c7d60@virtuozzo.com> References: <20261006-b4-prep-khorenko-vstor-148656-cpuid-v2-0-f6d1192c7d60@virtuozzo.com> In-Reply-To: <20261006-b4-prep-khorenko-vstor-148656-cpuid-v2-0-f6d1192c7d60@virtuozzo.com> To: Pavel Tikhomirov , Vasileios Almpanis X-Mailer: b4 0.16.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=9214; i=khorenko@virtuozzo.com; h=from:subject:message-id; bh=r+i03GsLKHca6fqEy641CwbBElY6gvcnYusfDau4n1A=; b=owEBbQKS/ZANAwAKAVGWCkf5YVwqAcsmYgBqxS2Tjh9STxRVap9kv84xL8fMCfmHIa0Pu1dGl VgU78ioR/qJAjMEAAEKAB0WIQRWD23IPcXT0GO9CCxRlgpH+WFcKgUCasUtkwAKCRBRlgpH+WFc KgklD/0afiea7W2nhPjEs69FBINKsWPaSKHbzKDH7EF/g2myGwl3Tfryw77+IFRw32y1cC3LNwp 1KOUGOWSDtnrlp0iUbS2VHrW0GDm1B5LBvFcL2XAe0jJWIRgkllvP6zduRE0j5XBxijO+Ul7jJc 5eBcN+aOUrJqeuzrxUQ1ZtK48SfHnQz7fNh65ZpzmGgzhJMHtjNLZv/jU0CetP43FHsV6YI3UVW rWZGZjDmfF9iNDvjRSFIAhBGt6hIZi1WOKzvvcMnsOSeSMCgxbNg+xGQzaVrP5SvowuRl39upI8 RbOldKyyI49iPYJvWkpJ8xU+w+EKucA6xYp7lI2HVrVDNgndVsCFV2IeWbe0rVg8R4LM4hyC/um Hg5jiASVkuI74BrrVHPqZhg7x2LjgfAgr3YgA9gcwc/ZwQwrekAHl+IVsU1OkMyfp8gT5PCrH5A ipD6JJ8PP+ict2QIPm7D9SGBGMH3XvFXyD/S5lRLxCN1y9YZck9ifB+oKkYCfOlP15L8NelV2iX HjLCLeNJRBp95eC3i0L+xTW5fpRnpuYRfP8lU1ZAr98snso2M1thJwXdsBYYfVdqPlrPL3oJB0x Jg/HdU8V7PPvGezqUa8DMtOA3wv0yKC3AoGfEfg5YO7bwr46jAhGRD3tH7CZtfazs3WXHWqVNIs LLCl2PqjM3pKTSg== 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 v2 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. The teardown runs in the parent because the harness skips a child-side teardown once an ASSERT has fired. 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 | 7 + .../testing/selftests/ve/ve_cpuid_override_test.c | 236 +++++++++++++++++++++ 3 files changed, 244 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..cd50a86fdb226 100644 --- a/tools/testing/selftests/ve/Makefile +++ b/tools/testing/selftests/ve/Makefile @@ -6,4 +6,11 @@ TEST_GEN_PROGS += ve_ns_owner_test TEST_GEN_PROGS += ve_perms_test TEST_GEN_PROGS += ve_mount_accounting_test +ifeq ($(ARCH),) +ARCH := $(shell uname -m 2>/dev/null || echo not) +endif +ifeq ($(ARCH),$(filter $(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..291b88404a53e --- /dev/null +++ b/tools/testing/selftests/ve/ve_cpuid_override_test.c @@ -0,0 +1,236 @@ +// 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 +/* 128 entries of up to 67 characters each, as the kernel prints them. */ +#define CPUID_TABLE_MAX 16384 + +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; + 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, sizeof(self->saved) - 1); + + /* 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)); + + 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, + int cgv2_fd, int ctid, struct child_ctx *ctx) +{ + int status; + pid_t pid; + + ASSERT_EQ(pipe(ctx->result_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]); + + 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, 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]); +} + +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); + resume_cpuid_child(_metadata, pid); + 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); + resume_cpuid_child(_metadata, pid); + collect_cpuid_child(_metadata, pid, &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