18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* Copyright (c) 2020 Facebook */ 38c2ecf20Sopenharmony_ci#include <linux/bpf.h> 48c2ecf20Sopenharmony_ci#include <linux/fs.h> 58c2ecf20Sopenharmony_ci#include <linux/filter.h> 68c2ecf20Sopenharmony_ci#include <linux/kernel.h> 78c2ecf20Sopenharmony_ci#include <linux/btf_ids.h> 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_cistruct bpf_iter_seq_map_info { 108c2ecf20Sopenharmony_ci u32 map_id; 118c2ecf20Sopenharmony_ci}; 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_cistatic void *bpf_map_seq_start(struct seq_file *seq, loff_t *pos) 148c2ecf20Sopenharmony_ci{ 158c2ecf20Sopenharmony_ci struct bpf_iter_seq_map_info *info = seq->private; 168c2ecf20Sopenharmony_ci struct bpf_map *map; 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci map = bpf_map_get_curr_or_next(&info->map_id); 198c2ecf20Sopenharmony_ci if (!map) 208c2ecf20Sopenharmony_ci return NULL; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci if (*pos == 0) 238c2ecf20Sopenharmony_ci ++*pos; 248c2ecf20Sopenharmony_ci return map; 258c2ecf20Sopenharmony_ci} 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic void *bpf_map_seq_next(struct seq_file *seq, void *v, loff_t *pos) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci struct bpf_iter_seq_map_info *info = seq->private; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci ++*pos; 328c2ecf20Sopenharmony_ci ++info->map_id; 338c2ecf20Sopenharmony_ci bpf_map_put((struct bpf_map *)v); 348c2ecf20Sopenharmony_ci return bpf_map_get_curr_or_next(&info->map_id); 358c2ecf20Sopenharmony_ci} 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistruct bpf_iter__bpf_map { 388c2ecf20Sopenharmony_ci __bpf_md_ptr(struct bpf_iter_meta *, meta); 398c2ecf20Sopenharmony_ci __bpf_md_ptr(struct bpf_map *, map); 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ciDEFINE_BPF_ITER_FUNC(bpf_map, struct bpf_iter_meta *meta, struct bpf_map *map) 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic int __bpf_map_seq_show(struct seq_file *seq, void *v, bool in_stop) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci struct bpf_iter__bpf_map ctx; 478c2ecf20Sopenharmony_ci struct bpf_iter_meta meta; 488c2ecf20Sopenharmony_ci struct bpf_prog *prog; 498c2ecf20Sopenharmony_ci int ret = 0; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci ctx.meta = &meta; 528c2ecf20Sopenharmony_ci ctx.map = v; 538c2ecf20Sopenharmony_ci meta.seq = seq; 548c2ecf20Sopenharmony_ci prog = bpf_iter_get_info(&meta, in_stop); 558c2ecf20Sopenharmony_ci if (prog) 568c2ecf20Sopenharmony_ci ret = bpf_iter_run_prog(prog, &ctx); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci return ret; 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic int bpf_map_seq_show(struct seq_file *seq, void *v) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci return __bpf_map_seq_show(seq, v, false); 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic void bpf_map_seq_stop(struct seq_file *seq, void *v) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci if (!v) 698c2ecf20Sopenharmony_ci (void)__bpf_map_seq_show(seq, v, true); 708c2ecf20Sopenharmony_ci else 718c2ecf20Sopenharmony_ci bpf_map_put((struct bpf_map *)v); 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic const struct seq_operations bpf_map_seq_ops = { 758c2ecf20Sopenharmony_ci .start = bpf_map_seq_start, 768c2ecf20Sopenharmony_ci .next = bpf_map_seq_next, 778c2ecf20Sopenharmony_ci .stop = bpf_map_seq_stop, 788c2ecf20Sopenharmony_ci .show = bpf_map_seq_show, 798c2ecf20Sopenharmony_ci}; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ciBTF_ID_LIST(btf_bpf_map_id) 828c2ecf20Sopenharmony_ciBTF_ID(struct, bpf_map) 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic const struct bpf_iter_seq_info bpf_map_seq_info = { 858c2ecf20Sopenharmony_ci .seq_ops = &bpf_map_seq_ops, 868c2ecf20Sopenharmony_ci .init_seq_private = NULL, 878c2ecf20Sopenharmony_ci .fini_seq_private = NULL, 888c2ecf20Sopenharmony_ci .seq_priv_size = sizeof(struct bpf_iter_seq_map_info), 898c2ecf20Sopenharmony_ci}; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic struct bpf_iter_reg bpf_map_reg_info = { 928c2ecf20Sopenharmony_ci .target = "bpf_map", 938c2ecf20Sopenharmony_ci .ctx_arg_info_size = 1, 948c2ecf20Sopenharmony_ci .ctx_arg_info = { 958c2ecf20Sopenharmony_ci { offsetof(struct bpf_iter__bpf_map, map), 968c2ecf20Sopenharmony_ci PTR_TO_BTF_ID_OR_NULL }, 978c2ecf20Sopenharmony_ci }, 988c2ecf20Sopenharmony_ci .seq_info = &bpf_map_seq_info, 998c2ecf20Sopenharmony_ci}; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic int bpf_iter_attach_map(struct bpf_prog *prog, 1028c2ecf20Sopenharmony_ci union bpf_iter_link_info *linfo, 1038c2ecf20Sopenharmony_ci struct bpf_iter_aux_info *aux) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci u32 key_acc_size, value_acc_size, key_size, value_size; 1068c2ecf20Sopenharmony_ci struct bpf_map *map; 1078c2ecf20Sopenharmony_ci bool is_percpu = false; 1088c2ecf20Sopenharmony_ci int err = -EINVAL; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (!linfo->map.map_fd) 1118c2ecf20Sopenharmony_ci return -EBADF; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci map = bpf_map_get_with_uref(linfo->map.map_fd); 1148c2ecf20Sopenharmony_ci if (IS_ERR(map)) 1158c2ecf20Sopenharmony_ci return PTR_ERR(map); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci if (map->map_type == BPF_MAP_TYPE_PERCPU_HASH || 1188c2ecf20Sopenharmony_ci map->map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH || 1198c2ecf20Sopenharmony_ci map->map_type == BPF_MAP_TYPE_PERCPU_ARRAY) 1208c2ecf20Sopenharmony_ci is_percpu = true; 1218c2ecf20Sopenharmony_ci else if (map->map_type != BPF_MAP_TYPE_HASH && 1228c2ecf20Sopenharmony_ci map->map_type != BPF_MAP_TYPE_LRU_HASH && 1238c2ecf20Sopenharmony_ci map->map_type != BPF_MAP_TYPE_ARRAY) 1248c2ecf20Sopenharmony_ci goto put_map; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci key_acc_size = prog->aux->max_rdonly_access; 1278c2ecf20Sopenharmony_ci value_acc_size = prog->aux->max_rdwr_access; 1288c2ecf20Sopenharmony_ci key_size = map->key_size; 1298c2ecf20Sopenharmony_ci if (!is_percpu) 1308c2ecf20Sopenharmony_ci value_size = map->value_size; 1318c2ecf20Sopenharmony_ci else 1328c2ecf20Sopenharmony_ci value_size = round_up(map->value_size, 8) * num_possible_cpus(); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if (key_acc_size > key_size || value_acc_size > value_size) { 1358c2ecf20Sopenharmony_ci err = -EACCES; 1368c2ecf20Sopenharmony_ci goto put_map; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci aux->map = map; 1408c2ecf20Sopenharmony_ci return 0; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ciput_map: 1438c2ecf20Sopenharmony_ci bpf_map_put_with_uref(map); 1448c2ecf20Sopenharmony_ci return err; 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic void bpf_iter_detach_map(struct bpf_iter_aux_info *aux) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci bpf_map_put_with_uref(aux->map); 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_civoid bpf_iter_map_show_fdinfo(const struct bpf_iter_aux_info *aux, 1538c2ecf20Sopenharmony_ci struct seq_file *seq) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci seq_printf(seq, "map_id:\t%u\n", aux->map->id); 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ciint bpf_iter_map_fill_link_info(const struct bpf_iter_aux_info *aux, 1598c2ecf20Sopenharmony_ci struct bpf_link_info *info) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci info->iter.map.map_id = aux->map->id; 1628c2ecf20Sopenharmony_ci return 0; 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ciDEFINE_BPF_ITER_FUNC(bpf_map_elem, struct bpf_iter_meta *meta, 1668c2ecf20Sopenharmony_ci struct bpf_map *map, void *key, void *value) 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic const struct bpf_iter_reg bpf_map_elem_reg_info = { 1698c2ecf20Sopenharmony_ci .target = "bpf_map_elem", 1708c2ecf20Sopenharmony_ci .attach_target = bpf_iter_attach_map, 1718c2ecf20Sopenharmony_ci .detach_target = bpf_iter_detach_map, 1728c2ecf20Sopenharmony_ci .show_fdinfo = bpf_iter_map_show_fdinfo, 1738c2ecf20Sopenharmony_ci .fill_link_info = bpf_iter_map_fill_link_info, 1748c2ecf20Sopenharmony_ci .ctx_arg_info_size = 2, 1758c2ecf20Sopenharmony_ci .ctx_arg_info = { 1768c2ecf20Sopenharmony_ci { offsetof(struct bpf_iter__bpf_map_elem, key), 1778c2ecf20Sopenharmony_ci PTR_TO_BUF | PTR_MAYBE_NULL | MEM_RDONLY }, 1788c2ecf20Sopenharmony_ci { offsetof(struct bpf_iter__bpf_map_elem, value), 1798c2ecf20Sopenharmony_ci PTR_TO_BUF | PTR_MAYBE_NULL }, 1808c2ecf20Sopenharmony_ci }, 1818c2ecf20Sopenharmony_ci}; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic int __init bpf_map_iter_init(void) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci int ret; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci bpf_map_reg_info.ctx_arg_info[0].btf_id = *btf_bpf_map_id; 1888c2ecf20Sopenharmony_ci ret = bpf_iter_reg_target(&bpf_map_reg_info); 1898c2ecf20Sopenharmony_ci if (ret) 1908c2ecf20Sopenharmony_ci return ret; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci return bpf_iter_reg_target(&bpf_map_elem_reg_info); 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cilate_initcall(bpf_map_iter_init); 196