bpf: Add support for local percpu kptr
Subsystems (5)
Yonghong Song
reviewerv2 cover bpf: Add support for local percpu kptr
Patch set [1] implemented cgroup local storage BPF_MAP_TYPE_CGRP_STORAGE similar to sk/task/inode local storage and old BPF_MAP_TYPE_CGROUP_STORAGE map is marked as deprecated since old BPF_MAP_TYPE_CGROUP_STORAGE map can only work with current cgroup.
Similarly, the existing BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE map is a percpu version of BPF_MAP_TYPE_CGROUP_STORAGE and only works with current cgroup. But there is no replacement which can work with arbitrary cgroup.
This patch set solved this problem but adding support for local percpu kptr. The map value can have a percpu kptr field which holds a bpf prog allocated percpu data. The below is an example,
struct percpu_val_t {
... fields ...
}
struct map_value_t {
struct percpu_val_t __percpu *percpu_data_ptr;
}In the above, 'map_value_t' is the map value type for a BPF_MAP_TYPE_CGRP_STORAGE map. User can access 'percpu_data_ptr' and then read/write percpu data. This covers BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE and more. So BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE map type
is marked as deprecated.
In additional, local percpu kptr supports the same map type as other kptrs including hash, lru_hash, array, sk/inode/task/cgrp local storage. Currently, percpu data structure does not support non-scalars or special fields (e.g., bpf_spin_lock, bpf_rb_root, etc.). They can be supported in the future if there exist use cases.
Please for individual patches for details.
[1] https://lore.kernel.org/all/[email protected]/
Changelog:
v1 -> v2:
- does not support special fields in percpu data structure.
- rename __percpu attr to __percpu_kptr attr.
- rename BPF_KPTR_PERCPU_REF to BPF_KPTR_PERCPU.
- better code to handle bpf_{this,per}_cpu_ptr() helpers.
- add more negative tests.
- fix a bpftool related test failure.
Yonghong Song (13):
bpf: Add support for non-fix-size percpu mem allocation
bpf: Add BPF_KPTR_PERCPU as a field type
bpf: Add alloc/xchg/direct_access support for local percpu kptr
bpf: Add bpf_this_cpu_ptr/bpf_per_cpu_ptr support for allocated percpu
obj
selftests/bpf: Update error message in negative linked_list test
libbpf: Add __percpu_kptr macro definition
selftests/bpf: Add bpf_percpu_obj_{new,drop}() macro in
bpf_experimental.h
selftests/bpf: Add tests for array map with local percpu kptr
bpf: Mark OBJ_RELEASE argument as MEM_RCU when possible selftests/bpf: Remove unnecessary direct read of local percpu kptr selftests/bpf: Add tests for cgrp_local_storage with local percpu kptr selftests/bpf: Add some negative tests
bpf: Mark BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE deprecated
include/linux/bpf.h | 22 +- include/linux/bpf_verifier.h | 1 + include/uapi/linux/bpf.h | 9 +- kernel/bpf/btf.c | 5 + kernel/bpf/core.c | 8 +- kernel/bpf/helpers.c | 16 ++ kernel/bpf/memalloc.c | 14 +- kernel/bpf/syscall.c | 4 + kernel/bpf/verifier.c | 191 +++++++++++++++--- tools/include/uapi/linux/bpf.h | 9 +- tools/lib/bpf/bpf_helpers.h | 1 + .../testing/selftests/bpf/bpf_experimental.h | 31 +++ .../selftests/bpf/prog_tests/linked_list.c | 4 +- .../selftests/bpf/prog_tests/percpu_alloc.c | 125 ++++++++++++ .../selftests/bpf/progs/percpu_alloc_array.c | 183 +++++++++++++++++ .../progs/percpu_alloc_cgrp_local_storage.c | 105 ++++++++++ .../selftests/bpf/progs/percpu_alloc_fail.c | 164 +++++++++++++++ .../selftests/bpf/test_bpftool_synctypes.py | 9 + 18 files changed, 848 insertions(+), 53 deletions(-) create mode 100644 tools/testing/selftests/bpf/prog_tests/percpu_alloc.c create mode 100644 tools/testing/selftests/bpf/progs/percpu_alloc_array.c create mode 100644 tools/testing/selftests/bpf/progs/percpu_alloc_cgrp_local_storage.c create mode 100644 tools/testing/selftests/bpf/progs/percpu_alloc_fail.c
Signature · 1 line
2.34.1
Yonghong Song
reviewerPATCH v2 1/13 bpf: Add support for non-fix-size percpu mem allocation
This is needed for later percpu mem allocation when the allocation is done by bpf program. For such cases, a global bpf_global_percpu_ma is added where a flexible allocation size is needed.
--- include/linux/bpf.h | 4 ++-- kernel/bpf/core.c | 8 +++++--- kernel/bpf/memalloc.c | 14 ++++++-------- 3 files changed, 13 insertions(+), 13 deletions(-)
include/linux/bpf.h +2 -2
@@ -55,8 +55,8 @@ struct cgroup;extern struct idr btf_idr;extern spinlock_t btf_idr_lock;extern struct kobject *btf_kobj; -extern struct bpf_mem_alloc bpf_global_ma; -extern bool bpf_global_ma_set; +extern struct bpf_mem_alloc bpf_global_ma, bpf_global_percpu_ma; +extern bool bpf_global_ma_set, bpf_global_percpu_ma_set;typedef u64 (*bpf_callback_t )(u64 ,u64 ,u64 ,u64 ,u64 ); typedef int (*bpf_iter_init_seq_priv_t)(void *private_data,
kernel/bpf/core.c +5 -3
@@ -64,8 +64,8 @@#define OFF insn->off#define IMM insn->imm -struct bpf_mem_alloc bpf_global_ma; -bool bpf_global_ma_set; +struct bpf_mem_alloc bpf_global_ma, bpf_global_percpu_ma; +bool bpf_global_ma_set, bpf_global_percpu_ma_set;/* No hurry in this branch *
@@ -2921,7 +2921,9 @@ static int __init bpf_global_ma_init(void) ret =bpf_mem_alloc_init (&bpf_global_ma,0 , false); bpf_global_ma_set = !ret; - return ret; + ret =bpf_mem_alloc_init (&bpf_global_percpu_ma,0 , true); + bpf_global_percpu_ma_set = !ret; + return !bpf_global_ma_set || !bpf_global_percpu_ma_set; }late_initcall (bpf_global_ma_init);#endif
kernel/bpf/memalloc.c +6 -8
@@ -499,15 +499,16 @@ int bpf_mem_alloc_init(struct bpf_mem_alloc *ma, int size, bool percpu)struct obj_cgroup *objcg =NULL ;int cpu, i, unit_size, percpu_size =0 ; +/* room for llist_node and per-cpu pointer */ +if (percpu) + percpu_size =LLIST_NODE_SZ +sizeof (void *); +if (size) { pc =__alloc_percpu_gfp (sizeof (*pc),8 ,GFP_KERNEL );if (!pc)return -ENOMEM ; -if (percpu) -/* room for llist_node and per-cpu pointer */ - percpu_size =LLIST_NODE_SZ +sizeof (void *); -else +if (!percpu) size +=LLIST_NODE_SZ ;/* room for llist_node */ unit_size = size;
@@ -527,10 +528,6 @@ int bpf_mem_alloc_init(struct bpf_mem_alloc *ma, int size, bool percpu)return 0 ; } -/* size == 0 && percpu is an invalid combination */ -if (WARN_ON_ONCE (percpu)) -return -EINVAL ; - pcc =__alloc_percpu_gfp (sizeof (*cc),8 ,GFP_KERNEL );if (!pcc)return -ENOMEM ;
@@ -543,6 +540,7 @@ int bpf_mem_alloc_init(struct bpf_mem_alloc *ma, int size, bool percpu) c = &cc->cache[i]; c->unit_size = sizes[i]; c->objcg = objcg; + c->percpu_size = percpu_size; c->tgt = c;prefill_mem_cache (c, cpu); }
Signature · 1 line
2.34.1
Yonghong Song
reviewerPATCH v2 2/13 bpf: Add BPF_KPTR_PERCPU as a field type
In reply to Yonghong SongBPF_KPTR_PERCPU represents a percpu field type like below
struct val_t {
... fields ...
};
struct t {
...
struct val_t __percpu_kptr *percpu_data_ptr;
...
};where
#define __percpu_kptr __attribute__((btf_type_tag("percpu_kptr")))
While BPF_KPTR_REF points to a trusted kernel object or a trusted local object, BPF_KPTR_PERCPU points to a trusted local percpu object.
This patch added basic support for BPF_KPTR_PERCPU related to percpu_kptr field parsing, recording and free operations. BPF_KPTR_PERCPU also supports the same map types
as BPF_KPTR_REF does.
Note that unlike a local kptr, it is possible that
a BPF_KTPR_PERCPU struct may not contain any
special fields like other kptr, bpf_spin_lock, bpf_list_head, etc.
--- include/linux/bpf.h | 18 ++++++++++++------ kernel/bpf/btf.c | 5 +++++ kernel/bpf/syscall.c | 4 ++++ 3 files changed, 21 insertions(+), 6 deletions(-)
include/linux/bpf.h +12 -6
@@ -180,14 +180,15 @@ enum btf_field_type {BPF_TIMER = (1 <<1 ),BPF_KPTR_UNREF = (1 <<2 ),BPF_KPTR_REF = (1 <<3 ), -BPF_KPTR =BPF_KPTR_UNREF |BPF_KPTR_REF , -BPF_LIST_HEAD = (1 <<4 ), -BPF_LIST_NODE = (1 <<5 ), -BPF_RB_ROOT = (1 <<6 ), -BPF_RB_NODE = (1 <<7 ), +BPF_KPTR_PERCPU = (1 <<4 ), +BPF_KPTR =BPF_KPTR_UNREF |BPF_KPTR_REF |BPF_KPTR_PERCPU , +BPF_LIST_HEAD = (1 <<5 ), +BPF_LIST_NODE = (1 <<6 ), +BPF_RB_ROOT = (1 <<7 ), +BPF_RB_NODE = (1 <<8 ),BPF_GRAPH_NODE_OR_ROOT =BPF_LIST_NODE |BPF_LIST_HEAD |BPF_RB_NODE |BPF_RB_ROOT , - BPF_REFCOUNT = (1 << 8), + BPF_REFCOUNT = (1 << 9), };typedef void (*btf_dtor_kfunc_t )(void *);
@@ -300,6 +301,8 @@ static inline const char *btf_field_type_name(enum btf_field_type type)case BPF_KPTR_UNREF :case BPF_KPTR_REF :return "kptr" ; +case BPF_KPTR_PERCPU : +return "percpu_kptr" ;case BPF_LIST_HEAD :return "bpf_list_head" ;case BPF_LIST_NODE :
@@ -325,6 +328,7 @@ static inline u32 btf_field_type_size(enum btf_field_type type)return sizeof (struct bpf_timer );case BPF_KPTR_UNREF :case BPF_KPTR_REF : +case BPF_KPTR_PERCPU :return sizeof (u64);case BPF_LIST_HEAD :return sizeof (struct bpf_list_head );
@@ -351,6 +355,7 @@ static inline u32 btf_field_type_align(enum btf_field_type type)return __alignof__(struct bpf_timer );case BPF_KPTR_UNREF :case BPF_KPTR_REF : +case BPF_KPTR_PERCPU :return __alignof__(u64 );case BPF_LIST_HEAD :return __alignof__(struct bpf_list_head );
@@ -389,6 +394,7 @@ static inline void bpf_obj_init_field(const struct btf_field *field, void *addr)case BPF_TIMER :case BPF_KPTR_UNREF :case BPF_KPTR_REF : +case BPF_KPTR_PERCPU :break ;default :WARN_ON_ONCE (1 );
kernel/bpf/btf.c +5 -0
@@ -3293,6 +3293,8 @@ static int btf_find_kptr(const struct btf *btf, const struct btf_type *t, type =BPF_KPTR_UNREF ; else if (!strcmp("kptr", __btf_name_by_offset(btf, t->name_off))) type =BPF_KPTR_REF ; + else if (!strcmp("percpu_kptr", __btf_name_by_offset(btf, t->name_off))) + type =BPF_KPTR_PERCPU ; else return -EINVAL;
@@ -3457,6 +3459,7 @@ static int btf_find_struct_field(const struct btf *btf,break ;case BPF_KPTR_UNREF :case BPF_KPTR_REF : +case BPF_KPTR_PERCPU : ret =btf_find_kptr (btf, member_type, off, sz, idx < info_cnt ? &info[idx] : &tmp);if (ret <0 )
@@ -3523,6 +3526,7 @@ static int btf_find_datasec_var(const struct btf *btf, const struct btf_type *t,break ;case BPF_KPTR_UNREF :case BPF_KPTR_REF : +case BPF_KPTR_PERCPU : ret =btf_find_kptr (btf, var_type, off, sz, idx < info_cnt ? &info[idx] : &tmp);if (ret <0 )
@@ -3783,6 +3787,7 @@ struct btf_record *btf_parse_fields(const struct btf *btf, const struct btf_typebreak ;case BPF_KPTR_UNREF :case BPF_KPTR_REF : +case BPF_KPTR_PERCPU : ret =btf_parse_kptr (btf, &rec->fields[i], &info_arr[i]);if (ret <0 ) goto end;
kernel/bpf/syscall.c +4 -0
@@ -514,6 +514,7 @@ void btf_record_free(struct btf_record *rec)switch (rec->fields[i].type) {case BPF_KPTR_UNREF :case BPF_KPTR_REF : +case BPF_KPTR_PERCPU :if (rec->fields[i].kptr.module)module_put (rec->fields[i].kptr.module);btf_put (rec->fields[i].kptr.btf);
@@ -560,6 +561,7 @@ struct btf_record *btf_record_dup(const struct btf_record *rec)switch (fields[i].type) {case BPF_KPTR_UNREF :case BPF_KPTR_REF : +case BPF_KPTR_PERCPU :btf_get (fields[i].kptr.btf);if (fields[i].kptr.module && !try_module_get (fields[i].kptr.module)) { ret = -ENXIO ;
@@ -650,6 +652,7 @@ void bpf_obj_free_fields(const struct btf_record *rec, void *obj)WRITE_ONCE (*(u64 *)field_ptr,0 );break ;case BPF_KPTR_REF : +case BPF_KPTR_PERCPU : xchgd_field = (void *)xchg ((unsigned long *)field_ptr,0 );if (!xchgd_field)break ;
@@ -1045,6 +1048,7 @@ static int map_check_btf(struct bpf_map *map, const struct btf *btf,break ;case BPF_KPTR_UNREF :case BPF_KPTR_REF : +case BPF_KPTR_PERCPU :case BPF_REFCOUNT : if (map->map_type != BPF_MAP_TYPE_HASH && map->map_type != BPF_MAP_TYPE_PERCPU_HASH &&
Signature · 1 line
2.34.1
Yonghong Song
reviewerPATCH v2 3/13 bpf: Add alloc/xchg/direct_access support for local percpu kptr
In reply to Yonghong SongAdd two new kfunc's, bpf_percpu_obj_new_impl() and bpf_percpu_obj_drop_impl(), to allocate a percpu obj. Two functions are very similar to bpf_obj_new_impl() and bpf_obj_drop_impl(). The major difference is related to percpu handling.
bpf_rcu_read_lock()
struct val_t __percpu_kptr *v = map_val->percpu_data;
...
bpf_rcu_read_unlock()For a percpu data map_val like above 'v', the reg->type is set as
PTR_TO_BTF_ID | MEM_PERCPU | MEM_RCU
if inside rcu critical section.
MEM_RCU marking here is similar to NON_OWN_REF as 'v' is not a owning reference. But NON_OWN_REF is trusted and typically inside the spinlock while MEM_RCU is under rcu read lock. RCU is preferred here since percpu data structures mean potential concurrent access into its contents.
Also, bpf_percpu_obj_new_impl() is restricted such that no pointers or special fields are allowed. Therefore, the bpf_list_head and bpf_rb_root will not be supported in this patch set to avoid potential memory leak issue due to racing between bpf_obj_free_fields() and another bpf_kptr_xchg() moving an allocated object to bpf_list_head and bpf_rb_root.
--- kernel/bpf/helpers.c | 16 ++++++ kernel/bpf/verifier.c | 112 +++++++++++++++++++++++++++++++++--------- 2 files changed, 106 insertions(+), 22 deletions(-)
kernel/bpf/helpers.c +16 -0
@@ -1902,6 +1902,14 @@ __bpf_kfunc void *bpf_obj_new_impl(u64 local_type_id__k, void *meta__ign)return p; } +__bpf_kfunc void *bpf_percpu_obj_new_impl(u64 local_type_id__k, void *meta__ign) +{ +u64 size = local_type_id__k; + +/* The verifier has ensured that meta__ign must be NULL */ +return bpf_mem_alloc (&bpf_global_percpu_ma, size); +} +/* Must be called under migrate_disable(), as required by bpf_mem_free */ void __bpf_obj_drop_impl (void *p,const struct btf_record *rec) {
@@ -1930,6 +1938,12 @@ __bpf_kfunc void bpf_obj_drop_impl(void *p__alloc, void *meta__ign)__bpf_obj_drop_impl (p, meta ? meta->record :NULL ); } +__bpf_kfunc void bpf_percpu_obj_drop_impl(void *p__alloc, void *meta__ign) +{ +/* The verifier has ensured that meta__ign must be NULL */ +bpf_mem_free_rcu (&bpf_global_percpu_ma, p__alloc); +} + __bpf_kfunc void *bpf_refcount_acquire_impl(void *p__refcounted_kptr, void *meta__ign) {struct btf_struct_meta *meta = meta__ign;
@@ -2442,7 +2456,9 @@ BTF_SET8_START(generic_btf_ids) BTF_ID_FLAGS(func, crash_kexec, KF_DESTRUCTIVE)#endif BTF_ID_FLAGS(func, bpf_obj_new_impl, KF_ACQUIRE | KF_RET_NULL) +BTF_ID_FLAGS(func, bpf_percpu_obj_new_impl, KF_ACQUIRE | KF_RET_NULL) BTF_ID_FLAGS(func, bpf_obj_drop_impl, KF_RELEASE) +BTF_ID_FLAGS(func, bpf_percpu_obj_drop_impl, KF_RELEASE) BTF_ID_FLAGS(func, bpf_refcount_acquire_impl, KF_ACQUIRE | KF_RET_NULL) BTF_ID_FLAGS(func, bpf_list_push_front_impl)BTF_ID_FLAGS (func, bpf_list_push_back_impl)
kernel/bpf/verifier.c +90 -22
271 lines not on this page · raw message
Signature · 1 line
2.34.1
Yonghong Song
reviewerPATCH v2 4/13 bpf: Add bpf_this_cpu_ptr/bpf_per_cpu_ptr support for allocated percpu obj
In reply to Yonghong SongThe bpf helpers bpf_this_cpu_ptr() and bpf_per_cpu_ptr() are re-purposed for allocated percpu objects. For an allocated percpu obj, the reg type is 'PTR_TO_BTF_ID | MEM_PERCPU | MEM_RCU'.
The return type for these two re-purposed helpera is 'PTR_TO_MEM | MEM_RCU | MEM_ALLOC'.
The MEM_ALLOC allows that the per-cpu data can be read and written.
Since the memory allocator bpf_mem_alloc() returns a ptr to a percpu ptr for percpu data, the first argument of bpf_this_cpu_ptr() and bpf_per_cpu_ptr() is patched with a dereference before passing to the helper func.
--- include/linux/bpf_verifier.h | 1 + kernel/bpf/verifier.c | 59 +++++++++++++++++++++++++++++++----- 2 files changed, 52 insertions(+), 8 deletions(-)
include/linux/bpf_verifier.h +1 -0
@@ -480,6 +480,7 @@ struct bpf_insn_aux_data {bool zext_dst;/* this insn zero extends dst reg */ bool storage_get_func_atomic;/* bpf_*_storage_get() with atomic memory alloc */ bool is_iter_next;/* bpf_iter_<type>_next() kfunc call */ +bool call_with_percpu_alloc_ptr;/* {this,per}_cpu_ptr() with prog percpu alloc */ u8 alu_state;/* used in combination with alu_limit */ /* below fields are initialized once */
kernel/bpf/verifier.c +51 -8
102 lines not on this page · raw message
Signature · 1 line
2.34.1
Yonghong Song
reviewerPATCH v2 5/13 selftests/bpf: Update error message in negative linked_list test
In reply to Yonghong SongSome error messages are changed due to the addition of percpu kptr support. Fix linked_list test with changed error messages.
--- tools/testing/selftests/bpf/prog_tests/linked_list.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
tools/testing/selftests/bpf/prog_tests/linked_list.c +2 -2
@@ -65,8 +65,8 @@ static struct { { "map_compat_raw_tp", "tracing progs cannot use bpf_{list_head,rb_root} yet" }, { "map_compat_raw_tp_w", "tracing progs cannot use bpf_{list_head,rb_root} yet" }, { "obj_type_id_oor", "local type ID argument must be in range [0, U32_MAX]" }, - { "obj_new_no_composite", "bpf_obj_new type ID argument must be of a struct" }, - { "obj_new_no_struct", "bpf_obj_new type ID argument must be of a struct" }, + { "obj_new_no_composite", "bpf_obj_new/bpf_percpu_obj_new type ID argument must be of a struct" }, + { "obj_new_no_struct", "bpf_obj_new/bpf_percpu_obj_new type ID argument must be of a struct" }, { "obj_drop_non_zero_off", "R1 must have zero offset when passed to release func" }, { "new_null_ret", "R0 invalid mem access 'ptr_or_null_'" }, { "obj_new_acq", "Unreleased reference id=" },
Signature · 1 line
2.34.1
Yonghong Song
reviewerPATCH v2 6/13 libbpf: Add __percpu_kptr macro definition
In reply to Yonghong SongAdd __percpu_kptr macro definition in bpf_helpers.h.
--- tools/lib/bpf/bpf_helpers.h | 1 + 1 file changed, 1 insertion(+)
tools/lib/bpf/bpf_helpers.h +1 -0
@@ -181,6 +181,7 @@ enum libbpf_tristate {#define __ksym __attribute__((section(".ksyms")))#define __kptr_untrusted __attribute__((btf_type_tag("kptr_untrusted")))#define __kptr __attribute__((btf_type_tag("kptr"))) +#define __percpu_kptr __attribute__((btf_type_tag("percpu_kptr")))#define bpf_ksym_exists (sym) ({ \ _Static_assert(!__builtin_constant_p(!!sym), #sym " should be marked as __weak"); \
Signature · 1 line
2.34.1
Re: [PATCH bpf-next v2 06/13] libbpf: Add __percpu_kptr macro definition
Quoted 13 lines · Yonghong Song
Add
__percpu_kptrmacro definition inbpf_helpers.h.Signed-off-by Yonghong Song <[email protected]>--- tools/lib/bpf/bpf_helpers.h | 1 + 1 file changed, 1 insertion(+)tools/lib/bpf/bpf_helpers.h +0
-0
tools/lib/bpf/bpf_helpers.h enum libbpf_tristate { · line 184
Quoted 4 lines
tools/lib/bpf/bpf_helpers.h +1
-0@@ -181,6 +181,7 @@ enum libbpf_tristate {#define __ksym __attribute__((section(".ksyms")))#define __kptr_untrusted __attribute__((btf_type_tag("kptr_untrusted")))#define __kptr __attribute__((btf_type_tag("kptr"))) +#define __percpu_kptr __attribute__((btf_type_tag("percpu_kptr")))
total nitpick, but given kptr_untrusted, should this stick to the pattern and be __kptr_percpu? It keeps this "kptr" umbrella/namespace consistent
Quoted 6 lines
tools/lib/bpf/bpf_helpers.h +0
-1#define bpf_ksym_exists (sym) ({ \ _Static_assert(!__builtin_constant_p(!!sym), #sym " should be marked as __weak"); \ --2.34.1
Yonghong Song
reviewerRe: [PATCH bpf-next v2 06/13] libbpf: Add __percpu_kptr macro definition
Quoted 9 lines · Andrii Nakryiko
Quoted 8 lines · Yonghong Song
Add
__percpu_kptrmacro definition inbpf_helpers.h.Signed-off-by Yonghong Song <[email protected]>--- tools/lib/bpf/bpf_helpers.h | 1 + 1 file changed, 1 insertion(+)
Quoted 13 lines
tools/lib/bpf/bpf_helpers.h enum libbpf_tristate { · line 184
Quoted 9 lines
tools/lib/bpf/bpf_helpers.h +1
-0@@ -181,6 +181,7 @@ enum libbpf_tristate {#define __ksym __attribute__((section(".ksyms")))#define __kptr_untrusted __attribute__((btf_type_tag("kptr_untrusted")))#define __kptr __attribute__((btf_type_tag("kptr"))) +#define __percpu_kptr __attribute__((btf_type_tag("percpu_kptr")))total nitpick, but given
kptr_untrusted, should this stick to the pattern and be__kptr_percpu? It keeps this "kptr" umbrella/namespace consistent
Alexei mentioned that __kptr_untrusted might be deprecated in the future.
I am using __percpu_kptr just feel it is more nature to user e.g., we use 'percpu ptr' for kernel percpu ptr. But I can change the name if there is a consensus among community.
Quoted 7 lines
Quoted 7 lines
tools/lib/bpf/bpf_helpers.h +0
-1#define bpf_ksym_exists (sym) ({ \ _Static_assert(!__builtin_constant_p(!!sym), #sym " should be marked as __weak"); \ --2.34.1
Yonghong Song
reviewerPATCH v2 7/13 selftests/bpf: Add bpf_percpu_obj_{new,drop}() macro in bpf_experimental.h
In reply to Yonghong SongThe new macro bpf_percpu_obj_{new/drop}() is very similar to bpf_obj_{new,drop}() as they both take a type as the argument.
--- .../testing/selftests/bpf/bpf_experimental.h | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+)
tools/testing/selftests/bpf/bpf_experimental.h +31 -0
@@ -131,4 +131,35 @@ extern int bpf_rbtree_add_impl(struct bpf_rb_root *root, struct bpf_rb_node *nod*/ extern struct bpf_rb_node *bpf_rbtree_first (struct bpf_rb_root *root) __ksym; +/* Description +* Allocates a percpu object of the type represented by 'local_type_id' in +* program BTF. User may use the bpf_core_type_id_local macro to pass the +* type ID of a struct in program BTF. +* +* The 'local_type_id' parameter must be a known constant. +* The 'meta' parameter is rewritten by the verifier, no need for BPF +* program to set it. +* Returns +* A pointer to a percpu object of the type corresponding to the passed in +* 'local_type_id', or NULL on failure. +*/ +extern void *bpf_percpu_obj_new_impl (__u64 local_type_id,void *meta) __ksym; + +/* Convenience macro to wrap over bpf_percpu_obj_new_impl */ +#define bpf_percpu_obj_new (type) ((type __percpu_kptr *)bpf_percpu_obj_new_impl(bpf_core_type_id_local(type), NULL)) + +/* Description +* Free an allocated percpu object. All fields of the object that require +* destruction will be destructed before the storage is freed. +* +* The 'meta' parameter is rewritten by the verifier, no need for BPF +* program to set it. +* Returns +* Void. +*/ +extern void bpf_percpu_obj_drop_impl(void *kptr, void *meta) __ksym; + +/* Convenience macro to wrap over bpf_obj_drop_impl */ +#define bpf_percpu_obj_drop (kptr) bpf_percpu_obj_drop_impl(kptr, NULL) +#endif
Signature · 1 line
2.34.1
Yonghong Song
reviewerPATCH v2 8/13 selftests/bpf: Add tests for array map with local percpu kptr
In reply to Yonghong SongAdd non-sleepable and sleepable tests with percpu kptr. For non-sleepable test, four programs are executed in the order of:
1. allocate percpu data.
2. assign values to percpu data.
3. retrieve percpu data.
4. de-allocate percpu data.
The sleepable prog tried to exercise all above 4 steps in a single prog. Also for sleepable prog, rcu_read_lock is needed to protect direct percpu ptr access (from map value) and following bpf_this_cpu_ptr() and bpf_per_cpu_ptr() helpers.
--- .../selftests/bpf/prog_tests/percpu_alloc.c | 78 ++++++++ .../selftests/bpf/progs/percpu_alloc_array.c | 187 ++++++++++++++++++ 2 files changed, 265 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/percpu_alloc.c create mode 100644 tools/testing/selftests/bpf/progs/percpu_alloc_array.c
tools/testing/selftests/bpf/prog_tests/percpu_alloc.c new file +78 -0
@@ -0,0 +1,78 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <test_progs.h> +#include "percpu_alloc_array.skel.h" + +static void test_array (void ) +{ +struct percpu_alloc_array *skel; +int err, prog_fd; +LIBBPF_OPTS (bpf_test_run_opts, topts); + + skel =percpu_alloc_array__open (); +if (!ASSERT_OK_PTR (skel,"percpu_alloc_array__open" )) +return ; + +bpf_program__set_autoload (skel->progs.test_array_map_1, true); +bpf_program__set_autoload (skel->progs.test_array_map_2, true); +bpf_program__set_autoload (skel->progs.test_array_map_3, true); +bpf_program__set_autoload (skel->progs.test_array_map_4, true); + + skel->rodata->nr_cpus =libbpf_num_possible_cpus (); + + err =percpu_alloc_array__load (skel); +if (!ASSERT_OK (err,"percpu_alloc_array__load" )) + goto out; + + err =percpu_alloc_array__attach (skel); +if (!ASSERT_OK (err,"percpu_alloc_array__attach" )) + goto out; + + prog_fd =bpf_program__fd (skel->progs.test_array_map_1); + err =bpf_prog_test_run_opts (prog_fd, &topts); +ASSERT_OK (err,"test_run array_map 1-4" ); +ASSERT_EQ (topts.retval,0 ,"test_run array_map 1-4" ); +ASSERT_EQ (skel->bss->cpu0_field_d,2 ,"cpu0_field_d" ); +ASSERT_EQ (skel->bss->sum_field_c,1 ,"sum_field_c" ); +out: +percpu_alloc_array__destroy (skel); +} + +static void test_array_sleepable (void ) +{ +struct percpu_alloc_array *skel; +int err, prog_fd; +LIBBPF_OPTS (bpf_test_run_opts, topts); + + skel =percpu_alloc_array__open (); +if (!ASSERT_OK_PTR (skel,"percpu_alloc__open" )) +return ; + +bpf_program__set_autoload (skel->progs.test_array_map_10, true); + + skel->rodata->nr_cpus =libbpf_num_possible_cpus (); + + err =percpu_alloc_array__load (skel); +if (!ASSERT_OK (err,"percpu_alloc_array__load" )) + goto out; + + err =percpu_alloc_array__attach (skel); +if (!ASSERT_OK (err,"percpu_alloc_array__attach" )) + goto out; + + prog_fd =bpf_program__fd (skel->progs.test_array_map_10); + err =bpf_prog_test_run_opts (prog_fd, &topts); +ASSERT_OK (err,"test_run array_map_10" ); +ASSERT_EQ (topts.retval,0 ,"test_run array_map_10" ); +ASSERT_EQ (skel->bss->cpu0_field_d,2 ,"cpu0_field_d" ); +ASSERT_EQ (skel->bss->sum_field_c,1 ,"sum_field_c" ); +out: +percpu_alloc_array__destroy (skel); +} + +void test_percpu_alloc (void ) +{ +if (test__start_subtest ("array" )) +test_array (); +if (test__start_subtest ("array_sleepable" )) +test_array_sleepable (); +}
tools/testing/selftests/bpf/progs/percpu_alloc_array.c new file +187 -0
187 lines not on this page · raw message
Signature · 1 line
2.34.1
Yonghong Song
reviewerPATCH v2 9/13 bpf: Mark OBJ_RELEASE argument as MEM_RCU when possible
In reply to Yonghong SongIn previous selftests/bpf patch, we have
p = bpf_percpu_obj_new(struct val_t);
if (!p)
goto out;
p1 = bpf_kptr_xchg(&e->pc, p);
if (p1) {
/* race condition */
bpf_percpu_obj_drop(p1);
}
p = e->pc;
if (!p)
goto out;After bpf_kptr_xchg(), we need to re-read e->pc into 'p'. This is due to that the second argument of bpf_kptr_xchg() is marked OBJ_RELEASE and it will be marked as invalid after the call. So after bpf_kptr_xchg(), 'p' is an unknown scalar,
and the bpf program needs to reread from the map value.
This patch checks if the 'p' has type MEM_ALLOC and MEM_PERCPU, and if 'p' is RCU protected. If this is the case, 'p' can be marked as MEM_RCU. MEM_ALLOC needs to be removed since 'p' is not an owning reference any more. Such a change makes re-read from the map value unnecessary.
Note that re-reading 'e->pc' after bpf_kptr_xchg() might get a different value from 'p' if immediately before 'p = e->pc', another cpu may do another bpf_kptr_xchg() and swap in another value into 'e->pc'. If this is the case, then 'p = e->pc' may get either 'p' or another value, and race condition already exists. So removing direct re-reading seems fine too.
--- kernel/bpf/verifier.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+)
kernel/bpf/verifier.c +20 -0
@@ -9660,6 +9660,26 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insnreturn -EFAULT ; } err =unmark_stack_slots_dynptr (env, ®s[meta.release_regno]); + }else if (func_id == BPF_FUNC_kptr_xchg && meta.ref_obj_id) { +u32 ref_obj_id = meta.ref_obj_id; +bool in_rcu =in_rcu_cs (env); +struct bpf_func_state *state; +struct bpf_reg_state *reg; + + err =release_reference_state (cur_func (env), ref_obj_id); +if (!err) { +bpf_for_each_reg_in_vstate (env->cur_state, state, reg, ({ +if (reg->ref_obj_id == ref_obj_id) { +if (in_rcu && (reg->type &MEM_ALLOC ) && (reg->type &MEM_PERCPU )) { + reg->ref_obj_id =0 ; + reg->type &= ~MEM_ALLOC ; + reg->type |=MEM_RCU ; + }else { +mark_reg_invalid (env, reg); + } + } + })); + } }else if (meta.ref_obj_id) { err =release_reference (env, meta.ref_obj_id); }else if (register_is_null (®s[meta.release_regno])) {
Signature · 1 line
2.34.1
Yonghong Song
reviewerPATCH v2 10/13 selftests/bpf: Remove unnecessary direct read of local percpu kptr
In reply to Yonghong SongFor the second argument of bpf_kptr_xchg(), if the reg type contains MEM_ALLOC and MEM_PERCPU, which means a percpu allocation, after bpf_kptr_xchg(), the argument is marked as MEM_RCU and MEM_PERCPU if in rcu critical section. This way, re-reading from the map value is not needed. Remove it from the percpu_alloc_array.c selftest.
Without previous kernel change, the test will fail like below:
0: R1=ctx(off=0,imm=0) R10=fp0
; int BPF_PROG(test_array_map_10, int a)
0: (b4) w1 = 0 ; R1_w=0
; int i, index = 0;
1: (63) *(u32 *)(r10 -4) = r1 ; R1_w=0 R10=fp0 fp-8=0000????
2: (bf) r2 = r10 ; R2_w=fp0 R10=fp0
;
3: (07) r2 += -4 ; R2_w=fp-4
; e = bpf_map_lookup_elem(&array, &index);
4: (18) r1 = 0xffff88810e771800 ; R1_w=map_ptr(off=0,ks=4,vs=16,imm=0)
6: (85) call bpf_map_lookup_elem#1 ; R0_w=map_value_or_null(id=1,off=0,ks=4,vs=16,imm=0)
7: (bf) r6 = r0 ; R0_w=map_value_or_null(id=1,off=0,ks=4,vs=16,imm=0) R6_w=map_value_or_null(id=1,off=0,ks=4,vs=16,imm=0)
; if (!e)
8: (15) if r6 == 0x0 goto pc+81 ; R6_w=map_value(off=0,ks=4,vs=16,imm=0)
; bpf_rcu_read_lock();
9: (85) call bpf_rcu_read_lock#87892 ;
; p = e->pc;
10: (bf) r7 = r6 ; R6=map_value(off=0,ks=4,vs=16,imm=0) R7_w=map_value(off=0,ks=4,vs=16,imm=0)
11: (07) r7 += 8 ; R7_w=map_value(off=8,ks=4,vs=16,imm=0)
12: (79) r6 = *(u64 *)(r6 +8) ; R6_w=percpu_rcu_ptr_or_null_val_t(id=2,off=0,imm=0)
; if (!p) {
13: (55) if r6 != 0x0 goto pc+13 ; R6_w=0
; p = bpf_percpu_obj_new(struct val_t);
14: (18) r1 = 0x12 ; R1_w=18
16: (b7) r2 = 0 ; R2_w=0
17: (85) call bpf_percpu_obj_new_impl#87883 ; R0_w=percpu_ptr_or_null_val_t(id=4,ref_obj_id=4,off=0,imm=0) refs=4
18: (bf) r6 = r0 ; R0=percpu_ptr_or_null_val_t(id=4,ref_obj_id=4,off=0,imm=0) R6=percpu_ptr_or_null_val_t(id=4,ref_obj_id=4,off=0,imm=0) refs=4
; if (!p)
19: (15) if r6 == 0x0 goto pc+69 ; R6=percpu_ptr_val_t(ref_obj_id=4,off=0,imm=0) refs=4
; p1 = bpf_kptr_xchg(&e->pc, p);
20: (bf) r1 = r7 ; R1_w=map_value(off=8,ks=4,vs=16,imm=0) R7=map_value(off=8,ks=4,vs=16,imm=0) refs=4
21: (bf) r2 = r6 ; R2_w=percpu_ptr_val_t(ref_obj_id=4,off=0,imm=0) R6=percpu_ptr_val_t(ref_obj_id=4,off=0,imm=0) refs=4
22: (85) call bpf_kptr_xchg#194 ; R0_w=percpu_ptr_or_null_val_t(id=6,ref_obj_id=6,off=0,imm=0) refs=6
; if (p1) {
23: (15) if r0 == 0x0 goto pc+3 ; R0_w=percpu_ptr_val_t(ref_obj_id=6,off=0,imm=0) refs=6
; bpf_percpu_obj_drop(p1);
24: (bf) r1 = r0 ; R0_w=percpu_ptr_val_t(ref_obj_id=6,off=0,imm=0) R1_w=percpu_ptr_val_t(ref_obj_id=6,off=0,imm=0) refs=6
25: (b7) r2 = 0 ; R2_w=0 refs=6
26: (85) call bpf_percpu_obj_drop_impl#87882 ;
; v = bpf_this_cpu_ptr(p);
27: (bf) r1 = r6 ; R1_w=scalar(id=7) R6=scalar(id=7)
28: (85) call bpf_this_cpu_ptr#154
R1 type=scalar expected=percpu_ptr_, percpu_rcu_ptr_, percpu_trusted_ptr_The R1 which gets its value from R6 is a scalar. But before insn 22, R6 is
R6=percpu_ptr_val_t(ref_obj_id=4,off=0,imm=0)
Its type is changed to a scalar at insn 22 without previous patch.
--- tools/testing/selftests/bpf/progs/percpu_alloc_array.c | 4 ---- 1 file changed, 4 deletions(-)
tools/testing/selftests/bpf/progs/percpu_alloc_array.c +0 -4
@@ -146,10 +146,6 @@ int BPF_PROG(test_array_map_10)/* race condition */ bpf_percpu_obj_drop (p1); } - - p = e->pc; -if (!p) - goto out; } v =bpf_this_cpu_ptr (p);
Signature · 1 line
2.34.1
Yonghong Song
reviewerPATCH v2 11/13 selftests/bpf: Add tests for cgrp_local_storage with local percpu kptr
In reply to Yonghong SongAdd a non-sleepable cgrp_local_storage test with percpu kptr. The test does allocation of percpu data, assigning values to percpu data and retrieval of percpu data. The de-allocation of percpu data is done when the map is freed.
--- .../selftests/bpf/prog_tests/percpu_alloc.c | 40 +++++++ .../progs/percpu_alloc_cgrp_local_storage.c | 105 ++++++++++++++++++ 2 files changed, 145 insertions(+) create mode 100644 tools/testing/selftests/bpf/progs/percpu_alloc_cgrp_local_storage.c
tools/testing/selftests/bpf/prog_tests/percpu_alloc.c +40 -0
@@ -1,6 +1,7 @@// SPDX-License-Identifier: GPL-2.0 #include <test_progs.h> #include "percpu_alloc_array.skel.h" +#include "percpu_alloc_cgrp_local_storage.skel.h" static void test_array (void ) {
@@ -69,10 +70,49 @@ static void test_array_sleepable(void)percpu_alloc_array__destroy (skel); } +static void test_cgrp_local_storage (void ) +{ +struct percpu_alloc_cgrp_local_storage *skel; +int err, cgroup_fd, prog_fd; +LIBBPF_OPTS (bpf_test_run_opts, topts); + + cgroup_fd =test__join_cgroup ("/percpu_alloc" ); +if (!ASSERT_GE (cgroup_fd,0 ,"join_cgroup /percpu_alloc" )) +return ; + + skel =percpu_alloc_cgrp_local_storage__open (); +if (!ASSERT_OK_PTR (skel,"percpu_alloc_cgrp_local_storage__open" )) + goto close_fd; + + skel->rodata->nr_cpus =libbpf_num_possible_cpus (); + + err =percpu_alloc_cgrp_local_storage__load (skel); +if (!ASSERT_OK (err,"percpu_alloc_cgrp_local_storage__load" )) + goto destroy_skel; + + err =percpu_alloc_cgrp_local_storage__attach (skel); +if (!ASSERT_OK (err,"percpu_alloc_cgrp_local_storage__attach" )) + goto destroy_skel; + + prog_fd =bpf_program__fd (skel->progs.test_cgrp_local_storage_1); + err =bpf_prog_test_run_opts (prog_fd, &topts); +ASSERT_OK (err,"test_run cgrp_local_storage 1-3" ); +ASSERT_EQ (topts.retval,0 ,"test_run cgrp_local_storage 1-3" ); +ASSERT_EQ (skel->bss->cpu0_field_d,2 ,"cpu0_field_d" ); +ASSERT_EQ (skel->bss->sum_field_c,1 ,"sum_field_c" ); + +destroy_skel: +percpu_alloc_cgrp_local_storage__destroy (skel); +close_fd: +close (cgroup_fd); +} +void test_percpu_alloc (void ) {if (test__start_subtest ("array" ))test_array ();if (test__start_subtest ("array_sleepable" ))test_array_sleepable (); +if (test__start_subtest ("cgrp_local_storage" )) +test_cgrp_local_storage (); }
tools/testing/selftests/bpf/progs/percpu_alloc_cgrp_local_storage.c new file +105 -0
105 lines not on this page · raw message
Signature · 1 line
2.34.1
Yonghong Song
reviewerPATCH v2 12/13 selftests/bpf: Add some negative tests
In reply to Yonghong SongAdd a few negative tests for common mistakes with using percpu kptr including:
- store to percpu kptr.
- type mistach in bpf_kptr_xchg arguments.
- sleepable prog with untrusted arg for bpf_this_cpu_ptr().
- bpf_percpu_obj_new && bpf_obj_drop, and bpf_obj_new && bpf_percpu_obj_drop
- struct with ptr for bpf_percpu_obj_new
- struct with special field (e.g., bpf_spin_lock) for bpf_percpu_obj_new
--- .../selftests/bpf/prog_tests/percpu_alloc.c | 7 + .../selftests/bpf/progs/percpu_alloc_fail.c | 164 ++++++++++++++++++ 2 files changed, 171 insertions(+) create mode 100644 tools/testing/selftests/bpf/progs/percpu_alloc_fail.c
tools/testing/selftests/bpf/prog_tests/percpu_alloc.c +7 -0
@@ -2,6 +2,7 @@#include <test_progs.h> #include "percpu_alloc_array.skel.h" #include "percpu_alloc_cgrp_local_storage.skel.h" +#include "percpu_alloc_fail.skel.h" static void test_array (void ) {
@@ -107,6 +108,10 @@ static void test_cgrp_local_storage(void)close (cgroup_fd); } +static void test_failure (void ) { +RUN_TESTS (percpu_alloc_fail); +} +void test_percpu_alloc (void ) {if (test__start_subtest ("array" ))
@@ -115,4 +120,6 @@ void test_percpu_alloc(void)test_array_sleepable ();if (test__start_subtest ("cgrp_local_storage" ))test_cgrp_local_storage (); +if (test__start_subtest ("failure_tests" )) +test_failure (); }
tools/testing/selftests/bpf/progs/percpu_alloc_fail.c new file +164 -0
164 lines not on this page · raw message
Signature · 1 line
2.34.1
Yonghong Song
reviewerPATCH v2 13/13 bpf: Mark BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE deprecated
In reply to Yonghong SongNow 'BPF_MAP_TYPE_CGRP_STORAGE + local percpu ptr'
can cover all BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE functionality and more. So mark BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE deprecated. Also make a change in selftests/bpf/test_bpftool_synctypes.py to fix a test error.
--- include/uapi/linux/bpf.h | 9 ++++++++- tools/include/uapi/linux/bpf.h | 9 ++++++++- tools/testing/selftests/bpf/test_bpftool_synctypes.py | 9 +++++++++ 3 files changed, 25 insertions(+), 2 deletions(-)
include/uapi/linux/bpf.h +8 -1
@@ -932,7 +932,14 @@ enum bpf_map_type {*/ BPF_MAP_TYPE_CGROUP_STORAGE =BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED ,BPF_MAP_TYPE_REUSEPORT_SOCKARRAY , -BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE , +BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE_DEPRECATED , +/* BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE is available to bpf programs +* attaching to a cgroup. The new mechanism (BPF_MAP_TYPE_CGRP_STORAGE + +* local percpu kptr) supports all BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE +* functionality and more. So mark * BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE +* deprecated. +*/ +BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE =BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE_DEPRECATED ,BPF_MAP_TYPE_QUEUE ,BPF_MAP_TYPE_STACK , BPF_MAP_TYPE_SK_STORAGE,
tools/include/uapi/linux/bpf.h +8 -1
@@ -932,7 +932,14 @@ enum bpf_map_type {*/ BPF_MAP_TYPE_CGROUP_STORAGE =BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED ,BPF_MAP_TYPE_REUSEPORT_SOCKARRAY , -BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE , +BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE_DEPRECATED , +/* BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE is available to bpf programs +* attaching to a cgroup. The new mechanism (BPF_MAP_TYPE_CGRP_STORAGE + +* local percpu kptr) supports all BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE +* functionality and more. So mark * BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE +* deprecated. +*/ +BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE =BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE_DEPRECATED ,BPF_MAP_TYPE_QUEUE ,BPF_MAP_TYPE_STACK , BPF_MAP_TYPE_SK_STORAGE,
tools/testing/selftests/bpf/test_bpftool_synctypes.py +9 -0
@@ -509,6 +509,15 @@ def main(): source_map_types.remove('cgroup_storage_deprecated') source_map_types.add('cgroup_storage') + # The same applied to BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE_DEPRECATED and + # BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE which share the same enum value + # and source_map_types picks + # BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE_DEPRECATED/percpu_cgroup_storage_deprecated. + # Replace 'percpu_cgroup_storage_deprecated' with 'percpu_cgroup_storage' + # so it aligns with what `bpftool map help` shows. + source_map_types.remove('percpu_cgroup_storage_deprecated') + source_map_types.add('percpu_cgroup_storage') + help_map_types = map_info.get_map_help() help_map_options = map_info.get_options() map_info.close()
Signature · 1 line
2.34.1
Yonghong Song
reviewerQuoted 83 lines · Yonghong Song
Patch set [1] implemented cgroup local storage
BPF_MAP_TYPE_CGRP_STORAGEsimilar to sk/task/inode local storage and oldBPF_MAP_TYPE_CGROUP_STORAGEmap is marked as deprecated since oldBPF_MAP_TYPE_CGROUP_STORAGEmap can only work with current cgroup.Similarly, the existing
BPF_MAP_TYPE_PERCPU_CGROUP_STORAGEmap is a percpu version ofBPF_MAP_TYPE_CGROUP_STORAGEand only works with current cgroup. But there is no replacement which can work with arbitrary cgroup.This patch set solved this problem but adding support for local percpu kptr. The map value can have a percpu kptr field which holds a bpf prog allocated percpu data. The below is an example,
struct percpu_val_t { ... fields ... } struct map_value_t { struct percpu_val_t __percpu *percpu_data_ptr; }In the above, '
map_value_t' is the map value type for aBPF_MAP_TYPE_CGRP_STORAGEmap. User can access 'percpu_data_ptr' and then read/write percpu data. This coversBPF_MAP_TYPE_PERCPU_CGROUP_STORAGEand more. SoBPF_MAP_TYPE_PERCPU_CGROUP_STORAGEmap type
is marked as deprecated.In additional, local percpu kptr supports the same map type as other kptrs including hash,
lru_hash, array, sk/inode/task/cgrp local storage. Currently, percpu data structure does not support non-scalars or special fields (e.g.,bpf_spin_lock,bpf_rb_root, etc.). They can be supported in the future if there exist use cases.Please for individual patches for details.
[1] https://lore.kernel.org/all/[email protected]/Changelog:
v1 -> v2:
- does not support special fields in percpu data structure.
- rename__percpuattr to__percpu_kptrattr.
- renameBPF_KPTR_PERCPU_REFtoBPF_KPTR_PERCPU.
- better code to handle bpf_{this,per}_cpu_ptr() helpers.
- add more negative tests.
- fix a bpftool related test failure.Yonghong Song (13):
bpf: Add support for non-fix-size percpu mem allocation
bpf: AddBPF_KPTR_PERCPUas a field type
bpf: Add alloc/xchg/direct_access support for local percpu kptr
bpf: Add bpf_this_cpu_ptr/bpf_per_cpu_ptr support for allocated percpu
obj
selftests/bpf: Update error message in negativelinked_listtest
libbpf: Add__percpu_kptrmacro definition
selftests/bpf: Add bpf_percpu_obj_{new,drop}() macro in
bpf_experimental.h
selftests/bpf: Add tests for array map with local percpu kptr
bpf: MarkOBJ_RELEASEargument asMEM_RCUwhen possible selftests/bpf: Remove unnecessary direct read of local percpu kptr selftests/bpf: Add tests forcgrp_local_storagewith local percpu kptr selftests/bpf: Add some negative tests
bpf: MarkBPF_MAP_TYPE_PERCPU_CGROUP_STORAGEdeprecatedinclude/linux/bpf.h | 22 +- include/linux/bpf_verifier.h | 1 + include/uapi/linux/bpf.h | 9 +- kernel/bpf/btf.c | 5 + kernel/bpf/core.c | 8 +- kernel/bpf/helpers.c | 16 ++ kernel/bpf/memalloc.c | 14 +- kernel/bpf/syscall.c | 4 + kernel/bpf/verifier.c | 191 +++++++++++++++--- tools/include/uapi/linux/bpf.h | 9 +- tools/lib/bpf/bpf_helpers.h | 1 + .../testing/selftests/bpf/bpf_experimental.h | 31 +++ .../selftests/bpf/prog_tests/linked_list.c | 4 +- .../selftests/bpf/prog_tests/percpu_alloc.c | 125 ++++++++++++ .../selftests/bpf/progs/percpu_alloc_array.c | 183 +++++++++++++++++ .../progs/percpu_alloc_cgrp_local_storage.c | 105 ++++++++++ .../selftests/bpf/progs/percpu_alloc_fail.c | 164 +++++++++++++++ .../selftests/bpf/test_bpftool_synctypes.py | 9 + 18 files changed, 848 insertions(+), 53 deletions(-)
Quoted 4 lines
create mode 100644
tools/testing/selftests/bpf/prog_tests/percpu_alloc.ccreate mode 100644tools/testing/selftests/bpf/progs/percpu_alloc_array.ccreate mode 100644tools/testing/selftests/bpf/progs/percpu_alloc_cgrp_local_storage.ccreate mode 100644tools/testing/selftests/bpf/progs/percpu_alloc_fail.c
There is a test failure for libbpf_str/bpf_map_type_str reported by CI, I will fix it in the next revision.