summaryrefslogtreecommitdiff
path: root/kernel/bpf/map_in_map.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/bpf/map_in_map.c')
-rw-r--r--kernel/bpf/map_in_map.c11
1 files changed, 9 insertions, 2 deletions
diff --git a/kernel/bpf/map_in_map.c b/kernel/bpf/map_in_map.c
index d2cbab4bdf64..0fa3f2ac0f99 100644
--- a/kernel/bpf/map_in_map.c
+++ b/kernel/bpf/map_in_map.c
@@ -25,6 +25,10 @@ struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd)
if (!inner_map->ops->map_meta_equal)
return ERR_PTR(-ENOTSUPP);
+ /* An inner map has no used_maps reference to hold it under a queued callback. */
+ if (btf_record_has_field(inner_map->record, BPF_RCU_HEAD))
+ return ERR_PTR(-EOPNOTSUPP);
+
inner_map_meta_size = sizeof(*inner_map_meta);
/* In some cases verifier needs to access beyond just base map. */
if (inner_map->ops == &array_map_ops || inner_map->ops == &percpu_array_map_ops)
@@ -106,10 +110,13 @@ void *bpf_map_fd_get_ptr(struct bpf_map *map,
return ERR_PTR(-ENOTSUPP);
inner_map_meta = map->inner_map_meta;
- if (inner_map_meta->ops->map_meta_equal(inner_map_meta, inner_map))
+ if (inner_map_meta->ops->map_meta_equal(inner_map_meta, inner_map)) {
bpf_map_inc(inner_map);
- else
+ /* Its users are those of the outer maps from now on */
+ WRITE_ONCE(inner_map->was_inner_map, true);
+ } else {
inner_map = ERR_PTR(-EINVAL);
+ }
return inner_map;
}