|
16 | 16 | #include <linux/rculist_nulls.h>
|
17 | 17 | #include "percpu_freelist.h"
|
18 | 18 | #include "bpf_lru_list.h"
|
| 19 | +#include "map_in_map.h" |
19 | 20 |
|
20 | 21 | struct bucket {
|
21 | 22 | struct hlist_nulls_head head;
|
@@ -88,6 +89,11 @@ static inline void __percpu *htab_elem_get_ptr(struct htab_elem *l, u32 key_size
|
88 | 89 | return *(void __percpu **)(l->key + key_size);
|
89 | 90 | }
|
90 | 91 |
|
| 92 | +static void *fd_htab_map_get_ptr(const struct bpf_map *map, struct htab_elem *l) |
| 93 | +{ |
| 94 | + return *(void **)(l->key + roundup(map->key_size, 8)); |
| 95 | +} |
| 96 | + |
91 | 97 | static struct htab_elem *get_htab_elem(struct bpf_htab *htab, int i)
|
92 | 98 | {
|
93 | 99 | return (struct htab_elem *) (htab->elems + i * htab->elem_size);
|
@@ -603,6 +609,14 @@ static void htab_elem_free_rcu(struct rcu_head *head)
|
603 | 609 |
|
604 | 610 | static void free_htab_elem(struct bpf_htab *htab, struct htab_elem *l)
|
605 | 611 | {
|
| 612 | + struct bpf_map *map = &htab->map; |
| 613 | + |
| 614 | + if (map->ops->map_fd_put_ptr) { |
| 615 | + void *ptr = fd_htab_map_get_ptr(map, l); |
| 616 | + |
| 617 | + map->ops->map_fd_put_ptr(ptr); |
| 618 | + } |
| 619 | + |
606 | 620 | if (l->state == HTAB_EXTRA_ELEM_USED) {
|
607 | 621 | l->state = HTAB_EXTRA_ELEM_FREE;
|
608 | 622 | return;
|
@@ -1057,6 +1071,7 @@ static void delete_all_elements(struct bpf_htab *htab)
|
1057 | 1071 | }
|
1058 | 1072 | }
|
1059 | 1073 | }
|
| 1074 | + |
1060 | 1075 | /* Called when map->refcnt goes to zero, either from workqueue or from syscall */
|
1061 | 1076 | static void htab_map_free(struct bpf_map *map)
|
1062 | 1077 | {
|
@@ -1213,12 +1228,118 @@ static struct bpf_map_type_list htab_lru_percpu_type __ro_after_init = {
|
1213 | 1228 | .type = BPF_MAP_TYPE_LRU_PERCPU_HASH,
|
1214 | 1229 | };
|
1215 | 1230 |
|
| 1231 | +static struct bpf_map *fd_htab_map_alloc(union bpf_attr *attr) |
| 1232 | +{ |
| 1233 | + struct bpf_map *map; |
| 1234 | + |
| 1235 | + if (attr->value_size != sizeof(u32)) |
| 1236 | + return ERR_PTR(-EINVAL); |
| 1237 | + |
| 1238 | + /* pointer is stored internally */ |
| 1239 | + attr->value_size = sizeof(void *); |
| 1240 | + map = htab_map_alloc(attr); |
| 1241 | + attr->value_size = sizeof(u32); |
| 1242 | + |
| 1243 | + return map; |
| 1244 | +} |
| 1245 | + |
| 1246 | +static void fd_htab_map_free(struct bpf_map *map) |
| 1247 | +{ |
| 1248 | + struct bpf_htab *htab = container_of(map, struct bpf_htab, map); |
| 1249 | + struct hlist_nulls_node *n; |
| 1250 | + struct hlist_nulls_head *head; |
| 1251 | + struct htab_elem *l; |
| 1252 | + int i; |
| 1253 | + |
| 1254 | + for (i = 0; i < htab->n_buckets; i++) { |
| 1255 | + head = select_bucket(htab, i); |
| 1256 | + |
| 1257 | + hlist_nulls_for_each_entry_safe(l, n, head, hash_node) { |
| 1258 | + void *ptr = fd_htab_map_get_ptr(map, l); |
| 1259 | + |
| 1260 | + map->ops->map_fd_put_ptr(ptr); |
| 1261 | + } |
| 1262 | + } |
| 1263 | + |
| 1264 | + htab_map_free(map); |
| 1265 | +} |
| 1266 | + |
| 1267 | +/* only called from syscall */ |
| 1268 | +int bpf_fd_htab_map_update_elem(struct bpf_map *map, struct file *map_file, |
| 1269 | + void *key, void *value, u64 map_flags) |
| 1270 | +{ |
| 1271 | + void *ptr; |
| 1272 | + int ret; |
| 1273 | + u32 ufd = *(u32 *)value; |
| 1274 | + |
| 1275 | + ptr = map->ops->map_fd_get_ptr(map, map_file, ufd); |
| 1276 | + if (IS_ERR(ptr)) |
| 1277 | + return PTR_ERR(ptr); |
| 1278 | + |
| 1279 | + ret = htab_map_update_elem(map, key, &ptr, map_flags); |
| 1280 | + if (ret) |
| 1281 | + map->ops->map_fd_put_ptr(ptr); |
| 1282 | + |
| 1283 | + return ret; |
| 1284 | +} |
| 1285 | + |
| 1286 | +static struct bpf_map *htab_of_map_alloc(union bpf_attr *attr) |
| 1287 | +{ |
| 1288 | + struct bpf_map *map, *inner_map_meta; |
| 1289 | + |
| 1290 | + inner_map_meta = bpf_map_meta_alloc(attr->inner_map_fd); |
| 1291 | + if (IS_ERR(inner_map_meta)) |
| 1292 | + return inner_map_meta; |
| 1293 | + |
| 1294 | + map = fd_htab_map_alloc(attr); |
| 1295 | + if (IS_ERR(map)) { |
| 1296 | + bpf_map_meta_free(inner_map_meta); |
| 1297 | + return map; |
| 1298 | + } |
| 1299 | + |
| 1300 | + map->inner_map_meta = inner_map_meta; |
| 1301 | + |
| 1302 | + return map; |
| 1303 | +} |
| 1304 | + |
| 1305 | +static void *htab_of_map_lookup_elem(struct bpf_map *map, void *key) |
| 1306 | +{ |
| 1307 | + struct bpf_map **inner_map = htab_map_lookup_elem(map, key); |
| 1308 | + |
| 1309 | + if (!inner_map) |
| 1310 | + return NULL; |
| 1311 | + |
| 1312 | + return READ_ONCE(*inner_map); |
| 1313 | +} |
| 1314 | + |
| 1315 | +static void htab_of_map_free(struct bpf_map *map) |
| 1316 | +{ |
| 1317 | + bpf_map_meta_free(map->inner_map_meta); |
| 1318 | + fd_htab_map_free(map); |
| 1319 | +} |
| 1320 | + |
| 1321 | +static const struct bpf_map_ops htab_of_map_ops = { |
| 1322 | + .map_alloc = htab_of_map_alloc, |
| 1323 | + .map_free = htab_of_map_free, |
| 1324 | + .map_get_next_key = htab_map_get_next_key, |
| 1325 | + .map_lookup_elem = htab_of_map_lookup_elem, |
| 1326 | + .map_delete_elem = htab_map_delete_elem, |
| 1327 | + .map_fd_get_ptr = bpf_map_fd_get_ptr, |
| 1328 | + .map_fd_put_ptr = bpf_map_fd_put_ptr, |
| 1329 | +}; |
| 1330 | + |
| 1331 | +static struct bpf_map_type_list htab_of_map_type __ro_after_init = { |
| 1332 | + .ops = &htab_of_map_ops, |
| 1333 | + .type = BPF_MAP_TYPE_HASH_OF_MAPS, |
| 1334 | +}; |
| 1335 | + |
1216 | 1336 | static int __init register_htab_map(void)
|
1217 | 1337 | {
|
1218 | 1338 | bpf_register_map_type(&htab_type);
|
1219 | 1339 | bpf_register_map_type(&htab_percpu_type);
|
1220 | 1340 | bpf_register_map_type(&htab_lru_type);
|
1221 | 1341 | bpf_register_map_type(&htab_lru_percpu_type);
|
| 1342 | + bpf_register_map_type(&htab_of_map_type); |
1222 | 1343 | return 0;
|
1223 | 1344 | }
|
1224 | 1345 | late_initcall(register_htab_map);
|
0 commit comments