Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support dynptr key for hash map #4919

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
d5fc6ca
bpf: Add two helpers to facilitate the parsing of bpf_dynptr
Jan 25, 2025
939a3f8
bpf: Parse bpf_dynptr in map key
Jan 25, 2025
0e84736
bpf: Factor out get_map_btf() helper
Jan 25, 2025
605e7ee
bpf: Move the initialization of btf before ->map_alloc_check
Jan 25, 2025
e587155
bpf: Introduce an internal map flag BPF_INT_F_DYNPTR_IN_KEY
Jan 25, 2025
021f69b
bpf: Set BPF_INT_F_DYNPTR_IN_KEY conditionally
Jan 25, 2025
33843cc
bpf: Use map_extra to indicate the max data size of dynptrs in map key
Jan 25, 2025
f867109
bpf: Split check_stack_range_initialized() into small functions
Jan 25, 2025
b96589b
bpf: Support map key with dynptr in verifier
Jan 25, 2025
3865807
bpf: Introduce bpf_dynptr_user
Jan 25, 2025
5346367
bpf: Handle bpf_dynptr_user in bpf syscall when it is used as input
Jan 25, 2025
17fa13f
bpf: Handle bpf_dynptr_user in bpf syscall when it is used as output
Jan 25, 2025
1a3356a
bpf: Support basic operations for dynptr key in hash map
Jan 25, 2025
5ec9490
bpf: Export bpf_dynptr_set_size
Jan 25, 2025
00db665
bpf: Support get_next_key operation for dynptr key in hash map
Jan 25, 2025
3e3da0a
bpf: Disable unsupported operations for map with dynptr key
Jan 25, 2025
ac9883f
bpf: Enable BPF_INT_F_DYNPTR_IN_KEY for hash map
Jan 25, 2025
b7ec685
selftests/bpf: Add bpf_dynptr_user_init() helper
Jan 25, 2025
b3a1698
selftests/bpf: Add test cases for hash map with dynptr key
Jan 25, 2025
bf1158f
selftests/bpf: Add benchmark for dynptr key support in hash map
Jan 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 37 additions & 3 deletions include/linux/bpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,8 @@ struct bpf_map_ops {
};

enum {
/* Support at most 11 fields in a BTF type */
BTF_FIELDS_MAX = 11,
/* Support at most 13 fields in a BTF type */
BTF_FIELDS_MAX = 13,
};

