From mboxrd@z Thu Jan 1 00:00:00 1970 From: Vasileios Almpanis Date: Wed, 19 Aug 2026 13:16:17 +0000 Subject: Re: [Devel] [PATCH VZ10 v6 7/9] ve: Introduce per-VE failcount In-Reply-To: References: Message-ID: <178714537779.798038.4175166408746452014.b4-review@b4> List-Id: > 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 > > 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 c9e2ab9b3b57..eeeb2f780e46 100644 > --- a/fs/namespace.c > +++ b/fs/namespace.c > @@ -3369,6 +3369,8 @@ static inline int ve_try_reserve_mount(struct ve_struct *ve) > > 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 5687faad46ff..9e73527e970e 100644 > --- a/include/linux/ve.h > +++ b/include/linux/ve.h > @@ -72,12 +72,15 @@ 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; > + 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; > @@ -86,6 +89,7 @@ struct ve_struct { > > atomic_t arp_neigh_nr; > atomic_t nd_neigh_nr; > + atomic_t neigh_tbl_failcount; > unsigned long meminfo_val; > > /* > @@ -94,6 +98,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 > @@ -121,6 +126,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 c94d4240e3d3..9d57e7999ae0 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 0f02835765ff..55a83b0b5981 100644 > --- a/kernel/ve/ve.c > +++ b/kernel/ve/ve.c > @@ -99,10 +99,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 = > @@ -114,8 +117,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), > @@ -780,12 +791,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); > + 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); > @@ -812,7 +826,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 > @@ -825,6 +841,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; > @@ -1065,6 +1082,50 @@ 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) } > + VE_FC_ENTRY(netns), > + VE_FC_ENTRY(mnt), > + VE_FC_ENTRY(netif), > + VE_FC_ENTRY(bpf_prog), > + VE_FC_ENTRY(neigh_tbl), > +#ifdef CONFIG_AIO > + VE_FC_ENTRY(aio), > +#endif > + {} > +}; > + > +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; Should we allow the container itself to reset the failcount? All other resource write handlers have a check that return EPERM when we are not super ve. Here we will allow the container to reset its failcount so it will no longer be trustworthy information. -- Vasileios Almpanis