xref: /kernel/linux/linux-5.10/kernel/bpf/map_iter.c (revision 8c2ecf20)
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