enum btf_field_type {
Expand All @@ -204,6 +204,7 @@ enum btf_field_type {
BPF_REFCOUNT = (1 << 9),
BPF_WORKQUEUE = (1 << 10),
BPF_UPTR = (1 << 11),
BPF_DYNPTR = (1 << 12),
};

typedef void (*btf_dtor_kfunc_t)(void *);
Expand Down Expand Up @@ -257,6 +258,14 @@ struct bpf_list_node_kern {
void *owner;
} __attribute__((aligned(8)));

/* Internal map flags */
enum {
/* map key supports bpf_dynptr */
BPF_INT_F_DYNPTR_IN_KEY = (1U << 31),
};

#define BPF_INT_F_MASK (1U << 31)

struct bpf_map {
const struct bpf_map_ops *ops;
struct bpf_map *inner_map_meta;
Expand All @@ -268,9 +277,20 @@ struct bpf_map {
u32 value_size;
u32 max_entries;
u64 map_extra; /* any per-map-type extra fields */
/* The topmost bit of map_flags is used as an internal map flag
* (aka BPF_INT_F_DYNPTR_IN_KEY) and it can't be set through bpf
* syscall.
*/
u32 map_flags;
u32 id;
/* BTF record for special fields in map value. bpf_dynptr is disallowed
* at present.
*/
struct btf_record *record;
/* BTF record for special fields in map key. Only bpf_dynptr is allowed
* at present.
*/
struct btf_record *key_record;
int numa_node;
u32 btf_key_type_id;
u32 btf_value_type_id;
Expand Down Expand Up @@ -309,6 +329,11 @@ struct bpf_map {
s64 __percpu *elem_count;
};

static inline bool bpf_map_has_dynptr_key(const struct bpf_map *map)
{
return map->map_flags & BPF_INT_F_DYNPTR_IN_KEY;
}

static inline const char *btf_field_type_name(enum btf_field_type type)
{
switch (type) {
Expand All @@ -335,6 +360,8 @@ static inline const char *btf_field_type_name(enum btf_field_type type)
return "bpf_rb_node";
case BPF_REFCOUNT:
return "bpf_refcount";
case BPF_DYNPTR:
return "bpf_dynptr";
default:
WARN_ON_ONCE(1);
return "unknown";
Expand Down Expand Up @@ -365,6 +392,8 @@ static inline u32 btf_field_type_size(enum btf_field_type type)
return sizeof(struct bpf_rb_node);
case BPF_REFCOUNT:
return sizeof(struct bpf_refcount);
case BPF_DYNPTR:
return sizeof(struct bpf_dynptr);
default:
WARN_ON_ONCE(1);
return 0;
Expand Down Expand Up @@ -395,6 +424,8 @@ static inline u32 btf_field_type_align(enum btf_field_type type)
return __alignof__(struct bpf_rb_node);
case BPF_REFCOUNT:
return __alignof__(struct bpf_refcount);
case BPF_DYNPTR:
return __alignof__(struct bpf_dynptr);
default:
WARN_ON_ONCE(1);
return 0;
Expand Down Expand Up @@ -425,6 +456,7 @@ static inline void bpf_obj_init_field(const struct btf_field *field, void *addr)
case BPF_KPTR_REF:
case BPF_KPTR_PERCPU:
case BPF_UPTR:
case BPF_DYNPTR:
break;
default:
WARN_ON_ONCE(1);
Expand Down Expand Up @@ -603,7 +635,8 @@ static inline bool bpf_map_offload_neutral(const struct bpf_map *map)
static inline bool bpf_map_support_seq_show(const struct bpf_map *map)
{
return (map->btf_value_type_id || map->btf_vmlinux_value_type_id) &&
map->ops->map_seq_show_elem;
map->ops->map_seq_show_elem &&
!bpf_map_has_dynptr_key(map);
}

int map_check_no_btf(const struct bpf_map *map,
Expand Down Expand Up @@ -1319,6 +1352,7 @@ enum bpf_dynptr_type {
};

int bpf_dynptr_check_size(u32 size);
void bpf_dynptr_set_size(struct bpf_dynptr_kern *ptr, u32 new_size);
u32 __bpf_dynptr_size(const struct bpf_dynptr_kern *ptr);
const void *__bpf_dynptr_data(const struct bpf_dynptr_kern *ptr, u32 len);
void *__bpf_dynptr_data_rw(const struct bpf_dynptr_kern *ptr, u32 len);
Expand Down
2 changes: 2 additions & 0 deletions include/linux/btf.h
Original file line number Diff line number Diff line change
Expand Up @@ -223,8 +223,10 @@ bool btf_member_is_reg_int(const struct btf *btf, const struct btf_type *s,
u32 expected_offset, u32 expected_size);
struct btf_record *btf_parse_fields(const struct btf *btf, const struct btf_type *t,
u32 field_mask, u32 value_size);
struct btf_record *btf_new_bpf_dynptr_record(void);
int btf_check_and_fixup_fields(const struct btf *btf, struct btf_record *rec);
bool btf_type_is_void(const struct btf_type *t);
bool btf_type_is_dynptr(const struct btf *btf, const struct btf_type *t);
s32 btf_find_by_name_kind(const struct btf *btf, const char *name, u8 kind);
s32 bpf_find_btf_id(const char *name, u32 kind, struct btf **btf_p);
const struct btf_type *btf_type_skip_modifiers(const struct btf *btf,
Expand Down
6 changes: 6 additions & 0 deletions include/uapi/linux/bpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -7335,6 +7335,12 @@ struct bpf_dynptr {
__u64 __opaque[2];
} __attribute__((aligned(8)));

struct bpf_dynptr_user {
__bpf_md_ptr(void *, data);
__u32 size;
__u32 reserved;
} __attribute__((aligned(8)));

struct bpf_list_head {
__u64 __opaque[2];
} __attribute__((aligned(8)));
Expand Down
46 changes: 41 additions & 5 deletions kernel/bpf/btf.c
Original file line number Diff line number Diff line change
Expand Up @@ -3500,6 +3500,7 @@ static int btf_get_field_type(const struct btf *btf, const struct btf_type *var_
field_mask_test_name(BPF_RB_ROOT, "bpf_rb_root");
field_mask_test_name(BPF_RB_NODE, "bpf_rb_node");
field_mask_test_name(BPF_REFCOUNT, "bpf_refcount");
field_mask_test_name(BPF_DYNPTR, "bpf_dynptr");

/* Only return BPF_KPTR when all other types with matchable names fail */
if (field_mask & (BPF_KPTR | BPF_UPTR) && !__btf_type_is_struct(var_type)) {
Expand Down Expand Up @@ -3538,6 +3539,7 @@ static int btf_repeat_fields(struct btf_field_info *info, int info_cnt,
case BPF_UPTR:
case BPF_LIST_HEAD:
case BPF_RB_ROOT:
case BPF_DYNPTR:
break;
default:
return -EINVAL;
Expand Down Expand Up @@ -3660,6 +3662,7 @@ static int btf_find_field_one(const struct btf *btf,
case BPF_LIST_NODE:
case BPF_RB_NODE:
case BPF_REFCOUNT:
case BPF_DYNPTR:
ret = btf_find_struct(btf, var_type, off, sz, field_type,
info_cnt ? &info[0] : &tmp);
if (ret < 0)
Expand Down Expand Up @@ -3925,6 +3928,16 @@ static int btf_field_cmp(const void *_a, const void *_b, const void *priv)
return 0;
}

static void btf_init_record(struct btf_record *record)
{
record->cnt = 0;
record->field_mask = 0;
record->spin_lock_off = -EINVAL;
record->timer_off = -EINVAL;
record->wq_off = -EINVAL;
record->refcount_off = -EINVAL;
}

struct btf_record *btf_parse_fields(const struct btf *btf, const struct btf_type *t,
u32 field_mask, u32 value_size)
{
Expand All @@ -3943,14 +3956,11 @@ struct btf_record *btf_parse_fields(const struct btf *btf, const struct btf_type
/* This needs to be kzalloc to zero out padding and unused fields, see
* comment in btf_record_equal.
*/
rec = kzalloc(offsetof(struct btf_record, fields[cnt]), GFP_KERNEL | __GFP_NOWARN);
rec = kzalloc(struct_size(rec, fields, cnt), GFP_KERNEL | __GFP_NOWARN);
if (!rec)
return ERR_PTR(-ENOMEM);

rec->spin_lock_off = -EINVAL;
rec->timer_off = -EINVAL;
rec->wq_off = -EINVAL;
rec->refcount_off = -EINVAL;
btf_init_record(rec);
for (i = 0; i < cnt; i++) {
field_type_size = btf_field_type_size(info_arr[i].type);
if (info_arr[i].off + field_type_size > value_size) {
Expand Down Expand Up @@ -4010,6 +4020,7 @@ struct btf_record *btf_parse_fields(const struct btf *btf, const struct btf_type
break;
case BPF_LIST_NODE:
case BPF_RB_NODE:
case BPF_DYNPTR:
break;
default:
ret = -EFAULT;
Expand Down Expand Up @@ -4041,6 +4052,25 @@ struct btf_record *btf_parse_fields(const struct btf *btf, const struct btf_type
return ERR_PTR(ret);
}

struct btf_record *btf_new_bpf_dynptr_record(void)
{
struct btf_record *record;

record = kzalloc(struct_size(record, fields, 1), GFP_KERNEL | __GFP_NOWARN);
if (!record)
return ERR_PTR(-ENOMEM);

btf_init_record(record);

record->cnt = 1;
record->field_mask = BPF_DYNPTR;
record->fields[0].offset = 0;
record->fields[0].size = sizeof(struct bpf_dynptr);
record->fields[0].type = BPF_DYNPTR;

return record;
}

int btf_check_and_fixup_fields(const struct btf *btf, struct btf_record *rec)
{
int i;
Expand Down Expand Up @@ -7439,6 +7469,12 @@ static bool btf_is_dynptr_ptr(const struct btf *btf, const struct btf_type *t)
return false;
}

bool btf_type_is_dynptr(const struct btf *btf, const struct btf_type *t)
{
return __btf_type_is_struct(t) && t->size == sizeof(struct bpf_dynptr) &&
!strcmp(__btf_name_by_offset(btf, t->name_off), "bpf_dynptr");
}

struct bpf_cand_cache {
const char *name;
u32 name_len;
Expand Down
Loading
Loading