[Devel] [PATCH VZ10 v2 6/6] ve: Introduce per-VE failcount

Vasileios Almpanis vasileios.almpanis at virtuozzo.com
Mon Jul 20 11:53:01 MSK 2026


On 7/16/26 10:50 PM, Vladimir Riabchun wrote:
> 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.
>
> One change, unrelated to failcounts: aio fields are now initialized
> in ve0.
>
> https://virtuozzo.atlassian.net/browse/VSTOR-135520
>
> Feature: per-ve failcounters
> Signed-off-by: Vladimir Riabchun <vladimir.riabchun at virtuozzo.com>
> ---
>   fs/aio.c                 |  1 +
>   fs/namespace.c           |  2 ++
>   include/linux/ve.h       |  6 ++++
>   kernel/bpf/syscall.c     |  1 +
>   kernel/ve/ve.c           | 65 ++++++++++++++++++++++++++++++++++++++++
>   net/core/dev.c           |  2 ++
>   net/core/neighbour.c     |  1 +
>   net/core/net_namespace.c |  4 ++-
>   8 files changed, 81 insertions(+), 1 deletion(-)
>
> diff --git a/fs/aio.c b/fs/aio.c
> index cb63416af135..3fa07cc626f8 100644
> --- a/fs/aio.c
> +++ b/fs/aio.c
> @@ -814,6 +814,7 @@ static struct kioctx *ioctx_alloc(unsigned nr_events)
>   	spin_lock(&ve->aio_nr_lock);
>   	if (ve->aio_nr + ctx->max_reqs > ve->aio_max_nr ||
>   	    ve->aio_nr + ctx->max_reqs < ve->aio_nr) {
> +		atomic_inc(&ve->aio_failcount);
>   		spin_unlock(&ve->aio_nr_lock);
>   		err = -EAGAIN;
>   		goto err_ctx;
> diff --git a/fs/namespace.c b/fs/namespace.c
> index c207e5d092be..ad0243dd827d 100644
> --- a/fs/namespace.c
> +++ b/fs/namespace.c
> @@ -3272,6 +3272,8 @@ static inline int ve_try_reserve_mount(struct ve_struct *ve)
>   	int ret = ve_is_super(ve) || atomic_dec_if_positive(&ve->mnt_avail_nr) >= 0;
>   	if (ret)
>   		get_ve(ve);
> +	else
> +		atomic_inc(&ve->mnt_failcount);
>   	return ret;
>   }
>   
> diff --git a/include/linux/ve.h b/include/linux/ve.h
> index 5a5413107eef..1f8b6fd0c289 100644
> --- a/include/linux/ve.h
> +++ b/include/linux/ve.h
> @@ -71,12 +71,15 @@ struct ve_struct {
>   	struct kmapset_key	sysfs_perms_key;
>   
>   	atomic_t		netns_avail_nr;
> +	atomic_t		netns_failcount;
>   	int			netns_max_nr;
>   
>   	atomic_t		netif_avail_nr;
> +	atomic_t		netif_failcount;
>   	int			netif_max_nr;
>   
>   	atomic_t		bpf_prog_avail_nr;
> +	atomic_t		bpf_prog_failcount;
>   	int			bpf_prog_max_nr;
>   
>   	atomic64_t		_uevent_seqnum;
> @@ -85,6 +88,7 @@ struct ve_struct {
>   
>   	atomic_t		arp_neigh_nr;
>   	atomic_t		nd_neigh_nr;
> +	atomic_t		neigh_tbl_failcount;
>   	unsigned long		meminfo_val;
>   
>   	/*
> @@ -93,6 +97,7 @@ struct ve_struct {
>   	 * other containers.
>   	 */
>   	atomic_t		mnt_avail_nr; /* number of present VE mounts */
> +	atomic_t		mnt_failcount;
>   	int			mnt_max_nr;
>   
>   #ifdef CONFIG_COREDUMP
> @@ -120,6 +125,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/bpf/syscall.c b/kernel/bpf/syscall.c
> index ff2a51c59f04..95e806fa19f4 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) {
> +			atomic_inc(&load_ve->bpf_prog_failcount);
>   			load_ve = NULL;
>   			err = -ENOSPC;
>   			goto put_token;
> diff --git a/kernel/ve/ve.c b/kernel/ve/ve.c
> index ba18141cce9d..ac953c553908 100644
> --- a/kernel/ve/ve.c
> +++ b/kernel/ve/ve.c
> @@ -96,10 +96,13 @@ 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_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	=
> @@ -111,8 +114,16 @@ struct ve_struct ve0 = {
>   
>   	.arp_neigh_nr		= ATOMIC_INIT(0),
>   	.nd_neigh_nr		= ATOMIC_INIT(0),
> +	.neigh_tbl_failcount	= 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(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),
>   	.umh_helpers_waitq	= __WAIT_QUEUE_HEAD_INITIALIZER(ve0.umh_helpers_waitq),
> @@ -777,12 +788,15 @@ 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);
NIT: ve_struct is allocated using kmem_cache_zalloc so setting 
failcounts to 0
is redundant.
> +	atomic_set(&ve->netns_failcount, 0);
>   	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);
> +	atomic_set(&ve->bpf_prog_failcount, 0);
>   	ve->bpf_prog_max_nr = BPF_PROG_MAX_NR_DEFAULT;
>   
>   	err = ve_log_init(ve);
> @@ -808,7 +822,9 @@ 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->neigh_tbl_failcount, 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
> @@ -821,6 +837,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;
> @@ -1059,6 +1076,48 @@ VE_RESOURCE(mnt);
>   VE_RESOURCE(netif);
>   VE_RESOURCE(bpf_prog);
>   
> +struct ve_failcount_entry {
> +	const char *name;
> +	size_t offset;
> +} ve_failcounts[] = {
> +#define VE_FC_ENTRY(name) { #name, offsetof(struct ve_struct, name##_failcount) }
> +	VE_FC_ENTRY(netns),
> +	VE_FC_ENTRY(mnt),
> +	VE_FC_ENTRY(netif),
> +	VE_FC_ENTRY(bpf_prog),
> +	VE_FC_ENTRY(neigh_tbl),
> +	VE_FC_ENTRY(aio),
> +	{}
> +};
> +
> +static int ve_failcount_read(struct seq_file *sf, void *v)
> +{
> +	struct ve_struct *ve = css_to_ve(seq_css(sf));
> +	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));
> +	struct ve_failcount_entry *entry;
> +	atomic_t *fc;
> +
> +	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);
> @@ -1596,6 +1655,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,
> +	},
>   	{ }
>   };
>   
> diff --git a/net/core/dev.c b/net/core/dev.c
> index c7dddb200489..05e0b9b6ba23 100644
> --- a/net/core/dev.c
> +++ b/net/core/dev.c
> @@ -10997,6 +10997,7 @@ int register_netdevice(struct net_device *dev)
>   
>   	ret = -ENOMEM;
>   	if (atomic_dec_if_positive(&net->owner_ve->netif_avail_nr) < 0) {
> +		atomic_inc(&net->owner_ve->netif_failcount);
>   		ve_pr_warn_ratelimited(VE_LOG_BOTH,
>   			"CT%s: hits max number of network devices, "
>   			"increase ve::netif_max_nr parameter\n",
> @@ -12211,6 +12212,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) {
> +		atomic_inc(&net->owner_ve->netif_failcount);
>   		ve_pr_warn_ratelimited(VE_LOG_BOTH,
>   			"CT%s: hits max number of network devices, "
>   			"increase ve::netif_max_nr parameter\n",
> diff --git a/net/core/neighbour.c b/net/core/neighbour.c
> index f90deb17fb25..57a49d9c98a7 100644
> --- a/net/core/neighbour.c
> +++ b/net/core/neighbour.c
> @@ -520,6 +520,7 @@ static struct neighbour *neigh_alloc(struct neigh_table *tbl,
>   	    (glob_entries >= READ_ONCE(tbl->gc_thresh2) &&
>   	     time_after(now, READ_ONCE(tbl->last_flush) + 5 * HZ))) {
>   		if (!neigh_forced_gc(tbl, ve) && entries >= gc_thresh3) {
> +			atomic_inc(&ve->neigh_tbl_failcount);
>   			net_info_ratelimited("%s: neighbor table overflow!\n",
>   					     tbl->id);
>   			NEIGH_CACHE_STAT_INC(tbl, table_fulls);
> diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c
> index b3d54cad984a..9a3376d2682f 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) {
> +		atomic_inc(&ve->netns_failcount);
>   		return -ENOSPC;
> +	}
>   	return 0;
>   }
>   

-- 
Best regards, Vasileios Almpanis
Software Developer, Virtuozzo.



More information about the Devel mailing list