165c93628SMartin KaFai Lau // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 265c93628SMartin KaFai Lau /* Copyright (C) 2020 Facebook */ 365c93628SMartin KaFai Lau 465c93628SMartin KaFai Lau #include <errno.h> 565c93628SMartin KaFai Lau #include <stdio.h> 665c93628SMartin KaFai Lau #include <unistd.h> 765c93628SMartin KaFai Lau 865c93628SMartin KaFai Lau #include <linux/err.h> 965c93628SMartin KaFai Lau 1065c93628SMartin KaFai Lau #include <bpf/bpf.h> 1165c93628SMartin KaFai Lau #include <bpf/btf.h> 1265c93628SMartin KaFai Lau #include <bpf/libbpf.h> 1365c93628SMartin KaFai Lau 1465c93628SMartin KaFai Lau #include "json_writer.h" 1565c93628SMartin KaFai Lau #include "main.h" 1665c93628SMartin KaFai Lau 1765c93628SMartin KaFai Lau #define STRUCT_OPS_VALUE_PREFIX "bpf_struct_ops_" 1865c93628SMartin KaFai Lau 1965c93628SMartin KaFai Lau static const struct btf_type *map_info_type; 2065c93628SMartin KaFai Lau static __u32 map_info_alloc_len; 2165c93628SMartin KaFai Lau static struct btf *btf_vmlinux; 2265c93628SMartin KaFai Lau static __s32 map_info_type_id; 2365c93628SMartin KaFai Lau 2465c93628SMartin KaFai Lau struct res { 2565c93628SMartin KaFai Lau unsigned int nr_maps; 2665c93628SMartin KaFai Lau unsigned int nr_errs; 2765c93628SMartin KaFai Lau }; 2865c93628SMartin KaFai Lau 2965c93628SMartin KaFai Lau static const struct btf *get_btf_vmlinux(void) 3065c93628SMartin KaFai Lau { 3165c93628SMartin KaFai Lau if (btf_vmlinux) 3265c93628SMartin KaFai Lau return btf_vmlinux; 3365c93628SMartin KaFai Lau 3465c93628SMartin KaFai Lau btf_vmlinux = libbpf_find_kernel_btf(); 35d1313e01SSahid Orentino Ferdjaoui if (!btf_vmlinux) 3665c93628SMartin KaFai Lau p_err("struct_ops requires kernel CONFIG_DEBUG_INFO_BTF=y"); 3765c93628SMartin KaFai Lau 3865c93628SMartin KaFai Lau return btf_vmlinux; 3965c93628SMartin KaFai Lau } 4065c93628SMartin KaFai Lau 4165c93628SMartin KaFai Lau static const char *get_kern_struct_ops_name(const struct bpf_map_info *info) 4265c93628SMartin KaFai Lau { 4365c93628SMartin KaFai Lau const struct btf *kern_btf; 4465c93628SMartin KaFai Lau const struct btf_type *t; 4565c93628SMartin KaFai Lau const char *st_ops_name; 4665c93628SMartin KaFai Lau 4765c93628SMartin KaFai Lau kern_btf = get_btf_vmlinux(); 48d1313e01SSahid Orentino Ferdjaoui if (!kern_btf) 4965c93628SMartin KaFai Lau return "<btf_vmlinux_not_found>"; 5065c93628SMartin KaFai Lau 5165c93628SMartin KaFai Lau t = btf__type_by_id(kern_btf, info->btf_vmlinux_value_type_id); 5265c93628SMartin KaFai Lau st_ops_name = btf__name_by_offset(kern_btf, t->name_off); 5365c93628SMartin KaFai Lau st_ops_name += strlen(STRUCT_OPS_VALUE_PREFIX); 5465c93628SMartin KaFai Lau 5565c93628SMartin KaFai Lau return st_ops_name; 5665c93628SMartin KaFai Lau } 5765c93628SMartin KaFai Lau 5865c93628SMartin KaFai Lau static __s32 get_map_info_type_id(void) 5965c93628SMartin KaFai Lau { 6065c93628SMartin KaFai Lau const struct btf *kern_btf; 6165c93628SMartin KaFai Lau 6265c93628SMartin KaFai Lau if (map_info_type_id) 6365c93628SMartin KaFai Lau return map_info_type_id; 6465c93628SMartin KaFai Lau 6565c93628SMartin KaFai Lau kern_btf = get_btf_vmlinux(); 66989f2851SSahid Orentino Ferdjaoui if (!kern_btf) 67989f2851SSahid Orentino Ferdjaoui return 0; 6865c93628SMartin KaFai Lau 6965c93628SMartin KaFai Lau map_info_type_id = btf__find_by_name_kind(kern_btf, "bpf_map_info", 7065c93628SMartin KaFai Lau BTF_KIND_STRUCT); 7165c93628SMartin KaFai Lau if (map_info_type_id < 0) { 7265c93628SMartin KaFai Lau p_err("can't find bpf_map_info from btf_vmlinux"); 7365c93628SMartin KaFai Lau return map_info_type_id; 7465c93628SMartin KaFai Lau } 7565c93628SMartin KaFai Lau map_info_type = btf__type_by_id(kern_btf, map_info_type_id); 7665c93628SMartin KaFai Lau 7765c93628SMartin KaFai Lau /* Ensure map_info_alloc() has at least what the bpftool needs */ 7865c93628SMartin KaFai Lau map_info_alloc_len = map_info_type->size; 7965c93628SMartin KaFai Lau if (map_info_alloc_len < sizeof(struct bpf_map_info)) 8065c93628SMartin KaFai Lau map_info_alloc_len = sizeof(struct bpf_map_info); 8165c93628SMartin KaFai Lau 8265c93628SMartin KaFai Lau return map_info_type_id; 8365c93628SMartin KaFai Lau } 8465c93628SMartin KaFai Lau 8565c93628SMartin KaFai Lau /* If the subcmd needs to print out the bpf_map_info, 8665c93628SMartin KaFai Lau * it should always call map_info_alloc to allocate 8765c93628SMartin KaFai Lau * a bpf_map_info object instead of allocating it 8865c93628SMartin KaFai Lau * on the stack. 8965c93628SMartin KaFai Lau * 9065c93628SMartin KaFai Lau * map_info_alloc() will take the running kernel's btf 9165c93628SMartin KaFai Lau * into account. i.e. it will consider the 9265c93628SMartin KaFai Lau * sizeof(struct bpf_map_info) of the running kernel. 9365c93628SMartin KaFai Lau * 9465c93628SMartin KaFai Lau * It will enable the "struct_ops" cmd to print the latest 9565c93628SMartin KaFai Lau * "struct bpf_map_info". 9665c93628SMartin KaFai Lau * 9765c93628SMartin KaFai Lau * [ Recall that "struct_ops" requires the kernel's btf to 9865c93628SMartin KaFai Lau * be available ] 9965c93628SMartin KaFai Lau */ 10065c93628SMartin KaFai Lau static struct bpf_map_info *map_info_alloc(__u32 *alloc_len) 10165c93628SMartin KaFai Lau { 10265c93628SMartin KaFai Lau struct bpf_map_info *info; 10365c93628SMartin KaFai Lau 10465c93628SMartin KaFai Lau if (get_map_info_type_id() < 0) 10565c93628SMartin KaFai Lau return NULL; 10665c93628SMartin KaFai Lau 10765c93628SMartin KaFai Lau info = calloc(1, map_info_alloc_len); 10865c93628SMartin KaFai Lau if (!info) 10965c93628SMartin KaFai Lau p_err("mem alloc failed"); 11065c93628SMartin KaFai Lau else 11165c93628SMartin KaFai Lau *alloc_len = map_info_alloc_len; 11265c93628SMartin KaFai Lau 11365c93628SMartin KaFai Lau return info; 11465c93628SMartin KaFai Lau } 11565c93628SMartin KaFai Lau 11665c93628SMartin KaFai Lau /* It iterates all struct_ops maps of the system. 11765c93628SMartin KaFai Lau * It returns the fd in "*res_fd" and map_info in "*info". 11865c93628SMartin KaFai Lau * In the very first iteration, info->id should be 0. 11965c93628SMartin KaFai Lau * An optional map "*name" filter can be specified. 12065c93628SMartin KaFai Lau * The filter can be made more flexible in the future. 12165c93628SMartin KaFai Lau * e.g. filter by kernel-struct-ops-name, regex-name, glob-name, ...etc. 12265c93628SMartin KaFai Lau * 12365c93628SMartin KaFai Lau * Return value: 12465c93628SMartin KaFai Lau * 1: A struct_ops map found. It is returned in "*res_fd" and "*info". 12565c93628SMartin KaFai Lau * The caller can continue to call get_next in the future. 12665c93628SMartin KaFai Lau * 0: No struct_ops map is returned. 12765c93628SMartin KaFai Lau * All struct_ops map has been found. 12865c93628SMartin KaFai Lau * -1: Error and the caller should abort the iteration. 12965c93628SMartin KaFai Lau */ 13065c93628SMartin KaFai Lau static int get_next_struct_ops_map(const char *name, int *res_fd, 13165c93628SMartin KaFai Lau struct bpf_map_info *info, __u32 info_len) 13265c93628SMartin KaFai Lau { 13365c93628SMartin KaFai Lau __u32 id = info->id; 13465c93628SMartin KaFai Lau int err, fd; 13565c93628SMartin KaFai Lau 13665c93628SMartin KaFai Lau while (true) { 13765c93628SMartin KaFai Lau err = bpf_map_get_next_id(id, &id); 13865c93628SMartin KaFai Lau if (err) { 13965c93628SMartin KaFai Lau if (errno == ENOENT) 14065c93628SMartin KaFai Lau return 0; 14165c93628SMartin KaFai Lau p_err("can't get next map: %s", strerror(errno)); 14265c93628SMartin KaFai Lau return -1; 14365c93628SMartin KaFai Lau } 14465c93628SMartin KaFai Lau 14565c93628SMartin KaFai Lau fd = bpf_map_get_fd_by_id(id); 14665c93628SMartin KaFai Lau if (fd < 0) { 14765c93628SMartin KaFai Lau if (errno == ENOENT) 14865c93628SMartin KaFai Lau continue; 14965c93628SMartin KaFai Lau p_err("can't get map by id (%u): %s", 15065c93628SMartin KaFai Lau id, strerror(errno)); 15165c93628SMartin KaFai Lau return -1; 15265c93628SMartin KaFai Lau } 15365c93628SMartin KaFai Lau 15438f0408eSIlya Leoshkevich err = bpf_map_get_info_by_fd(fd, info, &info_len); 15565c93628SMartin KaFai Lau if (err) { 15665c93628SMartin KaFai Lau p_err("can't get map info: %s", strerror(errno)); 15765c93628SMartin KaFai Lau close(fd); 15865c93628SMartin KaFai Lau return -1; 15965c93628SMartin KaFai Lau } 16065c93628SMartin KaFai Lau 16165c93628SMartin KaFai Lau if (info->type == BPF_MAP_TYPE_STRUCT_OPS && 16265c93628SMartin KaFai Lau (!name || !strcmp(name, info->name))) { 16365c93628SMartin KaFai Lau *res_fd = fd; 16465c93628SMartin KaFai Lau return 1; 16565c93628SMartin KaFai Lau } 16665c93628SMartin KaFai Lau close(fd); 16765c93628SMartin KaFai Lau } 16865c93628SMartin KaFai Lau } 16965c93628SMartin KaFai Lau 17065c93628SMartin KaFai Lau static int cmd_retval(const struct res *res, bool must_have_one_map) 17165c93628SMartin KaFai Lau { 17265c93628SMartin KaFai Lau if (res->nr_errs || (!res->nr_maps && must_have_one_map)) 17365c93628SMartin KaFai Lau return -1; 17465c93628SMartin KaFai Lau 17565c93628SMartin KaFai Lau return 0; 17665c93628SMartin KaFai Lau } 17765c93628SMartin KaFai Lau 17865c93628SMartin KaFai Lau /* "data" is the work_func private storage */ 17965c93628SMartin KaFai Lau typedef int (*work_func)(int fd, const struct bpf_map_info *info, void *data, 18065c93628SMartin KaFai Lau struct json_writer *wtr); 18165c93628SMartin KaFai Lau 18265c93628SMartin KaFai Lau /* Find all struct_ops map in the system. 18365c93628SMartin KaFai Lau * Filter out by "name" (if specified). 18465c93628SMartin KaFai Lau * Then call "func(fd, info, data, wtr)" on each struct_ops map found. 18565c93628SMartin KaFai Lau */ 18665c93628SMartin KaFai Lau static struct res do_search(const char *name, work_func func, void *data, 18765c93628SMartin KaFai Lau struct json_writer *wtr) 18865c93628SMartin KaFai Lau { 18965c93628SMartin KaFai Lau struct bpf_map_info *info; 19065c93628SMartin KaFai Lau struct res res = {}; 19165c93628SMartin KaFai Lau __u32 info_len; 19265c93628SMartin KaFai Lau int fd, err; 19365c93628SMartin KaFai Lau 19465c93628SMartin KaFai Lau info = map_info_alloc(&info_len); 19565c93628SMartin KaFai Lau if (!info) { 19665c93628SMartin KaFai Lau res.nr_errs++; 19765c93628SMartin KaFai Lau return res; 19865c93628SMartin KaFai Lau } 19965c93628SMartin KaFai Lau 20065c93628SMartin KaFai Lau if (wtr) 20165c93628SMartin KaFai Lau jsonw_start_array(wtr); 20265c93628SMartin KaFai Lau while ((err = get_next_struct_ops_map(name, &fd, info, info_len)) == 1) { 20365c93628SMartin KaFai Lau res.nr_maps++; 20465c93628SMartin KaFai Lau err = func(fd, info, data, wtr); 20565c93628SMartin KaFai Lau if (err) 20665c93628SMartin KaFai Lau res.nr_errs++; 20765c93628SMartin KaFai Lau close(fd); 20865c93628SMartin KaFai Lau } 20965c93628SMartin KaFai Lau if (wtr) 21065c93628SMartin KaFai Lau jsonw_end_array(wtr); 21165c93628SMartin KaFai Lau 21265c93628SMartin KaFai Lau if (err) 21365c93628SMartin KaFai Lau res.nr_errs++; 21465c93628SMartin KaFai Lau 21565c93628SMartin KaFai Lau if (!wtr && name && !res.nr_errs && !res.nr_maps) 21665c93628SMartin KaFai Lau /* It is not printing empty []. 21765c93628SMartin KaFai Lau * Thus, needs to specifically say nothing found 21865c93628SMartin KaFai Lau * for "name" here. 21965c93628SMartin KaFai Lau */ 22065c93628SMartin KaFai Lau p_err("no struct_ops found for %s", name); 22165c93628SMartin KaFai Lau else if (!wtr && json_output && !res.nr_errs) 22265c93628SMartin KaFai Lau /* The "func()" above is not writing any json (i.e. !wtr 22365c93628SMartin KaFai Lau * test here). 22465c93628SMartin KaFai Lau * 22565c93628SMartin KaFai Lau * However, "-j" is enabled and there is no errs here, 22665c93628SMartin KaFai Lau * so call json_null() as the current convention of 22765c93628SMartin KaFai Lau * other cmds. 22865c93628SMartin KaFai Lau */ 22965c93628SMartin KaFai Lau jsonw_null(json_wtr); 23065c93628SMartin KaFai Lau 23165c93628SMartin KaFai Lau free(info); 23265c93628SMartin KaFai Lau return res; 23365c93628SMartin KaFai Lau } 23465c93628SMartin KaFai Lau 23565c93628SMartin KaFai Lau static struct res do_one_id(const char *id_str, work_func func, void *data, 23665c93628SMartin KaFai Lau struct json_writer *wtr) 23765c93628SMartin KaFai Lau { 23865c93628SMartin KaFai Lau struct bpf_map_info *info; 23965c93628SMartin KaFai Lau struct res res = {}; 24065c93628SMartin KaFai Lau unsigned long id; 24165c93628SMartin KaFai Lau __u32 info_len; 24265c93628SMartin KaFai Lau char *endptr; 24365c93628SMartin KaFai Lau int fd; 24465c93628SMartin KaFai Lau 24565c93628SMartin KaFai Lau id = strtoul(id_str, &endptr, 0); 24665c93628SMartin KaFai Lau if (*endptr || !id || id > UINT32_MAX) { 24765c93628SMartin KaFai Lau p_err("invalid id %s", id_str); 24865c93628SMartin KaFai Lau res.nr_errs++; 24965c93628SMartin KaFai Lau return res; 25065c93628SMartin KaFai Lau } 25165c93628SMartin KaFai Lau 25265c93628SMartin KaFai Lau fd = bpf_map_get_fd_by_id(id); 25360f27075SDave Marchevsky if (fd < 0) { 25465c93628SMartin KaFai Lau p_err("can't get map by id (%lu): %s", id, strerror(errno)); 25565c93628SMartin KaFai Lau res.nr_errs++; 25665c93628SMartin KaFai Lau return res; 25765c93628SMartin KaFai Lau } 25865c93628SMartin KaFai Lau 25965c93628SMartin KaFai Lau info = map_info_alloc(&info_len); 26065c93628SMartin KaFai Lau if (!info) { 26165c93628SMartin KaFai Lau res.nr_errs++; 26265c93628SMartin KaFai Lau goto done; 26365c93628SMartin KaFai Lau } 26465c93628SMartin KaFai Lau 26538f0408eSIlya Leoshkevich if (bpf_map_get_info_by_fd(fd, info, &info_len)) { 26665c93628SMartin KaFai Lau p_err("can't get map info: %s", strerror(errno)); 26765c93628SMartin KaFai Lau res.nr_errs++; 26865c93628SMartin KaFai Lau goto done; 26965c93628SMartin KaFai Lau } 27065c93628SMartin KaFai Lau 27165c93628SMartin KaFai Lau if (info->type != BPF_MAP_TYPE_STRUCT_OPS) { 27265c93628SMartin KaFai Lau p_err("%s id %u is not a struct_ops map", info->name, info->id); 27365c93628SMartin KaFai Lau res.nr_errs++; 27465c93628SMartin KaFai Lau goto done; 27565c93628SMartin KaFai Lau } 27665c93628SMartin KaFai Lau 27765c93628SMartin KaFai Lau res.nr_maps++; 27865c93628SMartin KaFai Lau 27965c93628SMartin KaFai Lau if (func(fd, info, data, wtr)) 28065c93628SMartin KaFai Lau res.nr_errs++; 28165c93628SMartin KaFai Lau else if (!wtr && json_output) 28265c93628SMartin KaFai Lau /* The "func()" above is not writing any json (i.e. !wtr 28365c93628SMartin KaFai Lau * test here). 28465c93628SMartin KaFai Lau * 28565c93628SMartin KaFai Lau * However, "-j" is enabled and there is no errs here, 28665c93628SMartin KaFai Lau * so call json_null() as the current convention of 28765c93628SMartin KaFai Lau * other cmds. 28865c93628SMartin KaFai Lau */ 28965c93628SMartin KaFai Lau jsonw_null(json_wtr); 29065c93628SMartin KaFai Lau 29165c93628SMartin KaFai Lau done: 29265c93628SMartin KaFai Lau free(info); 29365c93628SMartin KaFai Lau close(fd); 29465c93628SMartin KaFai Lau 29565c93628SMartin KaFai Lau return res; 29665c93628SMartin KaFai Lau } 29765c93628SMartin KaFai Lau 29865c93628SMartin KaFai Lau static struct res do_work_on_struct_ops(const char *search_type, 29965c93628SMartin KaFai Lau const char *search_term, 30065c93628SMartin KaFai Lau work_func func, void *data, 30165c93628SMartin KaFai Lau struct json_writer *wtr) 30265c93628SMartin KaFai Lau { 30365c93628SMartin KaFai Lau if (search_type) { 30465c93628SMartin KaFai Lau if (is_prefix(search_type, "id")) 30565c93628SMartin KaFai Lau return do_one_id(search_term, func, data, wtr); 30665c93628SMartin KaFai Lau else if (!is_prefix(search_type, "name")) 30765c93628SMartin KaFai Lau usage(); 30865c93628SMartin KaFai Lau } 30965c93628SMartin KaFai Lau 31065c93628SMartin KaFai Lau return do_search(search_term, func, data, wtr); 31165c93628SMartin KaFai Lau } 31265c93628SMartin KaFai Lau 31365c93628SMartin KaFai Lau static int __do_show(int fd, const struct bpf_map_info *info, void *data, 31465c93628SMartin KaFai Lau struct json_writer *wtr) 31565c93628SMartin KaFai Lau { 31665c93628SMartin KaFai Lau if (wtr) { 31765c93628SMartin KaFai Lau jsonw_start_object(wtr); 31865c93628SMartin KaFai Lau jsonw_uint_field(wtr, "id", info->id); 31965c93628SMartin KaFai Lau jsonw_string_field(wtr, "name", info->name); 32065c93628SMartin KaFai Lau jsonw_string_field(wtr, "kernel_struct_ops", 32165c93628SMartin KaFai Lau get_kern_struct_ops_name(info)); 32265c93628SMartin KaFai Lau jsonw_end_object(wtr); 32365c93628SMartin KaFai Lau } else { 32465c93628SMartin KaFai Lau printf("%u: %-15s %-32s\n", info->id, info->name, 32565c93628SMartin KaFai Lau get_kern_struct_ops_name(info)); 32665c93628SMartin KaFai Lau } 32765c93628SMartin KaFai Lau 32865c93628SMartin KaFai Lau return 0; 32965c93628SMartin KaFai Lau } 33065c93628SMartin KaFai Lau 33165c93628SMartin KaFai Lau static int do_show(int argc, char **argv) 33265c93628SMartin KaFai Lau { 33365c93628SMartin KaFai Lau const char *search_type = NULL, *search_term = NULL; 33465c93628SMartin KaFai Lau struct res res; 33565c93628SMartin KaFai Lau 33665c93628SMartin KaFai Lau if (argc && argc != 2) 33765c93628SMartin KaFai Lau usage(); 33865c93628SMartin KaFai Lau 33965c93628SMartin KaFai Lau if (argc == 2) { 34065c93628SMartin KaFai Lau search_type = GET_ARG(); 34165c93628SMartin KaFai Lau search_term = GET_ARG(); 34265c93628SMartin KaFai Lau } 34365c93628SMartin KaFai Lau 34465c93628SMartin KaFai Lau res = do_work_on_struct_ops(search_type, search_term, __do_show, 34565c93628SMartin KaFai Lau NULL, json_wtr); 34665c93628SMartin KaFai Lau 34765c93628SMartin KaFai Lau return cmd_retval(&res, !!search_term); 34865c93628SMartin KaFai Lau } 34965c93628SMartin KaFai Lau 35065c93628SMartin KaFai Lau static int __do_dump(int fd, const struct bpf_map_info *info, void *data, 35165c93628SMartin KaFai Lau struct json_writer *wtr) 35265c93628SMartin KaFai Lau { 35365c93628SMartin KaFai Lau struct btf_dumper *d = (struct btf_dumper *)data; 35465c93628SMartin KaFai Lau const struct btf_type *struct_ops_type; 35565c93628SMartin KaFai Lau const struct btf *kern_btf = d->btf; 35665c93628SMartin KaFai Lau const char *struct_ops_name; 35765c93628SMartin KaFai Lau int zero = 0; 35865c93628SMartin KaFai Lau void *value; 35965c93628SMartin KaFai Lau 36065c93628SMartin KaFai Lau /* note: d->jw == wtr */ 36165c93628SMartin KaFai Lau 36265c93628SMartin KaFai Lau kern_btf = d->btf; 36365c93628SMartin KaFai Lau 36465c93628SMartin KaFai Lau /* The kernel supporting BPF_MAP_TYPE_STRUCT_OPS must have 36565c93628SMartin KaFai Lau * btf_vmlinux_value_type_id. 36665c93628SMartin KaFai Lau */ 36765c93628SMartin KaFai Lau struct_ops_type = btf__type_by_id(kern_btf, 36865c93628SMartin KaFai Lau info->btf_vmlinux_value_type_id); 36965c93628SMartin KaFai Lau struct_ops_name = btf__name_by_offset(kern_btf, 37065c93628SMartin KaFai Lau struct_ops_type->name_off); 37165c93628SMartin KaFai Lau value = calloc(1, info->value_size); 37265c93628SMartin KaFai Lau if (!value) { 37365c93628SMartin KaFai Lau p_err("mem alloc failed"); 37465c93628SMartin KaFai Lau return -1; 37565c93628SMartin KaFai Lau } 37665c93628SMartin KaFai Lau 37765c93628SMartin KaFai Lau if (bpf_map_lookup_elem(fd, &zero, value)) { 37865c93628SMartin KaFai Lau p_err("can't lookup struct_ops map %s id %u", 37965c93628SMartin KaFai Lau info->name, info->id); 38065c93628SMartin KaFai Lau free(value); 38165c93628SMartin KaFai Lau return -1; 38265c93628SMartin KaFai Lau } 38365c93628SMartin KaFai Lau 38465c93628SMartin KaFai Lau jsonw_start_object(wtr); 38565c93628SMartin KaFai Lau jsonw_name(wtr, "bpf_map_info"); 38665c93628SMartin KaFai Lau btf_dumper_type(d, map_info_type_id, (void *)info); 38765c93628SMartin KaFai Lau jsonw_end_object(wtr); 38865c93628SMartin KaFai Lau 38965c93628SMartin KaFai Lau jsonw_start_object(wtr); 39065c93628SMartin KaFai Lau jsonw_name(wtr, struct_ops_name); 39165c93628SMartin KaFai Lau btf_dumper_type(d, info->btf_vmlinux_value_type_id, value); 39265c93628SMartin KaFai Lau jsonw_end_object(wtr); 39365c93628SMartin KaFai Lau 39465c93628SMartin KaFai Lau free(value); 39565c93628SMartin KaFai Lau 39665c93628SMartin KaFai Lau return 0; 39765c93628SMartin KaFai Lau } 39865c93628SMartin KaFai Lau 39965c93628SMartin KaFai Lau static int do_dump(int argc, char **argv) 40065c93628SMartin KaFai Lau { 40165c93628SMartin KaFai Lau const char *search_type = NULL, *search_term = NULL; 40265c93628SMartin KaFai Lau json_writer_t *wtr = json_wtr; 40365c93628SMartin KaFai Lau const struct btf *kern_btf; 40465c93628SMartin KaFai Lau struct btf_dumper d = {}; 40565c93628SMartin KaFai Lau struct res res; 40665c93628SMartin KaFai Lau 40765c93628SMartin KaFai Lau if (argc && argc != 2) 40865c93628SMartin KaFai Lau usage(); 40965c93628SMartin KaFai Lau 41065c93628SMartin KaFai Lau if (argc == 2) { 41165c93628SMartin KaFai Lau search_type = GET_ARG(); 41265c93628SMartin KaFai Lau search_term = GET_ARG(); 41365c93628SMartin KaFai Lau } 41465c93628SMartin KaFai Lau 41565c93628SMartin KaFai Lau kern_btf = get_btf_vmlinux(); 416d1313e01SSahid Orentino Ferdjaoui if (!kern_btf) 41765c93628SMartin KaFai Lau return -1; 41865c93628SMartin KaFai Lau 41965c93628SMartin KaFai Lau if (!json_output) { 42065c93628SMartin KaFai Lau wtr = jsonw_new(stdout); 42165c93628SMartin KaFai Lau if (!wtr) { 42265c93628SMartin KaFai Lau p_err("can't create json writer"); 42365c93628SMartin KaFai Lau return -1; 42465c93628SMartin KaFai Lau } 42565c93628SMartin KaFai Lau jsonw_pretty(wtr, true); 42665c93628SMartin KaFai Lau } 42765c93628SMartin KaFai Lau 42865c93628SMartin KaFai Lau d.btf = kern_btf; 42965c93628SMartin KaFai Lau d.jw = wtr; 43065c93628SMartin KaFai Lau d.is_plain_text = !json_output; 43165c93628SMartin KaFai Lau d.prog_id_as_func_ptr = true; 43265c93628SMartin KaFai Lau 43365c93628SMartin KaFai Lau res = do_work_on_struct_ops(search_type, search_term, __do_dump, &d, 43465c93628SMartin KaFai Lau wtr); 43565c93628SMartin KaFai Lau 43665c93628SMartin KaFai Lau if (!json_output) 43765c93628SMartin KaFai Lau jsonw_destroy(&wtr); 43865c93628SMartin KaFai Lau 43965c93628SMartin KaFai Lau return cmd_retval(&res, !!search_term); 44065c93628SMartin KaFai Lau } 44165c93628SMartin KaFai Lau 44265c93628SMartin KaFai Lau static int __do_unregister(int fd, const struct bpf_map_info *info, void *data, 44365c93628SMartin KaFai Lau struct json_writer *wtr) 44465c93628SMartin KaFai Lau { 44565c93628SMartin KaFai Lau int zero = 0; 44665c93628SMartin KaFai Lau 44765c93628SMartin KaFai Lau if (bpf_map_delete_elem(fd, &zero)) { 44865c93628SMartin KaFai Lau p_err("can't unload %s %s id %u: %s", 44965c93628SMartin KaFai Lau get_kern_struct_ops_name(info), info->name, 45065c93628SMartin KaFai Lau info->id, strerror(errno)); 45165c93628SMartin KaFai Lau return -1; 45265c93628SMartin KaFai Lau } 45365c93628SMartin KaFai Lau 45465c93628SMartin KaFai Lau p_info("Unregistered %s %s id %u", 45565c93628SMartin KaFai Lau get_kern_struct_ops_name(info), info->name, 45665c93628SMartin KaFai Lau info->id); 45765c93628SMartin KaFai Lau 45865c93628SMartin KaFai Lau return 0; 45965c93628SMartin KaFai Lau } 46065c93628SMartin KaFai Lau 46165c93628SMartin KaFai Lau static int do_unregister(int argc, char **argv) 46265c93628SMartin KaFai Lau { 46365c93628SMartin KaFai Lau const char *search_type, *search_term; 46465c93628SMartin KaFai Lau struct res res; 46565c93628SMartin KaFai Lau 46665c93628SMartin KaFai Lau if (argc != 2) 46765c93628SMartin KaFai Lau usage(); 46865c93628SMartin KaFai Lau 46965c93628SMartin KaFai Lau search_type = GET_ARG(); 47065c93628SMartin KaFai Lau search_term = GET_ARG(); 47165c93628SMartin KaFai Lau 47265c93628SMartin KaFai Lau res = do_work_on_struct_ops(search_type, search_term, 47365c93628SMartin KaFai Lau __do_unregister, NULL, NULL); 47465c93628SMartin KaFai Lau 47565c93628SMartin KaFai Lau return cmd_retval(&res, true); 47665c93628SMartin KaFai Lau } 47765c93628SMartin KaFai Lau 478*0232b788SKui-Feng Lee static int pin_link(struct bpf_link *link, const char *pindir, 479*0232b788SKui-Feng Lee const char *name) 480*0232b788SKui-Feng Lee { 481*0232b788SKui-Feng Lee char pinfile[PATH_MAX]; 482*0232b788SKui-Feng Lee int err; 483*0232b788SKui-Feng Lee 484*0232b788SKui-Feng Lee err = pathname_concat(pinfile, sizeof(pinfile), pindir, name); 485*0232b788SKui-Feng Lee if (err) 486*0232b788SKui-Feng Lee return -1; 487*0232b788SKui-Feng Lee 488*0232b788SKui-Feng Lee return bpf_link__pin(link, pinfile); 489*0232b788SKui-Feng Lee } 490*0232b788SKui-Feng Lee 49165c93628SMartin KaFai Lau static int do_register(int argc, char **argv) 49265c93628SMartin KaFai Lau { 493b59e4ce8SAndrii Nakryiko LIBBPF_OPTS(bpf_object_open_opts, open_opts); 494*0232b788SKui-Feng Lee __u32 link_info_len = sizeof(struct bpf_link_info); 495*0232b788SKui-Feng Lee struct bpf_link_info link_info = {}; 49665c93628SMartin KaFai Lau struct bpf_map_info info = {}; 49765c93628SMartin KaFai Lau __u32 info_len = sizeof(info); 49865c93628SMartin KaFai Lau int nr_errs = 0, nr_maps = 0; 499*0232b788SKui-Feng Lee const char *linkdir = NULL; 50065c93628SMartin KaFai Lau struct bpf_object *obj; 50165c93628SMartin KaFai Lau struct bpf_link *link; 50265c93628SMartin KaFai Lau struct bpf_map *map; 50365c93628SMartin KaFai Lau const char *file; 50465c93628SMartin KaFai Lau 505*0232b788SKui-Feng Lee if (argc != 1 && argc != 2) 50665c93628SMartin KaFai Lau usage(); 50765c93628SMartin KaFai Lau 50865c93628SMartin KaFai Lau file = GET_ARG(); 509*0232b788SKui-Feng Lee if (argc == 1) 510*0232b788SKui-Feng Lee linkdir = GET_ARG(); 511*0232b788SKui-Feng Lee 512*0232b788SKui-Feng Lee if (linkdir && mount_bpffs_for_pin(linkdir)) { 513*0232b788SKui-Feng Lee p_err("can't mount bpffs for pinning"); 514*0232b788SKui-Feng Lee return -1; 515*0232b788SKui-Feng Lee } 51665c93628SMartin KaFai Lau 517b59e4ce8SAndrii Nakryiko if (verifier_logs) 518b59e4ce8SAndrii Nakryiko /* log_level1 + log_level2 + stats, but not stable UAPI */ 519b59e4ce8SAndrii Nakryiko open_opts.kernel_log_level = 1 + 2 + 4; 520b59e4ce8SAndrii Nakryiko 521b59e4ce8SAndrii Nakryiko obj = bpf_object__open_file(file, &open_opts); 522d1313e01SSahid Orentino Ferdjaoui if (!obj) 52365c93628SMartin KaFai Lau return -1; 52465c93628SMartin KaFai Lau 5256b4384ffSQuentin Monnet set_max_rlimit(); 5266b4384ffSQuentin Monnet 527b59e4ce8SAndrii Nakryiko if (bpf_object__load(obj)) { 52865c93628SMartin KaFai Lau bpf_object__close(obj); 52965c93628SMartin KaFai Lau return -1; 53065c93628SMartin KaFai Lau } 53165c93628SMartin KaFai Lau 53265c93628SMartin KaFai Lau bpf_object__for_each_map(map, obj) { 5333c28919fSChristy Lee if (bpf_map__type(map) != BPF_MAP_TYPE_STRUCT_OPS) 53465c93628SMartin KaFai Lau continue; 53565c93628SMartin KaFai Lau 53665c93628SMartin KaFai Lau link = bpf_map__attach_struct_ops(map); 537d2973ffdSSahid Orentino Ferdjaoui if (!link) { 53865c93628SMartin KaFai Lau p_err("can't register struct_ops %s: %s", 539d2973ffdSSahid Orentino Ferdjaoui bpf_map__name(map), strerror(errno)); 54065c93628SMartin KaFai Lau nr_errs++; 54165c93628SMartin KaFai Lau continue; 54265c93628SMartin KaFai Lau } 54365c93628SMartin KaFai Lau nr_maps++; 54465c93628SMartin KaFai Lau 545*0232b788SKui-Feng Lee if (bpf_map_get_info_by_fd(bpf_map__fd(map), &info, 546*0232b788SKui-Feng Lee &info_len)) { 54765c93628SMartin KaFai Lau /* Not p_err. The struct_ops was attached 54865c93628SMartin KaFai Lau * successfully. 54965c93628SMartin KaFai Lau */ 55065c93628SMartin KaFai Lau p_info("Registered %s but can't find id: %s", 55165c93628SMartin KaFai Lau bpf_map__name(map), strerror(errno)); 552*0232b788SKui-Feng Lee goto clean_link; 553*0232b788SKui-Feng Lee } 554*0232b788SKui-Feng Lee if (!(bpf_map__map_flags(map) & BPF_F_LINK)) { 555*0232b788SKui-Feng Lee p_info("Registered %s %s id %u", 556*0232b788SKui-Feng Lee get_kern_struct_ops_name(&info), 557*0232b788SKui-Feng Lee info.name, 558*0232b788SKui-Feng Lee info.id); 559*0232b788SKui-Feng Lee goto clean_link; 560*0232b788SKui-Feng Lee } 561*0232b788SKui-Feng Lee if (bpf_link_get_info_by_fd(bpf_link__fd(link), 562*0232b788SKui-Feng Lee &link_info, 563*0232b788SKui-Feng Lee &link_info_len)) { 564*0232b788SKui-Feng Lee p_err("Registered %s but can't find link id: %s", 565*0232b788SKui-Feng Lee bpf_map__name(map), strerror(errno)); 566*0232b788SKui-Feng Lee nr_errs++; 567*0232b788SKui-Feng Lee goto clean_link; 568*0232b788SKui-Feng Lee } 569*0232b788SKui-Feng Lee if (linkdir && pin_link(link, linkdir, info.name)) { 570*0232b788SKui-Feng Lee p_err("can't pin link %u for %s: %s", 571*0232b788SKui-Feng Lee link_info.id, info.name, 572*0232b788SKui-Feng Lee strerror(errno)); 573*0232b788SKui-Feng Lee nr_errs++; 574*0232b788SKui-Feng Lee goto clean_link; 575*0232b788SKui-Feng Lee } 576*0232b788SKui-Feng Lee p_info("Registered %s %s map id %u link id %u", 577*0232b788SKui-Feng Lee get_kern_struct_ops_name(&info), 578*0232b788SKui-Feng Lee info.name, info.id, link_info.id); 579*0232b788SKui-Feng Lee 580*0232b788SKui-Feng Lee clean_link: 581*0232b788SKui-Feng Lee bpf_link__disconnect(link); 582*0232b788SKui-Feng Lee bpf_link__destroy(link); 58365c93628SMartin KaFai Lau } 58465c93628SMartin KaFai Lau 58565c93628SMartin KaFai Lau bpf_object__close(obj); 58665c93628SMartin KaFai Lau 58765c93628SMartin KaFai Lau if (nr_errs) 58865c93628SMartin KaFai Lau return -1; 58965c93628SMartin KaFai Lau 59065c93628SMartin KaFai Lau if (!nr_maps) { 59165c93628SMartin KaFai Lau p_err("no struct_ops found in %s", file); 59265c93628SMartin KaFai Lau return -1; 59365c93628SMartin KaFai Lau } 59465c93628SMartin KaFai Lau 59565c93628SMartin KaFai Lau if (json_output) 59665c93628SMartin KaFai Lau jsonw_null(json_wtr); 59765c93628SMartin KaFai Lau 59865c93628SMartin KaFai Lau return 0; 59965c93628SMartin KaFai Lau } 60065c93628SMartin KaFai Lau 60165c93628SMartin KaFai Lau static int do_help(int argc, char **argv) 60265c93628SMartin KaFai Lau { 60365c93628SMartin KaFai Lau if (json_output) { 60465c93628SMartin KaFai Lau jsonw_null(json_wtr); 60565c93628SMartin KaFai Lau return 0; 60665c93628SMartin KaFai Lau } 60765c93628SMartin KaFai Lau 60865c93628SMartin KaFai Lau fprintf(stderr, 60990040351SQuentin Monnet "Usage: %1$s %2$s { show | list } [STRUCT_OPS_MAP]\n" 61090040351SQuentin Monnet " %1$s %2$s dump [STRUCT_OPS_MAP]\n" 611*0232b788SKui-Feng Lee " %1$s %2$s register OBJ [LINK_DIR]\n" 61290040351SQuentin Monnet " %1$s %2$s unregister STRUCT_OPS_MAP\n" 61390040351SQuentin Monnet " %1$s %2$s help\n" 61465c93628SMartin KaFai Lau "\n" 61590040351SQuentin Monnet " STRUCT_OPS_MAP := [ id STRUCT_OPS_MAP_ID | name STRUCT_OPS_MAP_NAME ]\n" 616c07ba629SQuentin Monnet " " HELP_SPEC_OPTIONS " }\n" 61790040351SQuentin Monnet "", 61865c93628SMartin KaFai Lau bin_name, argv[-2]); 61965c93628SMartin KaFai Lau 62065c93628SMartin KaFai Lau return 0; 62165c93628SMartin KaFai Lau } 62265c93628SMartin KaFai Lau 62365c93628SMartin KaFai Lau static const struct cmd cmds[] = { 62465c93628SMartin KaFai Lau { "show", do_show }, 62565c93628SMartin KaFai Lau { "list", do_show }, 62665c93628SMartin KaFai Lau { "register", do_register }, 62765c93628SMartin KaFai Lau { "unregister", do_unregister }, 62865c93628SMartin KaFai Lau { "dump", do_dump }, 62965c93628SMartin KaFai Lau { "help", do_help }, 63065c93628SMartin KaFai Lau { 0 } 63165c93628SMartin KaFai Lau }; 63265c93628SMartin KaFai Lau 63365c93628SMartin KaFai Lau int do_struct_ops(int argc, char **argv) 63465c93628SMartin KaFai Lau { 63565c93628SMartin KaFai Lau int err; 63665c93628SMartin KaFai Lau 63765c93628SMartin KaFai Lau err = cmd_select(cmds, argc, argv, do_help); 63865c93628SMartin KaFai Lau 63965c93628SMartin KaFai Lau btf__free(btf_vmlinux); 64096b2eb6eSDaniel T. Lee 64165c93628SMartin KaFai Lau return err; 64265c93628SMartin KaFai Lau } 643