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 #8405

Open
wants to merge 20 commits into
base: bpf-next_base
Choose a base branch
from
Open
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
1b579a5
bpf: Add two helpers to facilitate the parsing of bpf_dynptr
Jan 25, 2025
fc852ad
bpf: Parse bpf_dynptr in map key
Jan 25, 2025
a43d692
bpf: Factor out get_map_btf() helper
Jan 25, 2025
e8434c5
bpf: Move the initialization of btf before ->map_alloc_check
Jan 25, 2025
b1dc37e
bpf: Introduce an internal map flag BPF_INT_F_DYNPTR_IN_KEY
Jan 25, 2025
9aa5177
bpf: Set BPF_INT_F_DYNPTR_IN_KEY conditionally
Jan 25, 2025
3c3cbd9
bpf: Use map_extra to indicate the max data size of dynptrs in map key
Jan 25, 2025
4ad436d
bpf: Split check_stack_range_initialized() into small functions
Jan 25, 2025
3439e13
bpf: Support map key with dynptr in verifier
Jan 25, 2025
f765cd6
bpf: Introduce bpf_dynptr_user
Jan 25, 2025
757b7db
bpf: Handle bpf_dynptr_user in bpf syscall when it is used as input
Jan 25, 2025
062ca8a
bpf: Handle bpf_dynptr_user in bpf syscall when it is used as output
Jan 25, 2025
63458e2
bpf: Support basic operations for dynptr key in hash map
Jan 25, 2025
3de6332
bpf: Export bpf_dynptr_set_size
Jan 25, 2025
e4892e1
bpf: Support get_next_key operation for dynptr key in hash map
Jan 25, 2025
521966f
bpf: Disable unsupported operations for map with dynptr key
Jan 25, 2025
e8f3bc8
bpf: Enable BPF_INT_F_DYNPTR_IN_KEY for hash map
Jan 25, 2025
5557707
selftests/bpf: Add bpf_dynptr_user_init() helper
Jan 25, 2025
54b9804
selftests/bpf: Add test cases for hash map with dynptr key
Jan 25, 2025
e4396e1
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