All Virtuozzo development lists (kernel + QEMU)
 help / color / mirror / Atom feed
From: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
To: Konstantin Khorenko <khorenko@virtuozzo.com>, devel@openvz.org
Subject: Re: [Devel] [PATCH v2 VZ10 1/2] ve/bpf: Limit number of BPF programs loadable per-VE
Date: Fri, 28 Aug 2026 17:47:23 +0200	[thread overview]
Message-ID: <ccc3cbc8-e225-41cf-b563-699254aeb57b@virtuozzo.com> (raw)
In-Reply-To: <9f43be79-913a-4442-aa36-aa6059b02e4b@virtuozzo.com>

I thought we've decided in slack that having failcounts is enough,
so after "[PATCH VZ10 v7 7/9] ve: Introduce per-VE failcount" ads
bpf_prog_failcount, warning is not really needed to be aware about
lack of bpf programs.

On 8/26/26 18:48, Konstantin Khorenko wrote:
> a kind ping
> 
> --
> Best regards,
> 
> Konstantin Khorenko,
> Virtuozzo Linux Kernel Team
> 
> On 6/18/26 18:41, Konstantin Khorenko wrote:
>> i will apply the patch, but i would appreciate in case you add some WARN_ONCE
>> in case some CT hits the limit of available bpf programs.
>>
>> This will ease issue investigations + at some point might hints us that we need to increase the default, for example.
>>
>> --
>> Best regards,
>>
>> Konstantin Khorenko,
>> Virtuozzo Linux Kernel Team
>>
>> On 5/29/26 16:42, Pavel Tikhomirov wrote:
>>> Without a per-VE cap a single container could exhaust the system-wide
>>> bpf JIT memory budget by loading excessive numbers of CGROUP_DEVICE
>>> programs via the VE_FEATURE_BPF path.
>>>
>>> Add bpf_prog_avail_nr / bpf_prog_max_nr counters to ve_struct and
>>> enforce them in bpf_prog_load() for non-bpf-capable callers loading
>>> CGROUP_DEVICE programs.
>>>
>>> Lifetime note: A BPF program loadded in VE takes a reference to ve, when
>>> container is stopped, all open fds to the BPF programm will be closed
>>> and when container manager removes container cgroups the BPF program
>>> will be released and thus releasing the reference to VE.
>>>
>>> Default max number note: It is somehow similar to ve.netif_max_nr, there
>>> each docker container creates two veths, and I also observe that docker
>>> container loads two bpf programs (one by dockerd, one by systemd). So
>>> let's use the same number.
>>>
>>> https://virtuozzo.atlassian.net/browse/VSTOR-131947
>>> Signed-off-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
>>> Feature: ve: allow BPF in Containers
>>>
>>> --
>>> v2: Also put load_ve definition under CONFIG_VE.
>>> ---
>>>  include/linux/bpf.h  |  8 ++++++++
>>>  include/linux/ve.h   |  4 ++++
>>>  kernel/bpf/core.c    |  8 ++++++++
>>>  kernel/bpf/syscall.c | 37 +++++++++++++++++++++++++++++++++++++
>>>  kernel/ve/ve.c       |  5 +++++
>>>  5 files changed, 62 insertions(+)
>>>
>>> diff --git a/include/linux/bpf.h b/include/linux/bpf.h
>>> index 80175c7a21c27..0212806d5efc2 100644
>>> --- a/include/linux/bpf.h
>>> +++ b/include/linux/bpf.h
>>> @@ -56,6 +56,7 @@ struct cgroup;
>>>  struct bpf_token;
>>>  struct user_namespace;
>>>  struct super_block;
>>> +struct ve_struct;
>>>  struct inode;
>>>  
>>>  extern struct idr btf_idr;
>>> @@ -1522,6 +1523,13 @@ struct bpf_prog_aux {
>>>  	void *security;
>>>  #endif
>>>  	struct bpf_token *token;
>>> +#ifdef CONFIG_VE
>>> +	/* VE that loaded the program via VE_FEATURE_BPF path and against whose
>>> +	 * bpf_prog_avail_nr counter the program is accounted. NULL for programs
>>> +	 * loaded through the regular (non VE-restricted) path.
>>> +	 */
>>> +	struct ve_struct *owner_ve;
>>> +#endif
>>>  	struct bpf_prog_offload *offload;
>>>  	struct btf *btf;
>>>  	struct bpf_func_info *func_info;
>>> diff --git a/include/linux/ve.h b/include/linux/ve.h
>>> index 224acf012821f..88b4d531c466e 100644
>>> --- a/include/linux/ve.h
>>> +++ b/include/linux/ve.h
>>> @@ -76,6 +76,9 @@ struct ve_struct {
>>>  	atomic_t		netif_avail_nr;
>>>  	int			netif_max_nr;
>>>  
>>> +	atomic_t		bpf_prog_avail_nr;
>>> +	int			bpf_prog_max_nr;
>>> +
>>>  	atomic64_t		_uevent_seqnum;
>>>  
>>>  	int			_randomize_va_space;
>>> @@ -149,6 +152,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;
>>>  
>>> diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
>>> index 4de8774458aca..7aaf73180fcdc 100644
>>> --- a/kernel/bpf/core.c
>>> +++ b/kernel/bpf/core.c
>>> @@ -38,6 +38,7 @@
>>>  #include <linux/bpf_mem_alloc.h>
>>>  #include <linux/memcontrol.h>
>>>  #include <linux/execmem.h>
>>> +#include <linux/ve.h>
>>>  
>>>  #include <asm/barrier.h>
>>>  #include <linux/unaligned.h>
>>> @@ -2828,6 +2829,13 @@ void bpf_prog_free(struct bpf_prog *fp)
>>>  	if (aux->dst_prog)
>>>  		bpf_prog_put(aux->dst_prog);
>>>  	bpf_token_put(aux->token);
>>> +#ifdef CONFIG_VE
>>> +	if (aux->owner_ve) {
>>> +		atomic_inc(&aux->owner_ve->bpf_prog_avail_nr);
>>> +		put_ve(aux->owner_ve);
>>> +		aux->owner_ve = NULL;
>>> +	}
>>> +#endif
>>>  	INIT_WORK(&aux->work, bpf_prog_free_deferred);
>>>  	schedule_work(&aux->work);
>>>  }
>>> diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
>>> index 0475a72c93c06..481ae62429097 100644
>>> --- a/kernel/bpf/syscall.c
>>> +++ b/kernel/bpf/syscall.c
>>> @@ -2663,6 +2663,9 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
>>>  	struct bpf_prog *prog, *dst_prog = NULL;
>>>  	struct btf *attach_btf = NULL;
>>>  	struct bpf_token *token = NULL;
>>> +#ifdef CONFIG_VE
>>> +	struct ve_struct *load_ve = NULL;
>>> +#endif
>>>  	bool bpf_cap;
>>>  	int err;
>>>  	char license[128];
>>> @@ -2744,6 +2747,22 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
>>>  	if (is_perfmon_prog_type(type) && !bpf_token_capable(token, CAP_PERFMON))
>>>  		goto put_token;
>>>  
>>> +#ifdef CONFIG_VE
>>> +	/* Restrict the number of BPF programs that can be loaded via the
>>> +	 * VE-allowed path. Without this, a single container could exhaust
>>> +	 * the system-wide bpf JIT memory budget by loading excessive
>>> +	 * numbers of CGROUP_DEVICE programs.
>>> +	 */
>>> +	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) {
>>> +			load_ve = NULL;
>>> +			err = -ENOSPC;
>>> +			goto put_token;
>>> +		}
>>> +	}
>>> +#endif
>>> +
>>>  	/* attach_prog_fd/attach_btf_obj_fd can specify fd of either bpf_prog
>>>  	 * or btf, we need to check which one it is
>>>  	 */
>>> @@ -2809,6 +2828,16 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
>>>  	prog->aux->dev_bound = !!attr->prog_ifindex;
>>>  	prog->aux->xdp_has_frags = attr->prog_flags & BPF_F_XDP_HAS_FRAGS;
>>>  
>>> +#ifdef CONFIG_VE
>>> +	/* Hand the avail_nr slot reservation over to the prog. bpf_prog_free()
>>> +	 * will release it via put_ve + counter increment.
>>> +	 */
>>> +	if (load_ve) {
>>> +		prog->aux->owner_ve = get_ve(load_ve);
>>> +		load_ve = NULL;
>>> +	}
>>> +#endif
>>> +
>>>  	/* move token into prog->aux, reuse taken refcnt */
>>>  	prog->aux->token = token;
>>>  	token = NULL;
>>> @@ -2932,6 +2961,14 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
>>>  		btf_put(prog->aux->attach_btf);
>>>  	bpf_prog_free(prog);
>>>  put_token:
>>> +#ifdef CONFIG_VE
>>> +	/* The load_ve is non-NULL only if we decremented bpf_prog_avail_nr
>>> +	 * but did not hand the reservation off to the prog yet (i.e. failure
>>> +	 * happened before bpf_prog_alloc()). Roll back the counter.
>>> +	 */
>>> +	if (load_ve)
>>> +		atomic_inc(&load_ve->bpf_prog_avail_nr);
>>> +#endif
>>>  	bpf_token_put(token);
>>>  	return err;
>>>  }
>>> diff --git a/kernel/ve/ve.c b/kernel/ve/ve.c
>>> index 198c82f010cc1..48da546117bb7 100644
>>> --- a/kernel/ve/ve.c
>>> +++ b/kernel/ve/ve.c
>>> @@ -76,6 +76,8 @@ struct ve_struct ve0 = {
>>>  	.netns_max_nr		= INT_MAX,
>>>  	.netif_avail_nr		= ATOMIC_INIT(INT_MAX),
>>>  	.netif_max_nr		= INT_MAX,
>>> +	.bpf_prog_avail_nr	= ATOMIC_INIT(INT_MAX),
>>> +	.bpf_prog_max_nr	= INT_MAX,
>>>  	.fsync_enable		= FSYNC_FILTERED,
>>>  	._randomize_va_space	=
>>>  #ifdef CONFIG_COMPAT_BRK
>>> @@ -983,6 +985,9 @@ static struct cgroup_subsys_state *ve_create(struct cgroup_subsys_state *parent_
>>>  	atomic_set(&ve->netif_avail_nr, NETIF_MAX_NR_DEFAULT);
>>>  	ve->netif_max_nr = NETIF_MAX_NR_DEFAULT;
>>>  
>>> +	atomic_set(&ve->bpf_prog_avail_nr, BPF_PROG_MAX_NR_DEFAULT);
>>> +	ve->bpf_prog_max_nr = BPF_PROG_MAX_NR_DEFAULT;
>>> +
>>>  	err = ve_log_init(ve);
>>>  	if (err)
>>>  		goto err_log;
>>
> 

-- 
Best regards, Pavel Tikhomirov
Senior Software Developer, Virtuozzo.

_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel

      reply	other threads:[~2026-08-28 15:48 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <20260529144215.939517-1-ptikhomirov@virtuozzo.com>
     [not found] ` <20260529144215.939517-2-ptikhomirov@virtuozzo.com>
     [not found]   ` <3ca2d5c2-3d7e-4694-ab65-d8b3fa8b6844@virtuozzo.com>
2026-08-26 16:48     ` Konstantin Khorenko
2026-08-28 15:47       ` Pavel Tikhomirov [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=ccc3cbc8-e225-41cf-b563-699254aeb57b@virtuozzo.com \
    --to=ptikhomirov@virtuozzo.com \
    --cc=devel@openvz.org \
    --cc=khorenko@virtuozzo.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.