* [Devel] [PATCH hci-8.1 v10 01/13] ve/ve.{h, c}: farewell to spaces as indents
2026-09-25 16:53 [Devel] [PATCH hci-8.1 v10 00/13] Add per-VE failcount support Konstantin Khorenko
@ 2026-09-25 16:53 ` Konstantin Khorenko
2026-09-25 16:53 ` [Devel] [PATCH hci-8.1 v10 02/13] ve/fs: count available mounts instead of used ones Konstantin Khorenko
` (13 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Konstantin Khorenko @ 2026-09-25 16:53 UTC (permalink / raw)
To: Vasileios Almpanis, Pavel Tikhomirov,
cover.1789491989.git.vladimir.riabchun, Konstantin Khorenko,
devel
From: Vladimir Riabchun <vladimir.riabchun@virtuozzo.com>
Fix formatting while here.
Signed-off-by: Vladimir Riabchun <vladimir.riabchun@virtuozzo.com>
Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
include/linux/ve.h | 8 ++++----
kernel/ve/ve.c | 48 ++++++++++++++++++++++++------------------------
2 files changed, 28 insertions(+), 28 deletions(-)
diff --git a/include/linux/ve.h b/include/linux/ve.h
index 2dc93e42784b2..f946abe4d8596 100644
--- a/include/linux/ve.h
+++ b/include/linux/ve.h
@@ -71,7 +71,7 @@ struct ve_struct {
void *log_state;
#define VE_LOG_BUF_LEN 4096
- struct kstat_lat_pcpu_struct sched_lat_ve;
+ struct kstat_lat_pcpu_struct sched_lat_ve;
int odirect_enable;
int fsync_enable;
@@ -108,8 +108,8 @@ struct ve_struct {
struct kthread_worker *kthreadd_worker;
struct task_struct *kthreadd_task;
- atomic_t umh_running_helpers;
- struct wait_queue_head umh_helpers_waitq;
+ atomic_t umh_running_helpers;
+ struct wait_queue_head umh_helpers_waitq;
struct kthread_worker umh_worker;
struct task_struct *umh_task;
@@ -130,7 +130,7 @@ struct ve_struct {
struct ve_devmnt {
struct list_head link;
- dev_t dev;
+ dev_t dev;
char *allowed_options;
char *hidden_options; /* balloon_ino, etc. */
};
diff --git a/kernel/ve/ve.c b/kernel/ve/ve.c
index b2788b80e7655..e3ae2e5d9a86c 100644
--- a/kernel/ve/ve.c
+++ b/kernel/ve/ve.c
@@ -84,8 +84,8 @@ struct ve_struct ve0 = {
.nd_neigh_nr = ATOMIC_INIT(0),
.mnt_nr = ATOMIC_INIT(0),
.meminfo_val = VE_MEMINFO_SYSTEM,
- .umh_running_helpers = ATOMIC_INIT(0),
- .umh_helpers_waitq = __WAIT_QUEUE_HEAD_INITIALIZER(ve0.umh_helpers_waitq),
+ .umh_running_helpers = ATOMIC_INIT(0),
+ .umh_helpers_waitq = __WAIT_QUEUE_HEAD_INITIALIZER(ve0.umh_helpers_waitq),
#ifdef CONFIG_X86
.vdso_64 = (struct vdso_image*)&vdso_image_64,
.vdso_32 = (struct vdso_image*)&vdso_image_32,
@@ -289,7 +289,7 @@ EXPORT_SYMBOL(is_ve_init_net);
int nr_threads_ve(struct ve_struct *ve)
{
- return cgroup_task_count(ve->css.cgroup);
+ return cgroup_task_count(ve->css.cgroup);
}
EXPORT_SYMBOL(nr_threads_ve);
@@ -330,8 +330,8 @@ static void ve_drop_context(struct ve_struct *ve)
ve_nsproxy = rcu_dereference_protected(ve->ve_nsproxy, lockdep_is_held(&ve->op_sem));
/* Allows to dereference init_cred and init_task if ve_nsproxy is set */
- rcu_assign_pointer(ve->ve_nsproxy, NULL);
- synchronize_rcu();
+ rcu_assign_pointer(ve->ve_nsproxy, NULL);
+ synchronize_rcu();
put_nsproxy(ve_nsproxy);
put_cred(ve->init_cred);
@@ -895,23 +895,23 @@ static int ve_state_show(struct seq_file *sf, void *v)
struct ve_struct *ve = css_to_ve(css);
down_read(&ve->op_sem);
- switch (ve->state) {
- case VE_STATE_STARTING:
- seq_puts(sf, "STARTING");
- break;
- case VE_STATE_RUNNING:
- seq_puts(sf, "RUNNING");
- break;
- case VE_STATE_STOPPING:
- seq_puts(sf, "STOPPING");
- break;
- case VE_STATE_STOPPED:
- case VE_STATE_DEAD:
- seq_puts(sf, "STOPPED");
- break;
- default:
- seq_puts(sf, "UNKNOWN");
- }
+ switch (ve->state) {
+ case VE_STATE_STARTING:
+ seq_puts(sf, "STARTING");
+ break;
+ case VE_STATE_RUNNING:
+ seq_puts(sf, "RUNNING");
+ break;
+ case VE_STATE_STOPPING:
+ seq_puts(sf, "STOPPING");
+ break;
+ case VE_STATE_STOPPED:
+ case VE_STATE_DEAD:
+ seq_puts(sf, "STOPPED");
+ break;
+ default:
+ seq_puts(sf, "UNKNOWN");
+ }
seq_putc(sf, '\n');
up_read(&ve->op_sem);
@@ -923,7 +923,7 @@ static ssize_t ve_state_write(struct kernfs_open_file *of, char *buf,
{
struct cgroup_subsys_state *css = of_css(of);
- struct ve_struct *ve = css_to_ve(css);
+ struct ve_struct *ve = css_to_ve(css);
int ret = -EINVAL;
if (!strcmp(buf, "START")) {
@@ -1354,7 +1354,7 @@ static int ve_parse_mount_options(const char *ptr, const char *endp,
goto next;
}
- options = kmalloc(options_size, GFP_KERNEL);
+ options = kmalloc(options_size, GFP_KERNEL);
if (!options)
return -ENOMEM;
--
2.43.0
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 16+ messages in thread* [Devel] [PATCH hci-8.1 v10 02/13] ve/fs: count available mounts instead of used ones
2026-09-25 16:53 [Devel] [PATCH hci-8.1 v10 00/13] Add per-VE failcount support Konstantin Khorenko
2026-09-25 16:53 ` [Devel] [PATCH hci-8.1 v10 01/13] ve/ve.{h, c}: farewell to spaces as indents Konstantin Khorenko
@ 2026-09-25 16:53 ` Konstantin Khorenko
2026-09-25 16:53 ` [Devel] [PATCH hci-8.1 v10 03/13] selftests/ve: update ve_ns_owner_test Konstantin Khorenko
` (12 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Konstantin Khorenko @ 2026-09-25 16:53 UTC (permalink / raw)
To: Vasileios Almpanis, Pavel Tikhomirov,
cover.1789491989.git.vladimir.riabchun, Konstantin Khorenko,
devel
From: Vladimir Riabchun <vladimir.riabchun@virtuozzo.com>
Previous approach with current mounts counter had an issue:
there was a gap between ve_mount_allowed check and ve_mount_nr_inc,
which could allow CT to have more mounts than expected.
Fix this by tracking the number of available mounts instead
of current ones. This also makes resources accounting
more consistent - we are using ***_avail_nr approach more.
One more issue with inconsistent ve value is fixed:
ve_mount_allowed always used ve from get_exec_env, but
ve_mount_nr_inc operated with owner_ve.
Now actual ve value is calculated in the beginning of alloc_vfsmnt.
To avoid incorrect accounting when is_pseudosuper is changed,
update avail_nr count without > 0 check if VE is ve0 or pseudosuper.
This also simplifies ve_mount_put, since increment is
now unconditional.
https://virtuozzo.atlassian.net/browse/VSTOR-135520
Feature: per-ve failcounters
Signed-off-by: Vladimir Riabchun <vladimir.riabchun@virtuozzo.com>
Reviewed-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
fs/namespace.c | 78 ++++++++++++++++++++++++++++++++----------------------
include/linux/ve.h | 2 +-
kernel/ve/ve.c | 12 ++++-----
3 files changed, 54 insertions(+), 38 deletions(-)
diff --git a/fs/namespace.c b/fs/namespace.c
index 4adc1db84b4c0..1d6e66cd71745 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -317,19 +317,22 @@ int mnt_get_count(struct mount *mnt)
#endif
}
-static inline int ve_mount_allowed(void);
-static inline void ve_mount_nr_inc(struct mount *mnt, struct ve_struct *ve);
-static inline void ve_mount_nr_dec(struct mount *mnt);
+static inline int ve_try_reserve_mount(struct ve_struct *ve);
+static inline void ve_mount_put(struct mount *mnt, struct ve_struct *ve);
static inline void ve_mount_reown(struct mount *mnt, struct ve_struct *ve);
static struct mount *alloc_vfsmnt(const char *name, struct ve_struct *owner_ve)
{
struct mount *mnt;
+ struct ve_struct *ve = owner_ve;
- if (!ve_mount_allowed()) {
+ if (!ve)
+ ve = get_exec_env();
+
+ if (!ve_try_reserve_mount(ve)) {
pr_warn_ratelimited(
"CT#%s reached the limit on mounts.\n",
- ve_name(get_exec_env()));
+ ve_name(ve));
return NULL;
}
@@ -337,6 +340,14 @@ static struct mount *alloc_vfsmnt(const char *name, struct ve_struct *owner_ve)
if (mnt) {
int err;
+#ifdef CONFIG_VE
+ /*
+ * Got ve reference in ve_try_reserve_mount, set mnt ve data
+ * here, so in case of error ve_mount_put sees correct info.
+ */
+ mnt->ve_owner = ve;
+#endif
+
err = mnt_alloc_id(mnt);
if (err)
goto out_free_cache;
@@ -371,7 +382,8 @@ static struct mount *alloc_vfsmnt(const char *name, struct ve_struct *owner_ve)
INIT_LIST_HEAD(&mnt->mnt_umounting);
INIT_HLIST_HEAD(&mnt->mnt_stuck_children);
mnt->mnt.mnt_idmap = &nop_mnt_idmap;
- ve_mount_nr_inc(mnt, owner_ve);
+ } else {
+ ve_mount_put(mnt, ve);
}
return mnt;
@@ -382,6 +394,8 @@ static struct mount *alloc_vfsmnt(const char *name, struct ve_struct *owner_ve)
out_free_id:
mnt_free_id(mnt);
out_free_cache:
+ /* Got ve reference in ve_try_reserve_mount */
+ ve_mount_put(mnt, ve);
kmem_cache_free(mnt_cache, mnt);
return NULL;
}
@@ -751,7 +765,9 @@ int sb_prepare_remount_readonly(struct super_block *sb)
static void free_vfsmnt(struct mount *mnt)
{
mnt_idmap_put(mnt_idmap(&mnt->mnt));
- ve_mount_nr_dec(mnt);
+#ifdef CONFIG_VE
+ ve_mount_put(mnt, mnt->ve_owner);
+#endif
kfree_const(mnt->mnt_devname);
#ifdef CONFIG_SMP
free_percpu(mnt->mnt_pcp);
@@ -3214,7 +3230,7 @@ int ve_devmnt_process(struct ve_struct *ve, dev_t dev, void **data_pp, int remou
if (devmnt->dev == dev) {
err = ve_devmnt_check(data, devmnt->allowed_options);
/*
- * In case of @is_pseudouser set, ie restore procedure,
+ * In case of @is_pseudosuper set, ie restore procedure,
* we don't check for allowed options filtering, since
* restore mode is special.
*/
@@ -3353,30 +3369,30 @@ int ve_devmnt_verify(struct ve_struct *ve, dev_t dev, char *opts, bool new_mount
return err;
}
-static inline int ve_mount_allowed(void)
-{
- struct ve_struct *ve = get_exec_env();
-
- return ve_is_super(ve) || ve->is_pseudosuper ||
- atomic_read(&ve->mnt_nr) < (int)sysctl_ve_mount_nr;
-}
-
-static inline void ve_mount_nr_inc(struct mount *mnt, struct ve_struct *ve)
+static inline int ve_try_reserve_mount(struct ve_struct *ve)
{
- if (!ve)
- ve = get_exec_env();
+ int ret = ve_is_super(ve) || ve->is_pseudosuper;
+ /* Ignore limits in ve0 and pseudosuper cases, but still count. */
+ if (ret)
+ atomic_dec(&ve->mnt_avail_nr);
+ else
+ ret = atomic_dec_if_positive(&ve->mnt_avail_nr) >= 0;
- mnt->ve_owner = get_ve(ve);
- atomic_inc(&ve->mnt_nr);
+ if (ret)
+ get_ve(ve);
+ return ret;
}
-static inline void ve_mount_nr_dec(struct mount *mnt)
+static inline void ve_mount_put(struct mount *mnt, struct ve_struct *ve)
{
- struct ve_struct *ve = mnt->ve_owner;
-
- atomic_dec(&ve->mnt_nr);
+ /*
+ * ve argument is needed to reuse this function in alloc_vfsmnt error path.
+ * Other users should pass mnt->ve_owner value.
+ */
+ atomic_inc(&ve->mnt_avail_nr);
put_ve(ve);
- mnt->ve_owner = NULL;
+ if (mnt)
+ mnt->ve_owner = NULL;
}
/*
@@ -3397,9 +3413,9 @@ static inline void ve_mount_reown(struct mount *mnt, struct ve_struct *ve)
if (old == ve)
return;
- atomic_inc(&ve->mnt_nr);
+ atomic_dec(&ve->mnt_avail_nr);
WRITE_ONCE(mnt->ve_owner, get_ve(ve));
- atomic_dec(&old->mnt_nr);
+ atomic_inc(&old->mnt_avail_nr);
put_ve(old);
}
@@ -3422,10 +3438,10 @@ bool is_sb_ve_accessible(struct ve_struct *ve, struct super_block *sb)
#else /* CONFIG_VE */
-static inline int ve_mount_allowed(void) { return 1; }
-static inline void ve_mount_nr_inc(struct mount *mnt, struct ve_struct *ve) { }
-static inline void ve_mount_nr_dec(struct mount *mnt) { }
+static inline int ve_try_reserve_mount(struct ve_struct *ve) { return 1; }
+static inline void ve_mount_put(struct mount *mnt, struct ve_struct *ve) { }
static inline void ve_mount_reown(struct mount *mnt, struct ve_struct *ve) { }
+
#endif /* CONFIG_VE */
/*
diff --git a/include/linux/ve.h b/include/linux/ve.h
index f946abe4d8596..65c058c0f34d4 100644
--- a/include/linux/ve.h
+++ b/include/linux/ve.h
@@ -96,7 +96,7 @@ struct ve_struct {
atomic_t nd_neigh_nr;
unsigned long meminfo_val;
- atomic_t mnt_nr; /* number of present VE mounts */
+ atomic_t mnt_avail_nr; /* number of available VE mounts */
#ifdef CONFIG_COREDUMP
char core_pattern[CORENAME_MAX_SIZE];
diff --git a/kernel/ve/ve.c b/kernel/ve/ve.c
index e3ae2e5d9a86c..bed8a58af0853 100644
--- a/kernel/ve/ve.c
+++ b/kernel/ve/ve.c
@@ -82,7 +82,7 @@ struct ve_struct ve0 = {
.arp_neigh_nr = ATOMIC_INIT(0),
.nd_neigh_nr = ATOMIC_INIT(0),
- .mnt_nr = ATOMIC_INIT(0),
+ .mnt_avail_nr = ATOMIC_INIT(INT_MAX),
.meminfo_val = VE_MEMINFO_SYSTEM,
.umh_running_helpers = ATOMIC_INIT(0),
.umh_helpers_waitq = __WAIT_QUEUE_HEAD_INITIALIZER(ve0.umh_helpers_waitq),
@@ -787,7 +787,7 @@ static struct cgroup_subsys_state *ve_create(struct cgroup_subsys_state *parent_
atomic_set(&ve->arp_neigh_nr, 0);
atomic_set(&ve->nd_neigh_nr, 0);
- atomic_set(&ve->mnt_nr, 0);
+ atomic_set(&ve->mnt_avail_nr, sysctl_ve_mount_nr);
#ifdef CONFIG_COREDUMP
strcpy(ve->core_pattern, "core");
@@ -1063,9 +1063,9 @@ static u64 ve_netns_avail_nr_read(struct cgroup_subsys_state *css, struct cftype
return atomic_read(&css_to_ve(css)->netns_avail_nr);
}
-static u64 ve_mnt_nr_read(struct cgroup_subsys_state *css, struct cftype *cft)
+static s64 ve_mnt_avail_nr_read(struct cgroup_subsys_state *css, struct cftype *cft)
{
- return atomic_read(&css_to_ve(css)->mnt_nr);
+ return atomic_read(&css_to_ve(css)->mnt_avail_nr);
}
static u64 ve_netif_max_nr_read(struct cgroup_subsys_state *css, struct cftype *cft)
@@ -1627,8 +1627,8 @@ static struct cftype ve_cftypes[] = {
.read_u64 = ve_netns_avail_nr_read,
},
{
- .name = "mnt_nr",
- .read_u64 = ve_mnt_nr_read,
+ .name = "mnt_avail_nr",
+ .read_s64 = ve_mnt_avail_nr_read,
},
{
.name = "netif_max_nr",
--
2.43.0
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 16+ messages in thread* [Devel] [PATCH hci-8.1 v10 03/13] selftests/ve: update ve_ns_owner_test
2026-09-25 16:53 [Devel] [PATCH hci-8.1 v10 00/13] Add per-VE failcount support Konstantin Khorenko
2026-09-25 16:53 ` [Devel] [PATCH hci-8.1 v10 01/13] ve/ve.{h, c}: farewell to spaces as indents Konstantin Khorenko
2026-09-25 16:53 ` [Devel] [PATCH hci-8.1 v10 02/13] ve/fs: count available mounts instead of used ones Konstantin Khorenko
@ 2026-09-25 16:53 ` Konstantin Khorenko
2026-09-25 16:53 ` [Devel] [PATCH hci-8.1 v10 04/13] ve: make mount limit configurable per VE Konstantin Khorenko
` (11 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Konstantin Khorenko @ 2026-09-25 16:53 UTC (permalink / raw)
To: Vasileios Almpanis, Pavel Tikhomirov,
cover.1789491989.git.vladimir.riabchun, Konstantin Khorenko,
devel
From: Vladimir Riabchun <vladimir.riabchun@virtuozzo.com>
Mount accounting interface has changed, now it shows the number
of available mounts.
https://virtuozzo.atlassian.net/browse/VSTOR-135520
Feature: per-ve failcounters
Signed-off-by: Vladimir Riabchun <vladimir.riabchun@virtuozzo.com>
Reviewed-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
tools/testing/selftests/ve/ve_ns_owner_test.c | 33 ++++++++++++++-------------
1 file changed, 17 insertions(+), 16 deletions(-)
diff --git a/tools/testing/selftests/ve/ve_ns_owner_test.c b/tools/testing/selftests/ve/ve_ns_owner_test.c
index 82c31dff4b1ba..c3fa7d7b7f9c0 100644
--- a/tools/testing/selftests/ve/ve_ns_owner_test.c
+++ b/tools/testing/selftests/ve/ve_ns_owner_test.c
@@ -18,7 +18,7 @@
* with a new netns and mntns, the parent reads the new ve's counters
* via cgroupfs and asserts they reflect the just-created namespaces:
* - ve.netns_avail_nr drops by exactly one (the new netns);
- * - ve.mnt_nr is strictly greater than zero (mounts copied into the
+ * - ve.mnt_avail_nr drops by some value >= 1 (mounts copied into the
* new mntns are accounted to the new ve).
*
* We never assert against the parent ve's counters: those are shared
@@ -50,6 +50,7 @@
* any spurious accounting against the parent ve would overflow it.
*/
#define VE_NETNS_MAX 3
+#define VE_MOUNTS_MAX 4096
/*
* Synchronisation across the clone() boundary: child does its setup,
@@ -106,7 +107,7 @@ static int clone_child_func(void *arg)
/*
* Before fix:
* - clone path: ve.netns_avail_nr stays at VE_NETNS_MAX and
- * ve.mnt_nr stays at 0 because copy_net_ns()/copy_mnt_ns()
+ * ve.mnt_avail_nr stays at VE_MOUNTS_MAX because copy_net_ns()/copy_mnt_ns()
* charged the parent ve via get_exec_env().
* - unshare path: the syscall itself returned -EINVAL, so this
* check was unreachable.
@@ -117,16 +118,16 @@ static int clone_child_func(void *arg)
static void check_new_ve_owner(struct __test_metadata *_metadata,
int cgv2_fd, int ctid)
{
- unsigned long long avail, mnt;
- char path[PATH_MAX];
+ unsigned long long avail_netns, avail_mnt;
+ char path[64];
snprintf(path, sizeof(path), "%d/ve.netns_avail_nr", ctid);
- ASSERT_EQ(read_u64_at(cgv2_fd, path, &avail), 0);
- EXPECT_EQ(avail, VE_NETNS_MAX - 1);
+ ASSERT_EQ(read_u64_at(cgv2_fd, path, &avail_netns), 0);
+ EXPECT_EQ(avail_netns, VE_NETNS_MAX - 1);
- snprintf(path, sizeof(path), "%d/ve.mnt_nr", ctid);
- ASSERT_EQ(read_u64_at(cgv2_fd, path, &mnt), 0);
- EXPECT_GT(mnt, 0);
+ snprintf(path, sizeof(path), "%d/ve.mnt_avail_nr", ctid);
+ ASSERT_EQ(read_u64_at(cgv2_fd, path, &avail_mnt), 0);
+ EXPECT_LT(avail_mnt, VE_MOUNTS_MAX);
}
FIXTURE(ve_ns_owner)
@@ -137,7 +138,7 @@ FIXTURE(ve_ns_owner)
FIXTURE_SETUP(ve_ns_owner)
{
- unsigned long long initial_mnt_nr;
+ unsigned long long initial_mnt_avail_nr;
char val[16];
char path[PATH_MAX];
@@ -174,13 +175,13 @@ FIXTURE_SETUP(ve_ns_owner)
/*
* The new ve cgroup has not been entered by anything yet, so its
- * mnt_nr counter must start at 0. Each test below verifies that
- * the clone/unshare populates the new mntns under this ve, i.e.
- * mnt_nr rises strictly above zero.
+ * mnt_avail_nr counter should be VE_MOUNTS_MAX. Each test below
+ * verifies that the clone/unshare populates the new mntns
+ * under this ve, i.e. mnt_avail_nr strictly decreases.
*/
- snprintf(path, sizeof(path), "%d/ve.mnt_nr", self->ctid);
- ASSERT_EQ(read_u64_at(self->cgv2_fd, path, &initial_mnt_nr), 0);
- ASSERT_EQ(initial_mnt_nr, 0);
+ snprintf(path, sizeof(path), "%d/ve.mnt_avail_nr", self->ctid);
+ ASSERT_EQ(read_u64_at(self->cgv2_fd, path, &initial_mnt_avail_nr), 0);
+ ASSERT_EQ(initial_mnt_avail_nr, VE_MOUNTS_MAX);
};
FIXTURE_TEARDOWN(ve_ns_owner)
--
2.43.0
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 16+ messages in thread* [Devel] [PATCH hci-8.1 v10 04/13] ve: make mount limit configurable per VE
2026-09-25 16:53 [Devel] [PATCH hci-8.1 v10 00/13] Add per-VE failcount support Konstantin Khorenko
` (2 preceding siblings ...)
2026-09-25 16:53 ` [Devel] [PATCH hci-8.1 v10 03/13] selftests/ve: update ve_ns_owner_test Konstantin Khorenko
@ 2026-09-25 16:53 ` Konstantin Khorenko
2026-09-25 16:53 ` [Devel] [PATCH hci-8.1 v10 05/13] ve/ve.c: generate VE resource accessors using macros Konstantin Khorenko
` (10 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Konstantin Khorenko @ 2026-09-25 16:53 UTC (permalink / raw)
To: Vasileios Almpanis, Pavel Tikhomirov,
cover.1789491989.git.vladimir.riabchun, Konstantin Khorenko,
devel
From: Vladimir Riabchun <vladimir.riabchun@virtuozzo.com>
This change unifies mount limit handling with other VE limits.
This will simplify refactoring later and allow more precise limits
control from one place.
https://virtuozzo.atlassian.net/browse/VSTOR-135520
Feature: per-ve failcounters
Signed-off-by: Vladimir Riabchun <vladimir.riabchun@virtuozzo.com>
Reviewed-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
include/linux/ve.h | 9 +++++--
kernel/ve/ve.c | 34 ++++++++++++++++++++++++++-
kernel/ve/veowner.c | 19 ---------------
tools/testing/selftests/ve/ve_ns_owner_test.c | 6 ++++-
4 files changed, 45 insertions(+), 23 deletions(-)
diff --git a/include/linux/ve.h b/include/linux/ve.h
index 65c058c0f34d4..fabc608e31f51 100644
--- a/include/linux/ve.h
+++ b/include/linux/ve.h
@@ -96,7 +96,13 @@ struct ve_struct {
atomic_t nd_neigh_nr;
unsigned long meminfo_val;
+ /*
+ * Operations with a big amount of mount points can require a lot of time.
+ * These operations take the global lock namespace_sem, so they can affect
+ * other containers.
+ */
atomic_t mnt_avail_nr; /* number of available VE mounts */
+ int mnt_max_nr;
#ifdef CONFIG_COREDUMP
char core_pattern[CORENAME_MAX_SIZE];
@@ -143,8 +149,7 @@ extern int nr_ve;
#define NETNS_MAX_NR_DEFAULT 256 /* number of net-namespaces per-VE */
#define NETIF_MAX_NR_DEFAULT 256 /* number of net-interfaces per-VE */
#define BPF_PROG_MAX_NR_DEFAULT 256 /* number of loaded BPF progs per-VE */
-
-extern unsigned int sysctl_ve_mount_nr;
+#define MNT_MAX_NR_DEFAULT 4096 /* number of mounts per-VE */
#ifdef CONFIG_VE
static inline void ve_set_state(struct ve_struct *ve, int new_state)
diff --git a/kernel/ve/ve.c b/kernel/ve/ve.c
index bed8a58af0853..c113071540bf4 100644
--- a/kernel/ve/ve.c
+++ b/kernel/ve/ve.c
@@ -83,6 +83,7 @@ struct ve_struct ve0 = {
.arp_neigh_nr = ATOMIC_INIT(0),
.nd_neigh_nr = ATOMIC_INIT(0),
.mnt_avail_nr = ATOMIC_INIT(INT_MAX),
+ .mnt_max_nr = INT_MAX,
.meminfo_val = VE_MEMINFO_SYSTEM,
.umh_running_helpers = ATOMIC_INIT(0),
.umh_helpers_waitq = __WAIT_QUEUE_HEAD_INITIALIZER(ve0.umh_helpers_waitq),
@@ -787,7 +788,8 @@ static struct cgroup_subsys_state *ve_create(struct cgroup_subsys_state *parent_
atomic_set(&ve->arp_neigh_nr, 0);
atomic_set(&ve->nd_neigh_nr, 0);
- atomic_set(&ve->mnt_avail_nr, sysctl_ve_mount_nr);
+ ve->mnt_max_nr = MNT_MAX_NR_DEFAULT;
+ atomic_set(&ve->mnt_avail_nr, MNT_MAX_NR_DEFAULT);
#ifdef CONFIG_COREDUMP
strcpy(ve->core_pattern, "core");
@@ -1068,6 +1070,30 @@ static s64 ve_mnt_avail_nr_read(struct cgroup_subsys_state *css, struct cftype *
return atomic_read(&css_to_ve(css)->mnt_avail_nr);
}
+static u64 ve_mnt_max_nr_read(struct cgroup_subsys_state *css, struct cftype *cft)
+{
+ return css_to_ve(css)->mnt_max_nr;
+}
+
+static int ve_mnt_max_nr_write(struct cgroup_subsys_state *css, struct cftype *cft, u64 val)
+{
+ struct ve_struct *ve = css_to_ve(css);
+ int delta;
+
+ if (!ve_is_super(get_exec_env()))
+ return -EPERM;
+
+ if (val > INT_MAX)
+ return -EOVERFLOW;
+
+ down_write(&ve->op_sem);
+ delta = val - ve->mnt_max_nr;
+ ve->mnt_max_nr = val;
+ atomic_add(delta, &ve->mnt_avail_nr);
+ up_write(&ve->op_sem);
+ return 0;
+}
+
static u64 ve_netif_max_nr_read(struct cgroup_subsys_state *css, struct cftype *cft)
{
return css_to_ve(css)->netif_max_nr;
@@ -1630,6 +1656,12 @@ static struct cftype ve_cftypes[] = {
.name = "mnt_avail_nr",
.read_s64 = ve_mnt_avail_nr_read,
},
+ {
+ .name = "mnt_max_nr",
+ .flags = CFTYPE_NOT_ON_ROOT,
+ .read_u64 = ve_mnt_max_nr_read,
+ .write_u64 = ve_mnt_max_nr_write,
+ },
{
.name = "netif_max_nr",
.flags = CFTYPE_NOT_ON_ROOT,
diff --git a/kernel/ve/veowner.c b/kernel/ve/veowner.c
index 1e631baf0de9f..c77735f8c0355 100644
--- a/kernel/ve/veowner.c
+++ b/kernel/ve/veowner.c
@@ -48,26 +48,7 @@ static void prepare_proc(void)
* ------------------------------------------------------------------------
*/
-/*
- * Operations with a big amount of mount points can require a lot of time.
- * These operations take the global lock namespace_sem, so they can affect
- * other containers. Let us allow no more than sysctl_ve_mount_nr mount
- * points for a VE.
- */
-unsigned int sysctl_ve_mount_nr = 4096;
-static int ve_mount_nr_min = 0;
-static int ve_mount_nr_max = INT_MAX;
-
static struct ctl_table vz_fs_table[] = {
- {
- .procname = "ve-mount-nr",
- .data = &sysctl_ve_mount_nr,
- .maxlen = sizeof(sysctl_ve_mount_nr),
- .mode = 0644,
- .proc_handler = proc_dointvec_minmax,
- .extra1 = &ve_mount_nr_min,
- .extra2 = &ve_mount_nr_max,
- },
{
.procname = "fsync-enable",
.data = &ve0.fsync_enable,
diff --git a/tools/testing/selftests/ve/ve_ns_owner_test.c b/tools/testing/selftests/ve/ve_ns_owner_test.c
index c3fa7d7b7f9c0..dcff4d54172dc 100644
--- a/tools/testing/selftests/ve/ve_ns_owner_test.c
+++ b/tools/testing/selftests/ve/ve_ns_owner_test.c
@@ -50,7 +50,7 @@
* any spurious accounting against the parent ve would overflow it.
*/
#define VE_NETNS_MAX 3
-#define VE_MOUNTS_MAX 4096
+#define VE_MOUNTS_MAX 32
/*
* Synchronisation across the clone() boundary: child does its setup,
@@ -173,6 +173,10 @@ FIXTURE_SETUP(ve_ns_owner)
snprintf(val, sizeof(val), "%d", VE_NETNS_MAX);
ASSERT_EQ(write_file_at(self->cgv2_fd, path, val), 0);
+ snprintf(path, sizeof(path), "%d/ve.mnt_max_nr", self->ctid);
+ snprintf(val, sizeof(val), "%d", VE_MOUNTS_MAX);
+ ASSERT_EQ(write_file_at(self->cgv2_fd, path, val), 0);
+
/*
* The new ve cgroup has not been entered by anything yet, so its
* mnt_avail_nr counter should be VE_MOUNTS_MAX. Each test below
--
2.43.0
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 16+ messages in thread* [Devel] [PATCH hci-8.1 v10 05/13] ve/ve.c: generate VE resource accessors using macros
2026-09-25 16:53 [Devel] [PATCH hci-8.1 v10 00/13] Add per-VE failcount support Konstantin Khorenko
` (3 preceding siblings ...)
2026-09-25 16:53 ` [Devel] [PATCH hci-8.1 v10 04/13] ve: make mount limit configurable per VE Konstantin Khorenko
@ 2026-09-25 16:53 ` Konstantin Khorenko
2026-09-25 16:53 ` [Devel] [PATCH hci-8.1 v10 06/13] ve/aio: initialize aio fields in ve0 Konstantin Khorenko
` (9 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Konstantin Khorenko @ 2026-09-25 16:53 UTC (permalink / raw)
To: Vasileios Almpanis, Pavel Tikhomirov,
cover.1789491989.git.vladimir.riabchun, Konstantin Khorenko,
devel
From: Vladimir Riabchun <vladimir.riabchun@virtuozzo.com>
We have a bunch of almost identical code, which takes place
and has some unclear variations in it. Unify all resource
getters and setters using macros.
This changes behavior a little bit: some resources (netns number
for example) required stopped VE. Now such limits could be updated
with a running VE.
Also, some max_nr_write functions didn't verify provided value,
now it is not possible to pass overflowing value.
netns_avail_nr is now read as s64, like the other avail counters:
once the limit can be lowered on a running VE, the value can go
negative.
https://virtuozzo.atlassian.net/browse/VSTOR-135520
Feature: per-ve failcounters
Signed-off-by: Vladimir Riabchun <vladimir.riabchun@virtuozzo.com>
Reviewed-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
kernel/ve/ve.c | 209 +++++++++++++++------------------------------------------
1 file changed, 54 insertions(+), 155 deletions(-)
diff --git a/kernel/ve/ve.c b/kernel/ve/ve.c
index c113071540bf4..d7546cd5fffaa 100644
--- a/kernel/ve/ve.c
+++ b/kernel/ve/ve.c
@@ -43,6 +43,39 @@
#include <linux/cpuset.h> /* For css_tg() */
#include "../sched/sched.h" /* For css_tg() */
+#define VE_RESOURCE(name) \
+static u64 ve_##name##_max_nr_read(struct cgroup_subsys_state *css, \
+ struct cftype *cft) \
+{ \
+ return css_to_ve(css)->name##_max_nr; \
+} \
+ \
+static int ve_##name##_max_nr_write(struct cgroup_subsys_state *css, \
+ struct cftype *cft, u64 val) \
+{ \
+ struct ve_struct *ve = css_to_ve(css); \
+ int delta; \
+ \
+ if (!ve_is_super(get_exec_env())) \
+ return -EPERM; \
+ \
+ if (val > INT_MAX) \
+ return -EOVERFLOW; \
+ \
+ down_write(&ve->op_sem); \
+ delta = val - ve->name##_max_nr; \
+ ve->name##_max_nr = val; \
+ atomic_add(delta, &ve->name##_avail_nr); \
+ up_write(&ve->op_sem); \
+ return 0; \
+} \
+ \
+static s64 ve_##name##_avail_nr_read(struct cgroup_subsys_state *css, \
+ struct cftype *cft) \
+{ \
+ return atomic_read(&css_to_ve(css)->name##_avail_nr); \
+}
+
extern struct kmapset_set sysfs_ve_perms_set;
#ifdef CONFIG_PROC_FS
extern struct kmapset_set proc_ve_perms_set;
@@ -1036,121 +1069,10 @@ static int ve_features_write(struct cgroup_subsys_state *css, struct cftype *cft
return 0;
}
-static u64 ve_netns_max_nr_read(struct cgroup_subsys_state *css, struct cftype *cft)
-{
- return css_to_ve(css)->netns_max_nr;
-}
-
-static int ve_netns_max_nr_write(struct cgroup_subsys_state *css, struct cftype *cft, u64 val)
-{
- struct ve_struct *ve = css_to_ve(css);
- int delta;
-
- if (!ve_is_super(get_exec_env()))
- return -EPERM;
-
- down_write(&ve->op_sem);
- if (VE_IS_RUNNING(ve) || ve->ve_nsproxy) {
- up_write(&ve->op_sem);
- return -EBUSY;
- }
- delta = val - ve->netns_max_nr;
- ve->netns_max_nr = val;
- atomic_add(delta, &ve->netns_avail_nr);
- up_write(&ve->op_sem);
- return 0;
-}
-static u64 ve_netns_avail_nr_read(struct cgroup_subsys_state *css, struct cftype *cft)
-{
- return atomic_read(&css_to_ve(css)->netns_avail_nr);
-}
-
-static s64 ve_mnt_avail_nr_read(struct cgroup_subsys_state *css, struct cftype *cft)
-{
- return atomic_read(&css_to_ve(css)->mnt_avail_nr);
-}
-
-static u64 ve_mnt_max_nr_read(struct cgroup_subsys_state *css, struct cftype *cft)
-{
- return css_to_ve(css)->mnt_max_nr;
-}
-
-static int ve_mnt_max_nr_write(struct cgroup_subsys_state *css, struct cftype *cft, u64 val)
-{
- struct ve_struct *ve = css_to_ve(css);
- int delta;
-
- if (!ve_is_super(get_exec_env()))
- return -EPERM;
-
- if (val > INT_MAX)
- return -EOVERFLOW;
-
- down_write(&ve->op_sem);
- delta = val - ve->mnt_max_nr;
- ve->mnt_max_nr = val;
- atomic_add(delta, &ve->mnt_avail_nr);
- up_write(&ve->op_sem);
- return 0;
-}
-
-static u64 ve_netif_max_nr_read(struct cgroup_subsys_state *css, struct cftype *cft)
-{
- return css_to_ve(css)->netif_max_nr;
-}
-
-static int ve_netif_max_nr_write(struct cgroup_subsys_state *css, struct cftype *cft, u64 val)
-{
- struct ve_struct *ve = css_to_ve(css);
- int delta;
-
- if (!ve_is_super(get_exec_env()))
- return -EPERM;
-
- if (val > INT_MAX)
- return -EOVERFLOW;
-
- down_write(&ve->op_sem);
- delta = val - ve->netif_max_nr;
- ve->netif_max_nr = val;
- atomic_add(delta, &ve->netif_avail_nr);
- up_write(&ve->op_sem);
- return 0;
-}
-
-static s64 ve_netif_avail_nr_read(struct cgroup_subsys_state *css, struct cftype *cft)
-{
- return atomic_read(&css_to_ve(css)->netif_avail_nr);
-}
-
-static u64 ve_bpf_prog_max_nr_read(struct cgroup_subsys_state *css, struct cftype *cft)
-{
- return css_to_ve(css)->bpf_prog_max_nr;
-}
-
-static int ve_bpf_prog_max_nr_write(struct cgroup_subsys_state *css, struct cftype *cft, u64 val)
-{
- struct ve_struct *ve = css_to_ve(css);
- int delta;
-
- if (!ve_is_super(get_exec_env()))
- return -EPERM;
-
- if (val > INT_MAX)
- return -EOVERFLOW;
-
- down_write(&ve->op_sem);
- delta = val - ve->bpf_prog_max_nr;
- ve->bpf_prog_max_nr = val;
- atomic_add(delta, &ve->bpf_prog_avail_nr);
- up_write(&ve->op_sem);
- return 0;
-}
-
-static s64 ve_bpf_prog_avail_nr_read(struct cgroup_subsys_state *css, struct cftype *cft)
-{
- return atomic_read(&css_to_ve(css)->bpf_prog_avail_nr);
-}
+VE_RESOURCE(netns);
+VE_RESOURCE(mnt);
+VE_RESOURCE(netif);
+VE_RESOURCE(bpf_prog);
static int ve_os_release_read(struct seq_file *sf, void *v)
{
@@ -1598,6 +1520,19 @@ static int ve_rpc_kill_write(struct cgroup_subsys_state *css,
return 0;
}
+/* checkpatch will report it, can't enclose this in parentheses. */
+#define VE_RESOURCE_CFTYPE(res) \
+{ \
+ .name = #res "_max_nr", \
+ .flags = CFTYPE_NOT_ON_ROOT, \
+ .read_u64 = ve_##res##_max_nr_read, \
+ .write_u64 = ve_##res##_max_nr_write, \
+}, \
+{ \
+ .name = #res "_avail_nr", \
+ .read_s64 = ve_##res##_avail_nr_read, \
+}
+
static struct cftype ve_cftypes[] = {
{
@@ -1642,46 +1577,10 @@ static struct cftype ve_cftypes[] = {
.read_u64 = ve_pid_max_read_u64,
.write_u64 = ve_pid_max_write_running_u64,
},
- {
- .name = "netns_max_nr",
- .flags = CFTYPE_NOT_ON_ROOT,
- .read_u64 = ve_netns_max_nr_read,
- .write_u64 = ve_netns_max_nr_write,
- },
- {
- .name = "netns_avail_nr",
- .read_u64 = ve_netns_avail_nr_read,
- },
- {
- .name = "mnt_avail_nr",
- .read_s64 = ve_mnt_avail_nr_read,
- },
- {
- .name = "mnt_max_nr",
- .flags = CFTYPE_NOT_ON_ROOT,
- .read_u64 = ve_mnt_max_nr_read,
- .write_u64 = ve_mnt_max_nr_write,
- },
- {
- .name = "netif_max_nr",
- .flags = CFTYPE_NOT_ON_ROOT,
- .read_u64 = ve_netif_max_nr_read,
- .write_u64 = ve_netif_max_nr_write,
- },
- {
- .name = "netif_avail_nr",
- .read_s64 = ve_netif_avail_nr_read,
- },
- {
- .name = "bpf_prog_max_nr",
- .flags = CFTYPE_NOT_ON_ROOT,
- .read_u64 = ve_bpf_prog_max_nr_read,
- .write_u64 = ve_bpf_prog_max_nr_write,
- },
- {
- .name = "bpf_prog_avail_nr",
- .read_s64 = ve_bpf_prog_avail_nr_read,
- },
+ VE_RESOURCE_CFTYPE(netns),
+ VE_RESOURCE_CFTYPE(mnt),
+ VE_RESOURCE_CFTYPE(netif),
+ VE_RESOURCE_CFTYPE(bpf_prog),
{
.name = "os_release",
.max_write_len = __NEW_UTS_LEN + 1,
--
2.43.0
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 16+ messages in thread* [Devel] [PATCH hci-8.1 v10 06/13] ve/aio: initialize aio fields in ve0
2026-09-25 16:53 [Devel] [PATCH hci-8.1 v10 00/13] Add per-VE failcount support Konstantin Khorenko
` (4 preceding siblings ...)
2026-09-25 16:53 ` [Devel] [PATCH hci-8.1 v10 05/13] ve/ve.c: generate VE resource accessors using macros Konstantin Khorenko
@ 2026-09-25 16:53 ` Konstantin Khorenko
2026-09-25 16:53 ` [Devel] [PATCH hci-8.1 v10 07/13] ve: introduce per-VE failcounter infrastructure Konstantin Khorenko
` (8 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Konstantin Khorenko @ 2026-09-25 16:53 UTC (permalink / raw)
To: Vasileios Almpanis, Pavel Tikhomirov,
cover.1789491989.git.vladimir.riabchun, Konstantin Khorenko,
devel
From: Vladimir Riabchun <vladimir.riabchun@virtuozzo.com>
When aio_nr and aio_max_nr were moved into struct ve_struct,
ve_create() got per-VE defaults, but ve0 was left zero-initialized:
on the host aio_max_nr is 0, so io_setup() fails until
fs.aio-max-nr is raised by hand, and the zeroed aio_nr_lock
triggers a bad magic report with CONFIG_DEBUG_SPINLOCK.
Fixes: 3e96f90392f4a ("ve/fs/aio: aio_nr & aio_max_nr variables virtualization")
https://virtuozzo.atlassian.net/browse/VSTOR-135520
Signed-off-by: Vladimir Riabchun <vladimir.riabchun@virtuozzo.com>
Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
kernel/ve/ve.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/kernel/ve/ve.c b/kernel/ve/ve.c
index d7546cd5fffaa..c4e5c000bc427 100644
--- a/kernel/ve/ve.c
+++ b/kernel/ve/ve.c
@@ -117,6 +117,11 @@ struct ve_struct ve0 = {
.nd_neigh_nr = ATOMIC_INIT(0),
.mnt_avail_nr = ATOMIC_INIT(INT_MAX),
.mnt_max_nr = INT_MAX,
+#ifdef CONFIG_AIO
+ .aio_nr_lock = __SPIN_LOCK_UNLOCKED(ve0.aio_nr_lock),
+ .aio_nr = 0,
+ .aio_max_nr = AIO_MAX_NR_DEFAULT,
+#endif
.meminfo_val = VE_MEMINFO_SYSTEM,
.umh_running_helpers = ATOMIC_INIT(0),
.umh_helpers_waitq = __WAIT_QUEUE_HEAD_INITIALIZER(ve0.umh_helpers_waitq),
--
2.43.0
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 16+ messages in thread* [Devel] [PATCH hci-8.1 v10 07/13] ve: introduce per-VE failcounter infrastructure
2026-09-25 16:53 [Devel] [PATCH hci-8.1 v10 00/13] Add per-VE failcount support Konstantin Khorenko
` (5 preceding siblings ...)
2026-09-25 16:53 ` [Devel] [PATCH hci-8.1 v10 06/13] ve/aio: initialize aio fields in ve0 Konstantin Khorenko
@ 2026-09-25 16:53 ` Konstantin Khorenko
2026-09-25 16:53 ` [Devel] [PATCH hci-8.1 v10 08/13] ve: add netns failcounter Konstantin Khorenko
` (7 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Konstantin Khorenko @ 2026-09-25 16:53 UTC (permalink / raw)
To: Vasileios Almpanis, Pavel Tikhomirov,
cover.1789491989.git.vladimir.riabchun, Konstantin Khorenko,
devel
From: Vladimir Riabchun <vladimir.riabchun@virtuozzo.com>
It may be useful to have a history of resource limit hits for every VE,
this may simplify debugging and provide some information about the
resources usage.
This information is provided by ve.failcount file, any write to it
resets all failcounts.
To add a new failcounter we need to create a new atomic_t field
name_failcount in ve structure and add a new VE_FC_ENTRY in
ve_failcounts array. The counters themselves come in the follow-up
patches, one per resource.
The neighbour table overflow will not be counted for now: neigh_alloc
can run in softirq context, where ve log must not be used, and
ve_failcount_inc writes to it. The neigh_tbl counter will be added
together with the ve_printk softirq fix (VSTOR-146839).
https://virtuozzo.atlassian.net/browse/VSTOR-135520
Feature: per-ve failcounters
Signed-off-by: Vladimir Riabchun <vladimir.riabchun@virtuozzo.com>
Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
include/linux/ve.h | 10 ++++++++++
kernel/ve/ve.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 55 insertions(+)
diff --git a/include/linux/ve.h b/include/linux/ve.h
index fabc608e31f51..546060eddc765 100644
--- a/include/linux/ve.h
+++ b/include/linux/ve.h
@@ -151,6 +151,16 @@ extern int nr_ve;
#define BPF_PROG_MAX_NR_DEFAULT 256 /* number of loaded BPF progs per-VE */
#define MNT_MAX_NR_DEFAULT 4096 /* number of mounts per-VE */
+#define ve_failcount_inc(ve, name) \
+do { \
+ struct ve_struct *__ve = (ve); \
+ \
+ atomic_inc(&__ve->name##_failcount); \
+ ve_pr_warn_ratelimited(VE_LOG_BOTH, "CT%s: hits the " #name \
+ " limit, see the '" #name "' counter in ve.failcount " \
+ "of the container's ve cgroup\n", ve_name(__ve)); \
+} while (0)
+
#ifdef CONFIG_VE
static inline void ve_set_state(struct ve_struct *ve, int new_state)
{
diff --git a/kernel/ve/ve.c b/kernel/ve/ve.c
index c4e5c000bc427..f915f86e7471e 100644
--- a/kernel/ve/ve.c
+++ b/kernel/ve/ve.c
@@ -1079,6 +1079,45 @@ VE_RESOURCE(mnt);
VE_RESOURCE(netif);
VE_RESOURCE(bpf_prog);
+static const struct ve_failcount_entry {
+ const char *name;
+ size_t offset;
+} ve_failcounts[] = {
+#define VE_FC_ENTRY(name) { #name, offsetof(struct ve_struct, name##_failcount) }
+ {}
+};
+
+static int ve_failcount_read(struct seq_file *sf, void *v)
+{
+ struct ve_struct *ve = css_to_ve(seq_css(sf));
+ const struct ve_failcount_entry *entry;
+ atomic_t *fc;
+
+ for (entry = ve_failcounts; entry->name; entry++) {
+ fc = (void *)ve + entry->offset;
+ seq_printf(sf, "%s: %d\n", entry->name, atomic_read(fc));
+ }
+ return 0;
+}
+
+static ssize_t ve_failcount_write(struct kernfs_open_file *of, char *buf,
+ size_t nbytes, loff_t off)
+{
+ struct ve_struct *ve = css_to_ve(of_css(of));
+ const struct ve_failcount_entry *entry;
+ atomic_t *fc;
+
+ if (!ve_is_super(get_exec_env()) && !ve->is_pseudosuper)
+ return -EPERM;
+
+ for (entry = ve_failcounts; entry->name; entry++) {
+ fc = (void *)ve + entry->offset;
+ atomic_set(fc, 0);
+ }
+
+ return nbytes;
+}
+
static int ve_os_release_read(struct seq_file *sf, void *v)
{
struct cgroup_subsys_state *css = seq_css(sf);
@@ -1618,6 +1657,12 @@ static struct cftype ve_cftypes[] = {
.flags = CFTYPE_NOT_ON_ROOT,
.write_u64 = ve_rpc_kill_write,
},
+ {
+ .name = "failcount",
+ .flags = CFTYPE_NOT_ON_ROOT,
+ .seq_show = ve_failcount_read,
+ .write = ve_failcount_write,
+ },
{ }
};
--
2.43.0
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 16+ messages in thread* [Devel] [PATCH hci-8.1 v10 08/13] ve: add netns failcounter
2026-09-25 16:53 [Devel] [PATCH hci-8.1 v10 00/13] Add per-VE failcount support Konstantin Khorenko
` (6 preceding siblings ...)
2026-09-25 16:53 ` [Devel] [PATCH hci-8.1 v10 07/13] ve: introduce per-VE failcounter infrastructure Konstantin Khorenko
@ 2026-09-25 16:53 ` Konstantin Khorenko
2026-09-25 16:53 ` [Devel] [PATCH hci-8.1 v10 09/13] ve: add mnt failcounter Konstantin Khorenko
` (6 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Konstantin Khorenko @ 2026-09-25 16:53 UTC (permalink / raw)
To: Vasileios Almpanis, Pavel Tikhomirov,
cover.1789491989.git.vladimir.riabchun, Konstantin Khorenko,
devel
From: Vladimir Riabchun <vladimir.riabchun@virtuozzo.com>
Count hits of the per-VE net namespace limit in ve.failcount.
https://virtuozzo.atlassian.net/browse/VSTOR-135520
Feature: per-ve failcounters
Signed-off-by: Vladimir Riabchun <vladimir.riabchun@virtuozzo.com>
Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
include/linux/ve.h | 1 +
kernel/ve/ve.c | 3 +++
net/core/net_namespace.c | 4 +++-
3 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/include/linux/ve.h b/include/linux/ve.h
index 546060eddc765..969390bb511e7 100644
--- a/include/linux/ve.h
+++ b/include/linux/ve.h
@@ -80,6 +80,7 @@ struct ve_struct {
struct kmapset_key proc_perms_key;
atomic_t netns_avail_nr;
+ atomic_t netns_failcount;
int netns_max_nr;
atomic_t netif_avail_nr;
diff --git a/kernel/ve/ve.c b/kernel/ve/ve.c
index f915f86e7471e..56936ef4cc92b 100644
--- a/kernel/ve/ve.c
+++ b/kernel/ve/ve.c
@@ -100,6 +100,7 @@ struct ve_struct ve0 = {
.features = -1,
.sched_lat_ve.cur = &ve0_lat_stats,
.netns_avail_nr = ATOMIC_INIT(INT_MAX),
+ .netns_failcount = ATOMIC_INIT(0),
.netns_max_nr = INT_MAX,
.netif_avail_nr = ATOMIC_INIT(INT_MAX),
.netif_max_nr = INT_MAX,
@@ -792,6 +793,7 @@ static struct cgroup_subsys_state *ve_create(struct cgroup_subsys_state *parent_
ve->fsync_enable = FSYNC_FILTERED;
atomic_set(&ve->netns_avail_nr, NETNS_MAX_NR_DEFAULT);
+ atomic_set(&ve->netns_failcount, 0);
ve->netns_max_nr = NETNS_MAX_NR_DEFAULT;
atomic_set(&ve->netif_avail_nr, NETIF_MAX_NR_DEFAULT);
@@ -1084,6 +1086,7 @@ static const struct ve_failcount_entry {
size_t offset;
} ve_failcounts[] = {
#define VE_FC_ENTRY(name) { #name, offsetof(struct ve_struct, name##_failcount) }
+ VE_FC_ENTRY(netns),
{}
};
diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c
index b3d54cad984a9..328e1c96b6f4d 100644
--- a/net/core/net_namespace.c
+++ b/net/core/net_namespace.c
@@ -486,8 +486,10 @@ void net_drop_ns(void *p)
#ifdef CONFIG_VE
static int dec_netns_avail(struct ve_struct *ve)
{
- if (atomic_dec_if_positive(&ve->netns_avail_nr) < 0)
+ if (atomic_dec_if_positive(&ve->netns_avail_nr) < 0) {
+ ve_failcount_inc(ve, netns);
return -ENOSPC;
+ }
return 0;
}
--
2.43.0
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 16+ messages in thread* [Devel] [PATCH hci-8.1 v10 09/13] ve: add mnt failcounter
2026-09-25 16:53 [Devel] [PATCH hci-8.1 v10 00/13] Add per-VE failcount support Konstantin Khorenko
` (7 preceding siblings ...)
2026-09-25 16:53 ` [Devel] [PATCH hci-8.1 v10 08/13] ve: add netns failcounter Konstantin Khorenko
@ 2026-09-25 16:53 ` Konstantin Khorenko
2026-09-25 16:53 ` [Devel] [PATCH hci-8.1 v10 10/13] ve: add netif failcounter Konstantin Khorenko
` (5 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Konstantin Khorenko @ 2026-09-25 16:53 UTC (permalink / raw)
To: Vasileios Almpanis, Pavel Tikhomirov,
cover.1789491989.git.vladimir.riabchun, Konstantin Khorenko,
devel
From: Vladimir Riabchun <vladimir.riabchun@virtuozzo.com>
Count hits of the per-VE mount limit in ve.failcount.
The old warning in alloc_vfsmnt is dropped, otherwise every failed
mount would produce two ratelimited messages. The one printed by
ve_failcount_inc also names the counter to look at and goes to the
container log as well.
https://virtuozzo.atlassian.net/browse/VSTOR-135520
Feature: per-ve failcounters
Signed-off-by: Vladimir Riabchun <vladimir.riabchun@virtuozzo.com>
Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
fs/namespace.c | 8 +++-----
include/linux/ve.h | 1 +
kernel/ve/ve.c | 3 +++
3 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/fs/namespace.c b/fs/namespace.c
index 1d6e66cd71745..cf77c3fb98407 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -329,12 +329,8 @@ static struct mount *alloc_vfsmnt(const char *name, struct ve_struct *owner_ve)
if (!ve)
ve = get_exec_env();
- if (!ve_try_reserve_mount(ve)) {
- pr_warn_ratelimited(
- "CT#%s reached the limit on mounts.\n",
- ve_name(ve));
+ if (!ve_try_reserve_mount(ve))
return NULL;
- }
mnt = kmem_cache_zalloc(mnt_cache, GFP_KERNEL);
if (mnt) {
@@ -3380,6 +3376,8 @@ static inline int ve_try_reserve_mount(struct ve_struct *ve)
if (ret)
get_ve(ve);
+ else
+ ve_failcount_inc(ve, mnt);
return ret;
}
diff --git a/include/linux/ve.h b/include/linux/ve.h
index 969390bb511e7..0395e37208849 100644
--- a/include/linux/ve.h
+++ b/include/linux/ve.h
@@ -103,6 +103,7 @@ struct ve_struct {
* other containers.
*/
atomic_t mnt_avail_nr; /* number of available VE mounts */
+ atomic_t mnt_failcount;
int mnt_max_nr;
#ifdef CONFIG_COREDUMP
diff --git a/kernel/ve/ve.c b/kernel/ve/ve.c
index 56936ef4cc92b..4c3acda6b135e 100644
--- a/kernel/ve/ve.c
+++ b/kernel/ve/ve.c
@@ -118,6 +118,7 @@ struct ve_struct ve0 = {
.nd_neigh_nr = ATOMIC_INIT(0),
.mnt_avail_nr = ATOMIC_INIT(INT_MAX),
.mnt_max_nr = INT_MAX,
+ .mnt_failcount = ATOMIC_INIT(0),
#ifdef CONFIG_AIO
.aio_nr_lock = __SPIN_LOCK_UNLOCKED(ve0.aio_nr_lock),
.aio_nr = 0,
@@ -829,6 +830,7 @@ static struct cgroup_subsys_state *ve_create(struct cgroup_subsys_state *parent_
atomic_set(&ve->arp_neigh_nr, 0);
atomic_set(&ve->nd_neigh_nr, 0);
ve->mnt_max_nr = MNT_MAX_NR_DEFAULT;
+ atomic_set(&ve->mnt_failcount, 0);
atomic_set(&ve->mnt_avail_nr, MNT_MAX_NR_DEFAULT);
#ifdef CONFIG_COREDUMP
@@ -1087,6 +1089,7 @@ static const struct ve_failcount_entry {
} ve_failcounts[] = {
#define VE_FC_ENTRY(name) { #name, offsetof(struct ve_struct, name##_failcount) }
VE_FC_ENTRY(netns),
+ VE_FC_ENTRY(mnt),
{}
};
--
2.43.0
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 16+ messages in thread* [Devel] [PATCH hci-8.1 v10 10/13] ve: add netif failcounter
2026-09-25 16:53 [Devel] [PATCH hci-8.1 v10 00/13] Add per-VE failcount support Konstantin Khorenko
` (8 preceding siblings ...)
2026-09-25 16:53 ` [Devel] [PATCH hci-8.1 v10 09/13] ve: add mnt failcounter Konstantin Khorenko
@ 2026-09-25 16:53 ` Konstantin Khorenko
2026-09-25 16:53 ` [Devel] [PATCH hci-8.1 v10 11/13] ve: add bpf_prog failcounter Konstantin Khorenko
` (4 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Konstantin Khorenko @ 2026-09-25 16:53 UTC (permalink / raw)
To: Vasileios Almpanis, Pavel Tikhomirov,
cover.1789491989.git.vladimir.riabchun, Konstantin Khorenko,
devel
From: Vladimir Riabchun <vladimir.riabchun@virtuozzo.com>
Count hits of the per-VE network interface limit in ve.failcount.
The per-site "increase ve::netif_max_nr" hints are replaced by the
unified message from ve_failcount_inc.
https://virtuozzo.atlassian.net/browse/VSTOR-135520
Feature: per-ve failcounters
Signed-off-by: Vladimir Riabchun <vladimir.riabchun@virtuozzo.com>
Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
include/linux/ve.h | 1 +
kernel/ve/ve.c | 3 +++
net/core/dev.c | 10 ++--------
3 files changed, 6 insertions(+), 8 deletions(-)
diff --git a/include/linux/ve.h b/include/linux/ve.h
index 0395e37208849..5d94470692f72 100644
--- a/include/linux/ve.h
+++ b/include/linux/ve.h
@@ -84,6 +84,7 @@ struct ve_struct {
int netns_max_nr;
atomic_t netif_avail_nr;
+ atomic_t netif_failcount;
int netif_max_nr;
atomic_t bpf_prog_avail_nr;
diff --git a/kernel/ve/ve.c b/kernel/ve/ve.c
index 4c3acda6b135e..8505c1dae4a18 100644
--- a/kernel/ve/ve.c
+++ b/kernel/ve/ve.c
@@ -103,6 +103,7 @@ struct ve_struct ve0 = {
.netns_failcount = ATOMIC_INIT(0),
.netns_max_nr = INT_MAX,
.netif_avail_nr = ATOMIC_INIT(INT_MAX),
+ .netif_failcount = ATOMIC_INIT(0),
.netif_max_nr = INT_MAX,
.bpf_prog_avail_nr = ATOMIC_INIT(INT_MAX),
.bpf_prog_max_nr = INT_MAX,
@@ -798,6 +799,7 @@ static struct cgroup_subsys_state *ve_create(struct cgroup_subsys_state *parent_
ve->netns_max_nr = NETNS_MAX_NR_DEFAULT;
atomic_set(&ve->netif_avail_nr, NETIF_MAX_NR_DEFAULT);
+ atomic_set(&ve->netif_failcount, 0);
ve->netif_max_nr = NETIF_MAX_NR_DEFAULT;
atomic_set(&ve->bpf_prog_avail_nr, BPF_PROG_MAX_NR_DEFAULT);
@@ -1090,6 +1092,7 @@ static const struct ve_failcount_entry {
#define VE_FC_ENTRY(name) { #name, offsetof(struct ve_struct, name##_failcount) }
VE_FC_ENTRY(netns),
VE_FC_ENTRY(mnt),
+ VE_FC_ENTRY(netif),
{}
};
diff --git a/net/core/dev.c b/net/core/dev.c
index c7dddb200489f..7632ad1fd0151 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -10997,10 +10997,7 @@ int register_netdevice(struct net_device *dev)
ret = -ENOMEM;
if (atomic_dec_if_positive(&net->owner_ve->netif_avail_nr) < 0) {
- ve_pr_warn_ratelimited(VE_LOG_BOTH,
- "CT%s: hits max number of network devices, "
- "increase ve::netif_max_nr parameter\n",
- ve_name(net->owner_ve));
+ ve_failcount_inc(net->owner_ve, netif);
return ret;
}
@@ -12211,10 +12208,7 @@ int __dev_change_net_namespace(struct net_device *dev, struct net *net,
err = -ENOMEM;
if (atomic_dec_if_positive(&net->owner_ve->netif_avail_nr) < 0) {
- ve_pr_warn_ratelimited(VE_LOG_BOTH,
- "CT%s: hits max number of network devices, "
- "increase ve::netif_max_nr parameter\n",
- ve_name(net->owner_ve));
+ ve_failcount_inc(net->owner_ve, netif);
goto out;
}
atomic_inc(&dev_net(dev)->owner_ve->netif_avail_nr);
--
2.43.0
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 16+ messages in thread* [Devel] [PATCH hci-8.1 v10 11/13] ve: add bpf_prog failcounter
2026-09-25 16:53 [Devel] [PATCH hci-8.1 v10 00/13] Add per-VE failcount support Konstantin Khorenko
` (9 preceding siblings ...)
2026-09-25 16:53 ` [Devel] [PATCH hci-8.1 v10 10/13] ve: add netif failcounter Konstantin Khorenko
@ 2026-09-25 16:53 ` Konstantin Khorenko
2026-09-25 16:53 ` [Devel] [PATCH hci-8.1 v10 12/13] ve: add aio failcounter Konstantin Khorenko
` (3 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Konstantin Khorenko @ 2026-09-25 16:53 UTC (permalink / raw)
To: Vasileios Almpanis, Pavel Tikhomirov,
cover.1789491989.git.vladimir.riabchun, Konstantin Khorenko,
devel
From: Vladimir Riabchun <vladimir.riabchun@virtuozzo.com>
Count hits of the per-VE BPF program limit in ve.failcount.
https://virtuozzo.atlassian.net/browse/VSTOR-135520
Feature: per-ve failcounters
Signed-off-by: Vladimir Riabchun <vladimir.riabchun@virtuozzo.com>
Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
include/linux/ve.h | 1 +
kernel/bpf/syscall.c | 1 +
kernel/ve/ve.c | 3 +++
3 files changed, 5 insertions(+)
diff --git a/include/linux/ve.h b/include/linux/ve.h
index 5d94470692f72..b4c9e628cb333 100644
--- a/include/linux/ve.h
+++ b/include/linux/ve.h
@@ -88,6 +88,7 @@ struct ve_struct {
int netif_max_nr;
atomic_t bpf_prog_avail_nr;
+ atomic_t bpf_prog_failcount;
int bpf_prog_max_nr;
atomic64_t _uevent_seqnum;
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index c94d4240e3d3d..8498bc9df7ea6 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -2891,6 +2891,7 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
if (!bpf_cap && type == BPF_PROG_TYPE_CGROUP_DEVICE) {
load_ve = get_exec_env();
if (atomic_dec_if_positive(&load_ve->bpf_prog_avail_nr) < 0) {
+ ve_failcount_inc(load_ve, bpf_prog);
load_ve = NULL;
err = -ENOSPC;
goto put_token;
diff --git a/kernel/ve/ve.c b/kernel/ve/ve.c
index 8505c1dae4a18..be45975b74045 100644
--- a/kernel/ve/ve.c
+++ b/kernel/ve/ve.c
@@ -106,6 +106,7 @@ struct ve_struct ve0 = {
.netif_failcount = ATOMIC_INIT(0),
.netif_max_nr = INT_MAX,
.bpf_prog_avail_nr = ATOMIC_INIT(INT_MAX),
+ .bpf_prog_failcount = ATOMIC_INIT(0),
.bpf_prog_max_nr = INT_MAX,
.fsync_enable = FSYNC_FILTERED,
._randomize_va_space =
@@ -803,6 +804,7 @@ static struct cgroup_subsys_state *ve_create(struct cgroup_subsys_state *parent_
ve->netif_max_nr = NETIF_MAX_NR_DEFAULT;
atomic_set(&ve->bpf_prog_avail_nr, BPF_PROG_MAX_NR_DEFAULT);
+ atomic_set(&ve->bpf_prog_failcount, 0);
ve->bpf_prog_max_nr = BPF_PROG_MAX_NR_DEFAULT;
err = ve_log_init(ve);
@@ -1093,6 +1095,7 @@ static const struct ve_failcount_entry {
VE_FC_ENTRY(netns),
VE_FC_ENTRY(mnt),
VE_FC_ENTRY(netif),
+ VE_FC_ENTRY(bpf_prog),
{}
};
--
2.43.0
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 16+ messages in thread* [Devel] [PATCH hci-8.1 v10 12/13] ve: add aio failcounter
2026-09-25 16:53 [Devel] [PATCH hci-8.1 v10 00/13] Add per-VE failcount support Konstantin Khorenko
` (10 preceding siblings ...)
2026-09-25 16:53 ` [Devel] [PATCH hci-8.1 v10 11/13] ve: add bpf_prog failcounter Konstantin Khorenko
@ 2026-09-25 16:53 ` Konstantin Khorenko
2026-09-25 16:53 ` [Devel] [PATCH hci-8.1 v10 13/13] selftests/ve: add mount accounting selftest Konstantin Khorenko
` (2 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Konstantin Khorenko @ 2026-09-25 16:53 UTC (permalink / raw)
To: Vasileios Almpanis, Pavel Tikhomirov,
cover.1789491989.git.vladimir.riabchun, Konstantin Khorenko,
devel
From: Vladimir Riabchun <vladimir.riabchun@virtuozzo.com>
Count hits of the per-VE aio requests limit in ve.failcount.
https://virtuozzo.atlassian.net/browse/VSTOR-135520
Feature: per-ve failcounters
Signed-off-by: Vladimir Riabchun <vladimir.riabchun@virtuozzo.com>
Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
fs/aio.c | 1 +
include/linux/ve.h | 1 +
kernel/ve/ve.c | 5 +++++
3 files changed, 7 insertions(+)
diff --git a/fs/aio.c b/fs/aio.c
index cb63416af135a..901cb622fe2c4 100644
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -815,6 +815,7 @@ static struct kioctx *ioctx_alloc(unsigned nr_events)
if (ve->aio_nr + ctx->max_reqs > ve->aio_max_nr ||
ve->aio_nr + ctx->max_reqs < ve->aio_nr) {
spin_unlock(&ve->aio_nr_lock);
+ ve_failcount_inc(ve, aio);
err = -EAGAIN;
goto err_ctx;
}
diff --git a/include/linux/ve.h b/include/linux/ve.h
index b4c9e628cb333..a400d21593b25 100644
--- a/include/linux/ve.h
+++ b/include/linux/ve.h
@@ -133,6 +133,7 @@ struct ve_struct {
spinlock_t aio_nr_lock;
unsigned long aio_nr;
unsigned long aio_max_nr;
+ atomic_t aio_failcount;
#endif
struct vfsmount *devtmpfs_mnt;
};
diff --git a/kernel/ve/ve.c b/kernel/ve/ve.c
index be45975b74045..51cc635be2e24 100644
--- a/kernel/ve/ve.c
+++ b/kernel/ve/ve.c
@@ -125,6 +125,7 @@ struct ve_struct ve0 = {
.aio_nr_lock = __SPIN_LOCK_UNLOCKED(ve0.aio_nr_lock),
.aio_nr = 0,
.aio_max_nr = AIO_MAX_NR_DEFAULT,
+ .aio_failcount = ATOMIC_INIT(0),
#endif
.meminfo_val = VE_MEMINFO_SYSTEM,
.umh_running_helpers = ATOMIC_INIT(0),
@@ -847,6 +848,7 @@ static struct cgroup_subsys_state *ve_create(struct cgroup_subsys_state *parent_
spin_lock_init(&ve->aio_nr_lock);
ve->aio_nr = 0;
ve->aio_max_nr = AIO_MAX_NR_DEFAULT;
+ atomic_set(&ve->aio_failcount, 0);
#endif
return &ve->css;
@@ -1096,6 +1098,9 @@ static const struct ve_failcount_entry {
VE_FC_ENTRY(mnt),
VE_FC_ENTRY(netif),
VE_FC_ENTRY(bpf_prog),
+#ifdef CONFIG_AIO
+ VE_FC_ENTRY(aio),
+#endif
{}
};
--
2.43.0
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 16+ messages in thread* [Devel] [PATCH hci-8.1 v10 13/13] selftests/ve: add mount accounting selftest
2026-09-25 16:53 [Devel] [PATCH hci-8.1 v10 00/13] Add per-VE failcount support Konstantin Khorenko
` (11 preceding siblings ...)
2026-09-25 16:53 ` [Devel] [PATCH hci-8.1 v10 12/13] ve: add aio failcounter Konstantin Khorenko
@ 2026-09-25 16:53 ` Konstantin Khorenko
2026-09-25 17:10 ` [Devel] [PATCH hci-8.1 v10 00/13] Add per-VE failcount support Vladimir Riabchun
2026-09-25 17:33 ` Konstantin Khorenko
14 siblings, 0 replies; 16+ messages in thread
From: Konstantin Khorenko @ 2026-09-25 16:53 UTC (permalink / raw)
To: Vasileios Almpanis, Pavel Tikhomirov,
cover.1789491989.git.vladimir.riabchun, Konstantin Khorenko,
devel
From: Vladimir Riabchun <vladimir.riabchun@virtuozzo.com>
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 <vladimir.riabchun@virtuozzo.com>
Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
tools/testing/selftests/ve/.gitignore | 1 +
tools/testing/selftests/ve/Makefile | 1 +
.../selftests/ve/ve_mount_accounting_test.c | 554 +++++++++++++++++++++
tools/testing/selftests/ve/ve_selftest.h | 21 +
4 files changed, 577 insertions(+)
diff --git a/tools/testing/selftests/ve/.gitignore b/tools/testing/selftests/ve/.gitignore
index afa4c568c2c9b..3df4d05888dce 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 ec40cbc7b3a1b..c6efe7c4b4fba 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 0000000000000..e2a882137e11d
--- /dev/null
+++ b/tools/testing/selftests/ve/ve_mount_accounting_test.c
@@ -0,0 +1,554 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ve_mount_accounting selftests
+ *
+ * Tests to check the correctness of mount accounting.
+ */
+#define _GNU_SOURCE
+#include <asm/unistd.h>
+#include <linux/sched.h>
+#include <linux/limits.h>
+#include <sys/wait.h>
+#include <sys/syscall.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sched.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+
+#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;
+
+FIXTURE(ve_mnt_acc)
+{
+ int cgv2_fd;
+ int ctid;
+ struct namespace_ctx ns_ctx;
+ int orig_have_mnt;
+};
+
+/*
+ * free_vfsmnt is called via call_rcu, so we need to ensure
+ * that all pending call_rcu tasks are finished.
+ * However, one barrier is not enough:
+ * mntput_no_expire
+ * init_task_work(&mnt->mnt_rcu, __cleanup_mnt);
+ * task_work_add(task, &mnt->mnt_rcu, TWA_RESUME)
+ * OR
+ * schedule_delayed_work(&delayed_mntput_work, 1)
+ * # Both later call cleanup_mnt
+ * cleanup_mnt
+ * call_rcu(&mnt->mnt_rcu, delayed_free_vfsmnt);
+ *
+ * We can't enforce workqueue flush from userspace, so instead
+ * read several times until the value is the one we expect.
+ */
+static int wait_on_mnt_avail(FIXTURE_DATA(ve_mnt_acc) * self, int expected)
+{
+#define NSEC_PER_SEC (1000ULL * 1000 * 1000)
+#define WAIT_TIMEOUT_NS (4096ULL * 1000 * 1000)
+ char path_avail[64];
+ int mnt_avail_nr = 0;
+ unsigned long long delay_ns = 1000000;
+ struct timespec delay;
+
+ snprintf(path_avail, sizeof(path_avail), "%d/ve.mnt_avail_nr", self->ctid);
+
+ while (delay_ns <= WAIT_TIMEOUT_NS) {
+ if (read_s32_at(self->cgv2_fd, path_avail, &mnt_avail_nr)) {
+ fprintf(stderr, "Failed to read from %s\n", path_avail);
+ return 1;
+ }
+ if (mnt_avail_nr == expected)
+ return 0;
+
+ do_rcu_barrier();
+ delay.tv_sec = delay_ns / NSEC_PER_SEC;
+ delay.tv_nsec = delay_ns % NSEC_PER_SEC;
+ if (nanosleep(&delay, NULL)) {
+ fprintf(stderr, "nanosleep failed: %s\n", strerror(errno));
+ return 1;
+ }
+ delay_ns *= 2;
+ }
+ fprintf(stderr, "Didn't get expected value at %s\n", path_avail);
+ fprintf(stderr, "Expected %d, last read %d\n", expected, mnt_avail_nr);
+ return -1;
+}
+
+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;
+}
+
+/* Create a mount and wait until mount counter is valid */
+static int create_mount(FIXTURE_DATA(ve_mnt_acc) * self, int id)
+{
+ int ret, avail_before;
+ char path[64];
+
+ snprintf(path, sizeof(path), "%d/ve.mnt_avail_nr", self->ctid);
+
+ if (read_s32_at(self->cgv2_fd, path, &avail_before)) {
+ fprintf(stderr, "Failed to read mnt_avail_nr in create_mount\n");
+ return 1;
+ }
+
+ ret = run_in_ve(self->cgv2_fd, self->ctid, &self->ns_ctx,
+ CLONE_NEWVE | CLONE_NEWNS, _create_mount, &id);
+
+ if (ret)
+ wait_on_mnt_avail(self, avail_before);
+ else
+ ret = wait_on_mnt_avail(self, avail_before - 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;
+}
+
+/* Do umount and ensure that mnt_avail_nr is valid on exit(+1) */
+static int destroy_mount(FIXTURE_DATA(ve_mnt_acc) * self, int id)
+{
+ int ret, avail_before;
+ char path[64];
+
+ snprintf(path, sizeof(path), "%d/ve.mnt_avail_nr", self->ctid);
+
+ if (read_s32_at(self->cgv2_fd, path, &avail_before)) {
+ fprintf(stderr, "Failed to read mnt_avail_nr in create_mount\n");
+ return 1;
+ }
+
+ ret = run_in_ve(self->cgv2_fd, self->ctid, &self->ns_ctx,
+ CLONE_NEWVE | CLONE_NEWNS, _destroy_mount, &id);
+
+ /* Cost must be exactly 1 */
+ if (!ret)
+ ret = wait_on_mnt_avail(self, avail_before + 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;
+}
+
+/* Returns 0 if mount failed OR mount cost is not 1 */
+static int try_mount(FIXTURE_DATA(ve_mnt_acc) * self)
+{
+ int mnt_id = get_free_mnt_id();
+
+ if (mnt_id < 0 || create_mount(self, mnt_id))
+ return 0;
+
+ return destroy_mount(self, mnt_id) == 0;
+}
+
+/*
+ * 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 (try_mount(self))
+ r = mid;
+ else
+ l = 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);
+
+ ASSERT_GE(*need_avail, 1);
+ /* try_mount ensures cost is 1 */
+ ASSERT_EQ(try_mount(self), 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 - 1);
+
+ /* 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 + 1 + *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 - 1);
+
+ /* 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), 0);
+ ASSERT_EQ(read_s32_at(self->cgv2_fd, path_avail, &mnt_avail_nr), 0);
+ ASSERT_EQ(mnt_avail_nr, -1);
+
+ /* Step 6: try to do mount when avail < 0, ensure number is intact */
+ ASSERT_EQ(check_mount_fails(_metadata, self, 2, -1), 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);
+ 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 - 1);
+
+ 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 - 1);
+ ASSERT_EQ(mount_and_get_avail(_metadata, self, 1), *need_avail - 2);
+
+ /* 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);
+
+ ASSERT_EQ(set_pseudosuper(self, 0), 0);
+
+ /* Below zero should fail */
+ ASSERT_EQ(check_mount_fails(_metadata, self, 3, -3), 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 + *need_avail - 2), 0);
+ ASSERT_EQ(check_mount_fails(_metadata, self, 3, *need_avail - 2), 0);
+
+ ASSERT_EQ(destroy_mount(self, 0), 0);
+ ASSERT_EQ(check_mount_fails(_metadata, self, 3, *need_avail - 1), 0);
+ ASSERT_EQ(destroy_mount(self, 2), 0);
+
+ ASSERT_EQ(try_mount(self), 1);
+}
+
+/* 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 - 1);
+ ASSERT_EQ(set_pseudosuper(self, 1), 0);
+
+ /* Cost is the same when mount/umount happen under pseudosuper. */
+ ASSERT_EQ(try_mount(self), 1);
+
+ /* mnt 1 - mounted with pseudosuper, umounted without it. */
+ ASSERT_EQ(mount_and_get_avail(_metadata, self, 1),
+ orig_mnt_avail - 2);
+
+ 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 - 1);
+
+ 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)
+{
+ need_avail = mmap(NULL, sizeof(int),
+ PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+
+ if (need_avail == MAP_FAILED) {
+ fprintf(stderr, "Failed to mmap shared region: %s\n", strerror(errno));
+ return 1;
+ }
+ *need_avail = -1;
+
+ return test_harness_run(argc, argv);
+}
diff --git a/tools/testing/selftests/ve/ve_selftest.h b/tools/testing/selftests/ve/ve_selftest.h
index 7b45f91c1d561..1fc3f8dcac180 100644
--- a/tools/testing/selftests/ve/ve_selftest.h
+++ b/tools/testing/selftests/ve/ve_selftest.h
@@ -416,4 +416,25 @@ static inline int is_mounted(const char *path)
return errno == EXDEV ? 1 : -1;
}
+static inline int do_rcu_barrier(void)
+{
+ int fd;
+
+ errno = 0;
+ fd = open("/sys/module/rcutree/parameters/do_rcu_barrier", O_WRONLY);
+
+ if (fd < 0) {
+ fprintf(stderr, "Failed to open do_rcu_barrier: %s\n", strerror(errno));
+ return -1;
+ }
+ errno = 0;
+ if (write(fd, "1", 2) != 2) {
+ fprintf(stderr, "Failed to issue rcu_barrier: %s\n", strerror(errno));
+ close(fd);
+ return -1;
+ }
+ close(fd);
+ return 0;
+}
+
#endif /* __SELFTESTS_VE_VE_SELFTEST_H */
--
2.43.0
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 16+ messages in thread* Re: [Devel] [PATCH hci-8.1 v10 00/13] Add per-VE failcount support
2026-09-25 16:53 [Devel] [PATCH hci-8.1 v10 00/13] Add per-VE failcount support Konstantin Khorenko
` (12 preceding siblings ...)
2026-09-25 16:53 ` [Devel] [PATCH hci-8.1 v10 13/13] selftests/ve: add mount accounting selftest Konstantin Khorenko
@ 2026-09-25 17:10 ` Vladimir Riabchun
2026-09-25 17:33 ` Konstantin Khorenko
14 siblings, 0 replies; 16+ messages in thread
From: Vladimir Riabchun @ 2026-09-25 17:10 UTC (permalink / raw)
To: Konstantin Khorenko, Vasileios Almpanis, Pavel Tikhomirov,
cover.1789491989.git.vladimir.riabchun, devel
On 9/25/26 18:53, Konstantin Khorenko wrote:
> This patchset introduces per-VE failcount support.
> We have several resources that have per-VE limits, the users might
> want to know what limit hits make operations fail.
>
> - Patch 1 is cosmetic, nothing special.
> - Patches 2-4 change mount number and limit processing, unifying it
> with other resources. VE selftest is updated in patch 3.
> - Patch 5 removes copy-pasted resource accessors by generating them
> with macros.
> - Patch 6 is a split-out fix: ve0 was left without aio defaults when
> aio_nr/aio_max_nr moved into ve_struct.
> - Patch 7 adds the ve.failcount file and the helpers.
> - Patches 8-12 add one failcounter each: netns, mnt, netif,
> bpf_prog, aio.
> - Patch 13 introduces new VE selftest that checks mounts accounting,
> limits and failcount updates.
>
> NOTE: for running the new selftest this series should be applied on
> top of "[PATCH VZ10 v3 0/3] Improvements for ve selftests".
>
> https://virtuozzo.atlassian.net/browse/VSTOR-135520
>
> v9: <cover.1789491989.git.vladimir.riabchun@virtuozzo.com>
>
> Changes since v9 (respin by khorenko@):
> - Dropped v9 patch 7 "ve/printk: Don't use ve log outside task
> context": get_exec_env() being unreliable in interrupt context is
> a pre-existing problem, it will be fixed properly in a separate
> series under VSTOR-146839.
> - Dropped the neigh_tbl failcounter: neigh_alloc() can run in
> softirq, and ve_failcount_inc() writes to the ve log, which must
> not be used outside task context. Without the dropped printk
> patch this would leak messages to an unrelated CT dmesg. The
> counter will be added together with the ve_printk fix.
Will be added as a part of VSTOR-146839.
> - Split "ve: Introduce per-VE failcount" into a patch adding the
> infrastructure and one patch per counter, so each counter can be
> reviewed and reverted on its own.
> - Made the ve0 aio initialization a separate fix with a Fixes tag:
> the host has been running with aio_max_nr == 0 since
> aio_nr/aio_max_nr became per-VE, so io_setup() on the host fails
> until fs.aio-max-nr is raised by hand.
> - Patch 1 no longer moves the umh_running_helpers/umh_helpers_waitq
> ve0 initializers under #ifdef CONFIG_X86: the fields and their
> users are arch-independent, and a whitespace patch must not
> change the generated code.
> - Dropped the old mount limit warning in alloc_vfsmnt(), otherwise
> every failed mount would be reported twice; the message printed
> by ve_failcount_inc() also names the counter to look at.
> - Fixed wait_on_mnt_avail() in the selftest: the delay was kept in
> tv_nsec, and nanosleep() rejects values of 1s and above with
> EINVAL, so the wait gave up early with a misleading error instead
> of using the whole timeout. The delay is now normalized into
> tv_sec/tv_nsec.
> - Moved ve_failcount_inc() in ioctx_alloc() out of aio_nr_lock:
> the counter is an atomic and does not need the lock, no point in
> printing to two log buffers under it.
> - Reworded several subjects and commit messages.
>
Thanks a lot, Kostya.
Reviewed-by: Vladimir Riabchun <vladimir.riabchun@virtuozzo.com>
> Changes since v8:
> - Added new patch 7 to fix logs leakage from interrupts.
> We are fine if we miss some failure logs in container
> dmesg - CT can check failcounts anyway, and bringing
> new ratelimited printks with specified VE is too hard
> for such a small thing.
> - Selftest in patch 8 got major rework:
> 1. Correct mmap error handling.
> 2. Dropped mnt_cost value. Let's force 1, it is expected
> value in fresh mount+VE namespaces.
> 3. Proper solution for pending mount destructions. It used
> to be just sleep(1) which may not be enoght on loaded
> machines. Instead, issue rcu_barrier and wait on mnt_avail_nr.
> Since we forced mnt_cost == 1, it know precisely what the
> new value should be.
>
> Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
>
> ---
> Vladimir Riabchun (13):
> ve/ve.{h, c}: farewell to spaces as indents
> ve/fs: count available mounts instead of used ones
> selftests/ve: update ve_ns_owner_test
> ve: make mount limit configurable per VE
> ve/ve.c: generate VE resource accessors using macros
> ve/aio: initialize aio fields in ve0
> ve: introduce per-VE failcounter infrastructure
> ve: add netns failcounter
> ve: add mnt failcounter
> ve: add netif failcounter
> ve: add bpf_prog failcounter
> ve: add aio failcounter
> selftests/ve: add mount accounting selftest
>
> fs/aio.c | 1 +
> fs/namespace.c | 82 +--
> include/linux/ve.h | 34 +-
> kernel/bpf/syscall.c | 1 +
> kernel/ve/ve.c | 274 +++++-----
> kernel/ve/veowner.c | 19 -
> net/core/dev.c | 10 +-
> net/core/net_namespace.c | 4 +-
> tools/testing/selftests/ve/.gitignore | 1 +
> tools/testing/selftests/ve/Makefile | 1 +
> .../selftests/ve/ve_mount_accounting_test.c | 554 +++++++++++++++++++++
> tools/testing/selftests/ve/ve_ns_owner_test.c | 37 +-
> tools/testing/selftests/ve/ve_selftest.h | 21 +
> 13 files changed, 816 insertions(+), 223 deletions(-)
> ---
> base-commit: 03a6f36081f7055a2e41f9b79306a3ab0103d883
> change-id: 20260925-b4-kh-ve_fail-6271523cb384
>
> Best regards,
> --
> Konstantin Khorenko <khorenko@virtuozzo.com>
>
--
Best regards, Riabchun Vladimir
Linux Kernel Developer, Virtuozzo
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 16+ messages in thread* Re: [Devel] [PATCH hci-8.1 v10 00/13] Add per-VE failcount support
2026-09-25 16:53 [Devel] [PATCH hci-8.1 v10 00/13] Add per-VE failcount support Konstantin Khorenko
` (13 preceding siblings ...)
2026-09-25 17:10 ` [Devel] [PATCH hci-8.1 v10 00/13] Add per-VE failcount support Vladimir Riabchun
@ 2026-09-25 17:33 ` Konstantin Khorenko
14 siblings, 0 replies; 16+ messages in thread
From: Konstantin Khorenko @ 2026-09-25 17:33 UTC (permalink / raw)
To: Vasileios Almpanis, Pavel Tikhomirov,
cover.1789491989.git.vladimir.riabchun, devel,
Konstantin Khorenko
On Fri, 25 Sep 2026 18:53:21 +0200, Konstantin Khorenko wrote:
> Add per-VE failcount support
>
> This patchset introduces per-VE failcount support.
> We have several resources that have per-VE limits, the users might
> want to know what limit hits make operations fail.
>
> - Patch 1 is cosmetic, nothing special.
> - Patches 2-4 change mount number and limit processing, unifying it
> with other resources. VE selftest is updated in patch 3.
> - Patch 5 removes copy-pasted resource accessors by generating them
> with macros.
> - Patch 6 is a split-out fix: ve0 was left without aio defaults when
> aio_nr/aio_max_nr moved into ve_struct.
> - Patch 7 adds the ve.failcount file and the helpers.
> - Patches 8-12 add one failcounter each: netns, mnt, netif,
> bpf_prog, aio.
> - Patch 13 introduces new VE selftest that checks mounts accounting,
> limits and failcount updates.
>
> [...]
Applied to branch-rh10-6.12.0-211.52.1.22.x.vz10-ovz, will be released in rh10-6.12.0-211.52.1.22.4.vz10, thanks!
[01/13] ve/ve.{h, c}: farewell to spaces as indents
https://bitbucket.org/virtuozzocore/vzkernel.vzs/commits/94e1f3e566b2
[02/13] ve/fs: count available mounts instead of used ones
https://bitbucket.org/virtuozzocore/vzkernel.vzs/commits/b9c2e2d6ee85
[03/13] selftests/ve: update ve_ns_owner_test
https://bitbucket.org/virtuozzocore/vzkernel.vzs/commits/81eba31300aa
[04/13] ve: make mount limit configurable per VE
https://bitbucket.org/virtuozzocore/vzkernel.vzs/commits/dd2667127d88
[05/13] ve/ve.c: generate VE resource accessors using macros
https://bitbucket.org/virtuozzocore/vzkernel.vzs/commits/d7411af14db5
[06/13] ve/aio: initialize aio fields in ve0
https://bitbucket.org/virtuozzocore/vzkernel.vzs/commits/bc9ae303c2e3
[07/13] ve: introduce per-VE failcounter infrastructure
https://bitbucket.org/virtuozzocore/vzkernel.vzs/commits/82fcf1c48466
[08/13] ve: add netns failcounter
https://bitbucket.org/virtuozzocore/vzkernel.vzs/commits/edd7353a94df
[09/13] ve: add mnt failcounter
https://bitbucket.org/virtuozzocore/vzkernel.vzs/commits/0f50eece91e9
[10/13] ve: add netif failcounter
https://bitbucket.org/virtuozzocore/vzkernel.vzs/commits/4a6c30eb6f86
[11/13] ve: add bpf_prog failcounter
https://bitbucket.org/virtuozzocore/vzkernel.vzs/commits/0755704f524a
[12/13] ve: add aio failcounter
https://bitbucket.org/virtuozzocore/vzkernel.vzs/commits/77b09350431f
[13/13] selftests/ve: add mount accounting selftest
https://bitbucket.org/virtuozzocore/vzkernel.vzs/commits/32609fbd61a6
--
Konstantin Khorenko <khorenko@virtuozzo.com>
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 16+ messages in thread