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 851E78015D for ; Wed, 9 Sep 2026 21:37:26 +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 689LaFNj024111; Thu, 10 Sep 2026 00:36:15 +0300 DKIM-Filter: OpenDKIM Filter v2.11.0 mail.openvz.org 689LaFNj024111 Authentication-Results: mail.openvz.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=virtuozzo.com header.i=@virtuozzo.com header.b="ShncoVff" Received: from mail-wm1-f70.google.com (mail-wm1-f70.google.com [209.85.128.70]) by mail.openvz.org (8.14.4/8.14.4) with ESMTP id 689LZhFN024046 (version=TLSv1/SSLv3 cipher=AES128-GCM-SHA256 bits=128 verify=FAIL) for ; Thu, 10 Sep 2026 00:35:43 +0300 DKIM-Filter: OpenDKIM Filter v2.11.0 mail.openvz.org 689LZhFN024046 Received: by mail-wm1-f70.google.com with SMTP id 5b1f17b1804b1-495569acf8dso37764655e9.1 for ; Wed, 09 Sep 2026 14:35:43 -0700 (PDT) X-Gm-Message-State: AFuF++mDnQ88komjc+0lvc1+2UBhSfyPcsYdbChgYAuB25z0UZSKAyhT 7LJvoWDxiAIXtV/7apDKtx1yeHz4DuS0RR8ig1dWtRXxg63YR7KV0g/RWHApL/VNuc21wj2p9Uy LDrrbxuPPVIkWRivhz241bF9C1cdgb5tFQuYySDJ39YmtLaT0k607EA== X-Gm-Gg: AYBFou3y/QbX+2DlazTaRUazG1nyqZP6Bw9WfyeBElgVigQ1InCCJxuhCpC9sEhZGWE TjGkjVOL4M11vKvWzmc9okgaJdg9KN+GJZZ/uwO5A+iTXuwgDFROTvymrafE6FEsYNu3H9SS659 EqrJ73a4INfscG4GXXuc4z5PjTQP/N+UdOcuPZl7ygVv1qNMFIHIe+lJz8evDua4ccPshvXPQEG HUXDoUYeP/axgeqL6UfdnoFNY1wAXSf6EpBkR8feLmCNvFE8iLIdbjYTPTPmjGj0xy8LsMHH1jo wm0S4nTIL1PAsuq4vWtv/j3ZsXx0ouh26DhSMWIncPqmbVtkREe4jKJ2VCHEwjYjjST9XHTom10 hnpR4HMwcldHtGDZ1QxnqRdeVvL8h X-Received: by 2002:a05:600c:3543:b0:49c:e3c3:5efd with SMTP id 5b1f17b1804b1-49d1f354321mr117708375e9.9.1788989743430; Wed, 09 Sep 2026 14:35:43 -0700 (PDT) X-Received: by 2002:a05:600c:3543:b0:49c:e3c3:5efd with SMTP id 5b1f17b1804b1-49d1f354321mr117707945e9.9.1788989742844; Wed, 09 Sep 2026 14:35:42 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1788989742; cv=none; d=google.com; s=arc-20260327; b=FWfZvd3Zsu83VUqK99PPKFz6h7MaSt1jdSksrTmzSbWwkEKeV3xf4bt/XOtkgjg34L uZPbEs5RpZ6/qKb9W1mE/G0C5V4hZqO0J5Unh1O5zRPX4Loi6xR6Gjo6Q5Dc6iPvjmwm 6w4L1WUTyFAhBEE098mf0ralje8cwDklJZ9QKedMaKbjGpyp4T20wluRswYcbZMSBznQ 4k8TbOHxqvXJvtopfB6lFkVyzNkAQ+44rzIjvyZpWrUB+F7QpS0nMyzN+Vz6jJQz2gLq KM8l5sYPrJkfkaf9ZpTS+qei++YF/VpIXLDvIz7yw7KXYWoT43MNqqkAo045mvx26g7W BCpw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20260327; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:dkim-signature; bh=DputqMvRMYndDkDb3FjXmI1S6DSyupOWjbdKWMuQAZ4=; fh=QQvtjDaAlLft5IR2jnjma4lVp8TaaKU12WD0fWdFifQ=; b=TTNH42k2ZGKIgPTu37MGo1tVzd6JDhdE+EGh+ADa+0XHrlZfKbe2ew8/zcJKHjXaFH s9iNWF7Bs9TZFHqo5e2IUmak/NuchziPzejne98IuHKyc2VUp/SCTxENyTRUlrX+IR2l pakOIYaPpQj8igXn0xLkTmAoZP1vfUBLROvAOQjwwj5Jf26H1V+Uyui30wxkuvBjDNu4 Vp7Xcv5R3+XERge77MpHXCOnWpST/apmZDWA9b/iww1v+LviUEjMlTzfUmqvB4R4VP1I D94tvEBP4g5oTAlt8B9cBV8gaCOPpVb6vfrllRtdjO1RF1OYKCTjcsjbvSJIDFtzuNdq gygQ==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@virtuozzo.com header.s=relay header.b=ShncoVff; spf=pass (google.com: domain of vladimir.riabchun@virtuozzo.com designates 130.117.225.111 as permitted sender) smtp.mailfrom=vladimir.riabchun@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-485abf6a406si6747599f8f.305.2026.09.09.14.35.42 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 09 Sep 2026 14:35:42 -0700 (PDT) Received-SPF: pass (google.com: domain of vladimir.riabchun@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=ShncoVff; spf=pass (google.com: domain of vladimir.riabchun@virtuozzo.com designates 130.117.225.111 as permitted sender) smtp.mailfrom=vladimir.riabchun@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=MIME-Version:Message-ID:Date:Subject:From: Content-Type; bh=DputqMvRMYndDkDb3FjXmI1S6DSyupOWjbdKWMuQAZ4=; b=ShncoVffTT4T tEb35u0n+tYzSJnNAlRfz10JFSPfLx/qqq+s30dtcr9VPe/q0ChPPiFLU/GNEgYkOL+4ERxu1lUqr LRuvlZ3innKxrH/9/YGSdfIKaNzGOifRQaFyH7WSadHlRpnmWfOhkIrl0mCbJrtYipV6AoPOQzJvv irxrExmR497tGtqu0JNl7W3+mRuXQE4Fnxiu2COnsYHGd5snjbH11JB171Hjte93X1chADsfo3DFm zhZPMc11MAwv6Iu3M2rqcPG1BEbGckl5JJo08HmrWujutxpvqPofGSo1ULBgvXT2OAu/KdfDdjkDe lqiScXMQhOTDIkobCAQ6wA==; Received: from [130.117.225.5] (helo=dev012.aci.vzint.dev) by relay.virtuozzo.com with esmtps (TLS1.3) tls TLS_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1x4Pug-00DVmc-0l; Wed, 09 Sep 2026 23:35:41 +0200 From: Vladimir Riabchun To: devel@openvz.org Date: Wed, 9 Sep 2026 21:35:29 +0000 Message-ID: <8cdef39deacb456104771458f4f75d1fae3d38f9.1788989664.git.vladimir.riabchun@virtuozzo.com> X-Mailer: git-send-email 2.47.1 In-Reply-To: References: MIME-Version: 1.0 X-OZ-Fwd: true Subject: [Devel] [PATCH VZ10 v8 7/7] selftests/ve: Add mount accounting selftest 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 There are 6 test cases, covered in the new test: 1. Simple mount accouting correctness, just mount/umount. 2. Verification of correct limit hits and changes, including negative values. 3. Partial mounts test, when mount limit is hit in the middle of creation. 4. Test that enabled pseudosuper allows overuse. 5. Test that pseudosuper doesn't affect mount accounting. 6. Failcount feature verification. https://virtuozzo.atlassian.net/browse/VSTOR-135520 Feature: per-ve failcounters Signed-off-by: Vladimir Riabchun --- tools/testing/selftests/ve/.gitignore | 1 + tools/testing/selftests/ve/Makefile | 1 + .../selftests/ve/ve_mount_accounting_test.c | 489 ++++++++++++++++++ 3 files changed, 491 insertions(+) create mode 100644 tools/testing/selftests/ve/ve_mount_accounting_test.c diff --git a/tools/testing/selftests/ve/.gitignore b/tools/testing/selftests/ve/.gitignore index afa4c568c2c9..3df4d05888dc 100644 --- a/tools/testing/selftests/ve/.gitignore +++ b/tools/testing/selftests/ve/.gitignore @@ -1,2 +1,3 @@ ve_ns_owner_test ve_perms_test +ve_mount_accounting_test diff --git a/tools/testing/selftests/ve/Makefile b/tools/testing/selftests/ve/Makefile index ec40cbc7b3a1..c6efe7c4b4fb 100644 --- a/tools/testing/selftests/ve/Makefile +++ b/tools/testing/selftests/ve/Makefile @@ -4,5 +4,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 include ../lib.mk diff --git a/tools/testing/selftests/ve/ve_mount_accounting_test.c b/tools/testing/selftests/ve/ve_mount_accounting_test.c new file mode 100644 index 000000000000..e82d9bf23839 --- /dev/null +++ b/tools/testing/selftests/ve/ve_mount_accounting_test.c @@ -0,0 +1,489 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ve_mount_accounting selftests + * + * Tests to check the correctness of mount accounting. + */ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../kselftest_harness.h" +#include "ve_selftest.h" + +#define TMP_DIR "/ve-mnt-tmp/" +#define VE_MOUNTS_MAX 128 + +/* + * These values take a while to calculate, the first test will + * do this and store the values for those who come after. + */ +int *need_avail; +int *mnt_cost; + +FIXTURE(ve_mnt_acc) +{ + int cgv2_fd; + int ctid; + struct namespace_ctx ns_ctx; + int orig_have_mnt; +}; + +static int set_pseudosuper(FIXTURE_DATA(ve_mnt_acc) * self, int value) +{ + char path[64]; + + snprintf(path, sizeof(path), "%d/ve.pseudosuper", self->ctid); + return write_u64_at(self->cgv2_fd, path, value); +} + +static int _create_mount(void *id_ptr) +{ + char path[PATH_MAX]; + int id = *(int *)id_ptr, ret; + + snprintf(path, sizeof(path), TMP_DIR "%d", id); + + if (mkdir(path, 0755) < 0) { + fprintf(stderr, "Failed to create directory %s: %s\n", path, strerror(errno)); + return -1; + } + ret = mount("tmpfs", path, "tmpfs", 0, "size=1M"); + if (!ret) + return 0; + fprintf(stderr, "Failed to mount tmpfs to %s: %s\n", path, strerror(errno)); + + rmdir(path); + return -1; +} + +static int create_mount(FIXTURE_DATA(ve_mnt_acc) * self, int id) +{ + int ret = run_in_ve(self->cgv2_fd, self->ctid, &self->ns_ctx, + CLONE_NEWVE | CLONE_NEWNS, _create_mount, &id); + /* + * If mount fails, cleanup by free_vfsmnt will be called + * via call_rcu, need to wait for update. + */ + sleep(1); + return ret; +} + +static int _destroy_mount(void *id_ptr) +{ + char path[PATH_MAX]; + struct stat st; + int id = *(int *)id_ptr; + + snprintf(path, sizeof(path), TMP_DIR "%d", id); + + if (stat(path, &st)) + return 1; + if (umount(path)) { + fprintf(stderr, "failed to umount directory %s: %s\n", path, strerror(errno)); + return -1; + } + if (rmdir(path)) { + fprintf(stderr, "failed to remove directory %s: %s\n", path, strerror(errno)); + return -1; + } + return 0; +} + +static int destroy_mount(FIXTURE_DATA(ve_mnt_acc) * self, int id) +{ + int ret = run_in_ve(self->cgv2_fd, self->ctid, &self->ns_ctx, + CLONE_NEWVE | CLONE_NEWNS, _destroy_mount, &id); + /* free_vfsmnt is called via call_rcu, need to wait for update */ + sleep(1); + return ret; +} + +#define MAX_MNT_ID 32 + +static int get_free_mnt_id(void) +{ + int i; + struct stat st; + char path[PATH_MAX]; + + for (i = 0; i < MAX_MNT_ID; i++) { + snprintf(path, sizeof(path), TMP_DIR "%d", i); + if (stat(path, &st)) + return i; + } + return -1; +} + +static int get_mount_cost(FIXTURE_DATA(ve_mnt_acc) * self) +{ + int avail1, avail2, mnt_id; + char path[64]; + + mnt_id = get_free_mnt_id(); + + snprintf(path, sizeof(path), "%d/ve.mnt_avail_nr", self->ctid); + if (mnt_id < 0 || + read_s32_at(self->cgv2_fd, path, &avail1) || + create_mount(self, mnt_id) || + read_s32_at(self->cgv2_fd, path, &avail2) || + destroy_mount(self, mnt_id)) + return -1; + + return avail1 - avail2; +} + +/* + * Current kernel code first creates many mounts with ve_owner != ve0, + * so this VE needs more mounts than would be really consumed after commit_tree. + */ +static int get_required_avail_mnts(FIXTURE_DATA(ve_mnt_acc) * self) +{ + int l = 0, r = VE_MOUNTS_MAX + 1, mid; + char path_max_nr[64]; + + snprintf(path_max_nr, sizeof(path_max_nr), "%d/ve.mnt_max_nr", self->ctid); + + while (r - l > 1) { + mid = (l + r) / 2; + if (write_u64_at(self->cgv2_fd, path_max_nr, mid + self->orig_have_mnt)) + return -1; + + if (get_mount_cost(self) < 0) + l = mid; + else + r = mid; + } + if (r == VE_MOUNTS_MAX + 1) { + fprintf(stderr, "%s: %d avail mounts is not enough\n", __func__, r); + return -1; + } + if (write_u64_at(self->cgv2_fd, path_max_nr, VE_MOUNTS_MAX)) + return -1; + return r; +} + +/* Expect mount success and return new avail value */ +static int mount_and_get_avail(struct __test_metadata *_metadata, + FIXTURE_DATA(ve_mnt_acc) * self, int mnt_id) +{ + char path_avail[64]; + int mnt_avail_nr; + + snprintf(path_avail, sizeof(path_avail), "%d/ve.mnt_avail_nr", self->ctid); + + ASSERT_EQ(create_mount(self, mnt_id), 0); + ASSERT_EQ(read_s32_at(self->cgv2_fd, path_avail, &mnt_avail_nr), 0); + return mnt_avail_nr; +} + +/* Expect mount failure and ensure intact avail number */ +static int check_mount_fails(struct __test_metadata *_metadata, + FIXTURE_DATA(ve_mnt_acc) * self, + int mnt_id, int avail_count) +{ + char path_avail[64]; + int mnt_avail_nr; + + snprintf(path_avail, sizeof(path_avail), "%d/ve.mnt_avail_nr", self->ctid); + + if (read_s32_at(self->cgv2_fd, path_avail, &mnt_avail_nr)) + return 1; + if (mnt_avail_nr != avail_count) { + fprintf(stderr, "%s: mnt_avail(%d) != avail_count(%d) before mount\n", + __func__, mnt_avail_nr, avail_count); + return -1; + } + + if (create_mount(self, mnt_id) == 0) { + fprintf(stderr, "%s: successful mount\n", __func__); + return -1; + } + + if (read_s32_at(self->cgv2_fd, path_avail, &mnt_avail_nr)) + return 1; + if (mnt_avail_nr != avail_count) { + fprintf(stderr, "%s: mnt_avail(%d) != avail_count(%d) after mount\n", + __func__, mnt_avail_nr, avail_count); + return -1; + } + return 0; +} + +FIXTURE_SETUP(ve_mnt_acc) +{ + int initial_mnt_avail_nr; + char path[64]; + + self->cgv2_fd = mount_cg2_fd(); + ASSERT_GE(self->cgv2_fd, 0); + mkdir(TMP_DIR, 0755); + + 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); + + snprintf(path, sizeof(path), "%d/ve.mnt_max_nr", self->ctid); + ASSERT_EQ(write_u64_at(self->cgv2_fd, path, VE_MOUNTS_MAX), 0); + + /* + * The new ve cgroup has not been entered by anything yet, so its + * mnt_avail_nr counter should be VE_MOUNTS_MAX. + */ + snprintf(path, sizeof(path), "%d/ve.mnt_avail_nr", self->ctid); + ASSERT_EQ(read_s32_at(self->cgv2_fd, path, &initial_mnt_avail_nr), 0); + ASSERT_EQ(initial_mnt_avail_nr, VE_MOUNTS_MAX); + + ASSERT_EQ(prepare_namespaces(&self->ns_ctx, CLONE_NEWNS | CLONE_NEWVE, + self->cgv2_fd, self->ctid), 0); + + ASSERT_EQ(read_s32_at(self->cgv2_fd, path, &initial_mnt_avail_nr), 0); + self->orig_have_mnt = VE_MOUNTS_MAX - initial_mnt_avail_nr; + + /* Calculate once - it is time-consuming */ + if (*need_avail == -1) + *need_avail = get_required_avail_mnts(self); + if (*mnt_cost == -1) + *mnt_cost = get_mount_cost(self); + ASSERT_GE(*need_avail, 1); + ASSERT_GE(*mnt_cost, 1); +}; + +FIXTURE_TEARDOWN(ve_mnt_acc) +{ + for (int i = 0; i < MAX_MNT_ID; i++) + _destroy_mount((void *)&i); + + close_namespace_fds(&self->ns_ctx); + destroy_ve(self->cgv2_fd, self->ctid); + close(self->cgv2_fd); + rmdir(TMP_DIR); +} + +/* Simple test to check mount/umount accounting correctness */ +TEST_F(ve_mnt_acc, mount_umount) +{ + int original_mnt_avail, mnt_avail_nr; + char path[64]; + + snprintf(path, sizeof(path), "%d/ve.mnt_avail_nr", self->ctid); + + ASSERT_EQ(read_s32_at(self->cgv2_fd, path, &original_mnt_avail), 0); + + ASSERT_LT(mount_and_get_avail(_metadata, self, 0), + original_mnt_avail); + + ASSERT_EQ(destroy_mount(self, 0), 0); + ASSERT_EQ(read_s32_at(self->cgv2_fd, path, &mnt_avail_nr), 0); + ASSERT_EQ(mnt_avail_nr, original_mnt_avail); +} + +/* Test mount limit hits */ +TEST_F(ve_mnt_acc, hit_limits) +{ + int mnt_avail_nr; + char path_avail[64], path_max_nr[64]; + + snprintf(path_avail, sizeof(path_avail), "%d/ve.mnt_avail_nr", self->ctid); + snprintf(path_max_nr, sizeof(path_max_nr), "%d/ve.mnt_max_nr", self->ctid); + + /* Step 1: reduce number of available mounts to *need_avail */ + ASSERT_EQ(write_u64_at(self->cgv2_fd, path_max_nr, + self->orig_have_mnt + 1 * (*need_avail)), 0); + ASSERT_EQ(read_s32_at(self->cgv2_fd, path_avail, &mnt_avail_nr), 0); + ASSERT_EQ(mnt_avail_nr, 1 * *need_avail); + + /* Step 2: do one mount, no mounts should be available */ + ASSERT_EQ(mount_and_get_avail(_metadata, self, 0), *need_avail - *mnt_cost); + + /* Step 3: check that one more mount fails */ + ASSERT_EQ(check_mount_fails(_metadata, self, 1, (*need_avail) - 1), 0); + + /* Step 4: increase by 1 and win now */ + ASSERT_EQ(write_u64_at(self->cgv2_fd, path_max_nr, + self->orig_have_mnt + *mnt_cost + *need_avail), 0); + ASSERT_EQ(read_s32_at(self->cgv2_fd, path_avail, &mnt_avail_nr), 0); + ASSERT_EQ(mnt_avail_nr, *need_avail); + + ASSERT_EQ(mount_and_get_avail(_metadata, self, 1), *need_avail - *mnt_cost); + + /* Step 5: reduce mnt_max_nr so we have more mounts than allowed */ + ASSERT_EQ(write_u64_at(self->cgv2_fd, path_max_nr, + self->orig_have_mnt + 1 * (*mnt_cost)), 0); + ASSERT_EQ(read_s32_at(self->cgv2_fd, path_avail, &mnt_avail_nr), 0); + ASSERT_EQ(mnt_avail_nr, -1 * (*mnt_cost)); + + /* Step 6: try to do mount when avail < 0, ensure number is intact */ + ASSERT_EQ(check_mount_fails(_metadata, self, 2, -1 * (*mnt_cost)), 0); + + /* Step 7: remove one mount, check avail value update, mount should fail */ + ASSERT_EQ(destroy_mount(self, 0), 0); + ASSERT_EQ(check_mount_fails(_metadata, self, 2, 0), 0); + + /* Step 8: remove one more mount and check that new mount succeeds */ + ASSERT_EQ(destroy_mount(self, 1), 0); + ASSERT_EQ(read_s32_at(self->cgv2_fd, path_avail, &mnt_avail_nr), 0); + ASSERT_EQ(mnt_avail_nr, 1 * *mnt_cost); + ASSERT_EQ(write_u64_at(self->cgv2_fd, path_max_nr, self->orig_have_mnt + *need_avail), 0); + ASSERT_EQ(mount_and_get_avail(_metadata, self, 2), *need_avail - *mnt_cost); + + ASSERT_EQ(destroy_mount(self, 2), 0); +} + +/* + * Mount propagation makes one mount cost more. + * This test checks that if we run out of mounts in the middle of creating + * a new one, everything is restored smoothly and nothing leaks. + */ +TEST_F(ve_mnt_acc, partial_mounts) +{ + char path_avail[64], path_max_nr[64]; + int i; + + snprintf(path_max_nr, sizeof(path_max_nr), "%d/ve.mnt_max_nr", self->ctid); + snprintf(path_avail, sizeof(path_avail), "%d/ve.mnt_avail_nr", self->ctid); + + if (*need_avail == 1) + SKIP(return, "mount cost is 1, no partial mounts possible"); + + for (i = 0; i < *need_avail; i++) { + ASSERT_EQ(write_u64_at(self->cgv2_fd, path_max_nr, self->orig_have_mnt + i), 0); + ASSERT_EQ(check_mount_fails(_metadata, self, 0, i), 0); + } +} + +/* Test that pseudosuper allows negative avail with correct accounting. */ +TEST_F(ve_mnt_acc, pseudosuper_allows_overuse) +{ + char path_max_nr[64]; + + snprintf(path_max_nr, sizeof(path_max_nr), "%d/ve.mnt_max_nr", self->ctid); + + ASSERT_EQ(write_u64_at(self->cgv2_fd, path_max_nr, self->orig_have_mnt + *need_avail), 0); + ASSERT_EQ(set_pseudosuper(self, 1), 0); + + ASSERT_EQ(mount_and_get_avail(_metadata, self, 0), *need_avail - *mnt_cost); + ASSERT_EQ(mount_and_get_avail(_metadata, self, 1), *need_avail - 2 * (*mnt_cost)); + + /* Go below zero */ + ASSERT_EQ(write_u64_at(self->cgv2_fd, path_max_nr, self->orig_have_mnt), 0); + ASSERT_EQ(mount_and_get_avail(_metadata, self, 2), -3 * (*mnt_cost)); + + ASSERT_EQ(set_pseudosuper(self, 0), 0); + + /* Below zero should fail */ + ASSERT_EQ(check_mount_fails(_metadata, self, 3, -3 * (*mnt_cost)), 0); + /* Now need 2 more to add a new mount */ + ASSERT_EQ(write_u64_at(self->cgv2_fd, path_max_nr, + self->orig_have_mnt + 3 * (*mnt_cost) + *need_avail - 2 * (*mnt_cost)), 0); + ASSERT_EQ(check_mount_fails(_metadata, self, 3, *need_avail - 2 * (*mnt_cost)), 0); + + ASSERT_EQ(destroy_mount(self, 0), 0); + ASSERT_EQ(check_mount_fails(_metadata, self, 3, *need_avail - *mnt_cost), 0); + ASSERT_EQ(destroy_mount(self, 2), 0); + ASSERT_EQ(get_mount_cost(self), *mnt_cost); +} + +/* Test that pseudosuper doesn't disable accounting. */ +TEST_F(ve_mnt_acc, pseudosuper_continues_accounting) +{ + int orig_mnt_avail, mnt_avail_nr; + char path_avail[64]; + + snprintf(path_avail, sizeof(path_avail), "%d/ve.mnt_avail_nr", self->ctid); + ASSERT_EQ(read_s32_at(self->cgv2_fd, path_avail, &orig_mnt_avail), 0); + + ASSERT_EQ(set_pseudosuper(self, 0), 0); + + /* mnt 0 - mounted without pseudosuper, umounted with it. */ + ASSERT_EQ(mount_and_get_avail(_metadata, self, 0), orig_mnt_avail - *mnt_cost); + ASSERT_EQ(set_pseudosuper(self, 1), 0); + + /* Cost is the same when mount/umount happen under pseudosuper. */ + ASSERT_EQ(get_mount_cost(self), *mnt_cost); + + /* mnt 1 - mounted with pseudosuper, umounted without it. */ + ASSERT_EQ(mount_and_get_avail(_metadata, self, 1), + orig_mnt_avail - 2 * (*mnt_cost)); + + ASSERT_EQ(destroy_mount(self, 0), 0); + ASSERT_EQ(read_s32_at(self->cgv2_fd, path_avail, &mnt_avail_nr), 0); + ASSERT_EQ(mnt_avail_nr, orig_mnt_avail - *mnt_cost); + + ASSERT_EQ(set_pseudosuper(self, 0), 0); + + ASSERT_EQ(destroy_mount(self, 1), 0); + ASSERT_EQ(read_s32_at(self->cgv2_fd, path_avail, &mnt_avail_nr), 0); + ASSERT_EQ(mnt_avail_nr, orig_mnt_avail); +} + +/* Test failcount feature */ +TEST_F(ve_mnt_acc, failcount) +{ + char path_fc[64], failcount_str[512], path_max_nr[64]; + + snprintf(path_fc, sizeof(path_fc), "%d/ve.failcount", self->ctid); + snprintf(path_max_nr, sizeof(path_max_nr), "%d/ve.mnt_max_nr", self->ctid); + + ASSERT_GE(read_file_at(self->cgv2_fd, path_fc, + failcount_str, sizeof(failcount_str)), 0); + ASSERT_TRUE(strstr(failcount_str, "mnt: 0\n") != NULL); + + /* Check successful mount doesn't affect failcount */ + mount_and_get_avail(_metadata, self, 0); + ASSERT_GE(read_file_at(self->cgv2_fd, path_fc, + failcount_str, sizeof(failcount_str)), 0); + ASSERT_TRUE(strstr(failcount_str, "mnt: 0\n") != NULL); + ASSERT_EQ(destroy_mount(self, 0), 0); + + /* Check failcount update when mount fails */ + ASSERT_EQ(write_u64_at(self->cgv2_fd, path_max_nr, 0), 0); + ASSERT_LT(create_mount(self, 1), 0); + ASSERT_GE(read_file_at(self->cgv2_fd, path_fc, + failcount_str, sizeof(failcount_str)), 0); + ASSERT_TRUE(strstr(failcount_str, "mnt: 1\n") != NULL); + + /* Check failcount flush */ + ASSERT_EQ(write_u64_at(self->cgv2_fd, path_fc, 0), 0); + ASSERT_GE(read_file_at(self->cgv2_fd, path_fc, + failcount_str, sizeof(failcount_str)), 0); + ASSERT_TRUE(strstr(failcount_str, "mnt: 0\n") != NULL); + + /* Check failcount update when mount fails again */ + ASSERT_EQ(write_u64_at(self->cgv2_fd, path_max_nr, 0), 0); + ASSERT_LT(create_mount(self, 1), 0); + ASSERT_GE(read_file_at(self->cgv2_fd, path_fc, + failcount_str, sizeof(failcount_str)), 0); + ASSERT_TRUE(strstr(failcount_str, "mnt: 1\n") != NULL); +} + +int main(int argc, char **argv) +{ + int *shared_data = mmap(NULL, sizeof(int) * 2, + PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + + if (!shared_data) { + fprintf(stderr, "Failed to mmap shared region: %s\n", strerror(errno)); + return 1; + } + need_avail = &shared_data[0]; + *need_avail = -1; + mnt_cost = &shared_data[1]; + *mnt_cost = -1; + + return test_harness_run(argc, argv); +} -- 2.47.1 _______________________________________________ Devel mailing list Devel@openvz.org https://lists.openvz.org/mailman/listinfo/devel