162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 262306a36Sopenharmony_ci/* Copyright (C) 2020 Facebook */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <errno.h> 562306a36Sopenharmony_ci#include <stdio.h> 662306a36Sopenharmony_ci#include <unistd.h> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/err.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <bpf/bpf.h> 1162306a36Sopenharmony_ci#include <bpf/btf.h> 1262306a36Sopenharmony_ci#include <bpf/libbpf.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "json_writer.h" 1562306a36Sopenharmony_ci#include "main.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define STRUCT_OPS_VALUE_PREFIX "bpf_struct_ops_" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic const struct btf_type *map_info_type; 2062306a36Sopenharmony_cistatic __u32 map_info_alloc_len; 2162306a36Sopenharmony_cistatic struct btf *btf_vmlinux; 2262306a36Sopenharmony_cistatic __s32 map_info_type_id; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistruct res { 2562306a36Sopenharmony_ci unsigned int nr_maps; 2662306a36Sopenharmony_ci unsigned int nr_errs; 2762306a36Sopenharmony_ci}; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic const struct btf *get_btf_vmlinux(void) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci if (btf_vmlinux) 3262306a36Sopenharmony_ci return btf_vmlinux; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci btf_vmlinux = libbpf_find_kernel_btf(); 3562306a36Sopenharmony_ci if (!btf_vmlinux) 3662306a36Sopenharmony_ci p_err("struct_ops requires kernel CONFIG_DEBUG_INFO_BTF=y"); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci return btf_vmlinux; 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic const char *get_kern_struct_ops_name(const struct bpf_map_info *info) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci const struct btf *kern_btf; 4462306a36Sopenharmony_ci const struct btf_type *t; 4562306a36Sopenharmony_ci const char *st_ops_name; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci kern_btf = get_btf_vmlinux(); 4862306a36Sopenharmony_ci if (!kern_btf) 4962306a36Sopenharmony_ci return "<btf_vmlinux_not_found>"; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci t = btf__type_by_id(kern_btf, info->btf_vmlinux_value_type_id); 5262306a36Sopenharmony_ci st_ops_name = btf__name_by_offset(kern_btf, t->name_off); 5362306a36Sopenharmony_ci st_ops_name += strlen(STRUCT_OPS_VALUE_PREFIX); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci return st_ops_name; 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic __s32 get_map_info_type_id(void) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci const struct btf *kern_btf; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if (map_info_type_id) 6362306a36Sopenharmony_ci return map_info_type_id; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci kern_btf = get_btf_vmlinux(); 6662306a36Sopenharmony_ci if (!kern_btf) 6762306a36Sopenharmony_ci return 0; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci map_info_type_id = btf__find_by_name_kind(kern_btf, "bpf_map_info", 7062306a36Sopenharmony_ci BTF_KIND_STRUCT); 7162306a36Sopenharmony_ci if (map_info_type_id < 0) { 7262306a36Sopenharmony_ci p_err("can't find bpf_map_info from btf_vmlinux"); 7362306a36Sopenharmony_ci return map_info_type_id; 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci map_info_type = btf__type_by_id(kern_btf, map_info_type_id); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci /* Ensure map_info_alloc() has at least what the bpftool needs */ 7862306a36Sopenharmony_ci map_info_alloc_len = map_info_type->size; 7962306a36Sopenharmony_ci if (map_info_alloc_len < sizeof(struct bpf_map_info)) 8062306a36Sopenharmony_ci map_info_alloc_len = sizeof(struct bpf_map_info); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci return map_info_type_id; 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/* If the subcmd needs to print out the bpf_map_info, 8662306a36Sopenharmony_ci * it should always call map_info_alloc to allocate 8762306a36Sopenharmony_ci * a bpf_map_info object instead of allocating it 8862306a36Sopenharmony_ci * on the stack. 8962306a36Sopenharmony_ci * 9062306a36Sopenharmony_ci * map_info_alloc() will take the running kernel's btf 9162306a36Sopenharmony_ci * into account. i.e. it will consider the 9262306a36Sopenharmony_ci * sizeof(struct bpf_map_info) of the running kernel. 9362306a36Sopenharmony_ci * 9462306a36Sopenharmony_ci * It will enable the "struct_ops" cmd to print the latest 9562306a36Sopenharmony_ci * "struct bpf_map_info". 9662306a36Sopenharmony_ci * 9762306a36Sopenharmony_ci * [ Recall that "struct_ops" requires the kernel's btf to 9862306a36Sopenharmony_ci * be available ] 9962306a36Sopenharmony_ci */ 10062306a36Sopenharmony_cistatic struct bpf_map_info *map_info_alloc(__u32 *alloc_len) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci struct bpf_map_info *info; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if (get_map_info_type_id() < 0) 10562306a36Sopenharmony_ci return NULL; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci info = calloc(1, map_info_alloc_len); 10862306a36Sopenharmony_ci if (!info) 10962306a36Sopenharmony_ci p_err("mem alloc failed"); 11062306a36Sopenharmony_ci else 11162306a36Sopenharmony_ci *alloc_len = map_info_alloc_len; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci return info; 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci/* It iterates all struct_ops maps of the system. 11762306a36Sopenharmony_ci * It returns the fd in "*res_fd" and map_info in "*info". 11862306a36Sopenharmony_ci * In the very first iteration, info->id should be 0. 11962306a36Sopenharmony_ci * An optional map "*name" filter can be specified. 12062306a36Sopenharmony_ci * The filter can be made more flexible in the future. 12162306a36Sopenharmony_ci * e.g. filter by kernel-struct-ops-name, regex-name, glob-name, ...etc. 12262306a36Sopenharmony_ci * 12362306a36Sopenharmony_ci * Return value: 12462306a36Sopenharmony_ci * 1: A struct_ops map found. It is returned in "*res_fd" and "*info". 12562306a36Sopenharmony_ci * The caller can continue to call get_next in the future. 12662306a36Sopenharmony_ci * 0: No struct_ops map is returned. 12762306a36Sopenharmony_ci * All struct_ops map has been found. 12862306a36Sopenharmony_ci * -1: Error and the caller should abort the iteration. 12962306a36Sopenharmony_ci */ 13062306a36Sopenharmony_cistatic int get_next_struct_ops_map(const char *name, int *res_fd, 13162306a36Sopenharmony_ci struct bpf_map_info *info, __u32 info_len) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci __u32 id = info->id; 13462306a36Sopenharmony_ci int err, fd; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci while (true) { 13762306a36Sopenharmony_ci err = bpf_map_get_next_id(id, &id); 13862306a36Sopenharmony_ci if (err) { 13962306a36Sopenharmony_ci if (errno == ENOENT) 14062306a36Sopenharmony_ci return 0; 14162306a36Sopenharmony_ci p_err("can't get next map: %s", strerror(errno)); 14262306a36Sopenharmony_ci return -1; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci fd = bpf_map_get_fd_by_id(id); 14662306a36Sopenharmony_ci if (fd < 0) { 14762306a36Sopenharmony_ci if (errno == ENOENT) 14862306a36Sopenharmony_ci continue; 14962306a36Sopenharmony_ci p_err("can't get map by id (%u): %s", 15062306a36Sopenharmony_ci id, strerror(errno)); 15162306a36Sopenharmony_ci return -1; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci err = bpf_map_get_info_by_fd(fd, info, &info_len); 15562306a36Sopenharmony_ci if (err) { 15662306a36Sopenharmony_ci p_err("can't get map info: %s", strerror(errno)); 15762306a36Sopenharmony_ci close(fd); 15862306a36Sopenharmony_ci return -1; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci if (info->type == BPF_MAP_TYPE_STRUCT_OPS && 16262306a36Sopenharmony_ci (!name || !strcmp(name, info->name))) { 16362306a36Sopenharmony_ci *res_fd = fd; 16462306a36Sopenharmony_ci return 1; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci close(fd); 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic int cmd_retval(const struct res *res, bool must_have_one_map) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci if (res->nr_errs || (!res->nr_maps && must_have_one_map)) 17362306a36Sopenharmony_ci return -1; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci return 0; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci/* "data" is the work_func private storage */ 17962306a36Sopenharmony_citypedef int (*work_func)(int fd, const struct bpf_map_info *info, void *data, 18062306a36Sopenharmony_ci struct json_writer *wtr); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci/* Find all struct_ops map in the system. 18362306a36Sopenharmony_ci * Filter out by "name" (if specified). 18462306a36Sopenharmony_ci * Then call "func(fd, info, data, wtr)" on each struct_ops map found. 18562306a36Sopenharmony_ci */ 18662306a36Sopenharmony_cistatic struct res do_search(const char *name, work_func func, void *data, 18762306a36Sopenharmony_ci struct json_writer *wtr) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci struct bpf_map_info *info; 19062306a36Sopenharmony_ci struct res res = {}; 19162306a36Sopenharmony_ci __u32 info_len; 19262306a36Sopenharmony_ci int fd, err; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci info = map_info_alloc(&info_len); 19562306a36Sopenharmony_ci if (!info) { 19662306a36Sopenharmony_ci res.nr_errs++; 19762306a36Sopenharmony_ci return res; 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (wtr) 20162306a36Sopenharmony_ci jsonw_start_array(wtr); 20262306a36Sopenharmony_ci while ((err = get_next_struct_ops_map(name, &fd, info, info_len)) == 1) { 20362306a36Sopenharmony_ci res.nr_maps++; 20462306a36Sopenharmony_ci err = func(fd, info, data, wtr); 20562306a36Sopenharmony_ci if (err) 20662306a36Sopenharmony_ci res.nr_errs++; 20762306a36Sopenharmony_ci close(fd); 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci if (wtr) 21062306a36Sopenharmony_ci jsonw_end_array(wtr); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (err) 21362306a36Sopenharmony_ci res.nr_errs++; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (!wtr && name && !res.nr_errs && !res.nr_maps) 21662306a36Sopenharmony_ci /* It is not printing empty []. 21762306a36Sopenharmony_ci * Thus, needs to specifically say nothing found 21862306a36Sopenharmony_ci * for "name" here. 21962306a36Sopenharmony_ci */ 22062306a36Sopenharmony_ci p_err("no struct_ops found for %s", name); 22162306a36Sopenharmony_ci else if (!wtr && json_output && !res.nr_errs) 22262306a36Sopenharmony_ci /* The "func()" above is not writing any json (i.e. !wtr 22362306a36Sopenharmony_ci * test here). 22462306a36Sopenharmony_ci * 22562306a36Sopenharmony_ci * However, "-j" is enabled and there is no errs here, 22662306a36Sopenharmony_ci * so call json_null() as the current convention of 22762306a36Sopenharmony_ci * other cmds. 22862306a36Sopenharmony_ci */ 22962306a36Sopenharmony_ci jsonw_null(json_wtr); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci free(info); 23262306a36Sopenharmony_ci return res; 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic struct res do_one_id(const char *id_str, work_func func, void *data, 23662306a36Sopenharmony_ci struct json_writer *wtr) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci struct bpf_map_info *info; 23962306a36Sopenharmony_ci struct res res = {}; 24062306a36Sopenharmony_ci unsigned long id; 24162306a36Sopenharmony_ci __u32 info_len; 24262306a36Sopenharmony_ci char *endptr; 24362306a36Sopenharmony_ci int fd; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci id = strtoul(id_str, &endptr, 0); 24662306a36Sopenharmony_ci if (*endptr || !id || id > UINT32_MAX) { 24762306a36Sopenharmony_ci p_err("invalid id %s", id_str); 24862306a36Sopenharmony_ci res.nr_errs++; 24962306a36Sopenharmony_ci return res; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci fd = bpf_map_get_fd_by_id(id); 25362306a36Sopenharmony_ci if (fd < 0) { 25462306a36Sopenharmony_ci p_err("can't get map by id (%lu): %s", id, strerror(errno)); 25562306a36Sopenharmony_ci res.nr_errs++; 25662306a36Sopenharmony_ci return res; 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci info = map_info_alloc(&info_len); 26062306a36Sopenharmony_ci if (!info) { 26162306a36Sopenharmony_ci res.nr_errs++; 26262306a36Sopenharmony_ci goto done; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci if (bpf_map_get_info_by_fd(fd, info, &info_len)) { 26662306a36Sopenharmony_ci p_err("can't get map info: %s", strerror(errno)); 26762306a36Sopenharmony_ci res.nr_errs++; 26862306a36Sopenharmony_ci goto done; 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (info->type != BPF_MAP_TYPE_STRUCT_OPS) { 27262306a36Sopenharmony_ci p_err("%s id %u is not a struct_ops map", info->name, info->id); 27362306a36Sopenharmony_ci res.nr_errs++; 27462306a36Sopenharmony_ci goto done; 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci res.nr_maps++; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (func(fd, info, data, wtr)) 28062306a36Sopenharmony_ci res.nr_errs++; 28162306a36Sopenharmony_ci else if (!wtr && json_output) 28262306a36Sopenharmony_ci /* The "func()" above is not writing any json (i.e. !wtr 28362306a36Sopenharmony_ci * test here). 28462306a36Sopenharmony_ci * 28562306a36Sopenharmony_ci * However, "-j" is enabled and there is no errs here, 28662306a36Sopenharmony_ci * so call json_null() as the current convention of 28762306a36Sopenharmony_ci * other cmds. 28862306a36Sopenharmony_ci */ 28962306a36Sopenharmony_ci jsonw_null(json_wtr); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cidone: 29262306a36Sopenharmony_ci free(info); 29362306a36Sopenharmony_ci close(fd); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci return res; 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic struct res do_work_on_struct_ops(const char *search_type, 29962306a36Sopenharmony_ci const char *search_term, 30062306a36Sopenharmony_ci work_func func, void *data, 30162306a36Sopenharmony_ci struct json_writer *wtr) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci if (search_type) { 30462306a36Sopenharmony_ci if (is_prefix(search_type, "id")) 30562306a36Sopenharmony_ci return do_one_id(search_term, func, data, wtr); 30662306a36Sopenharmony_ci else if (!is_prefix(search_type, "name")) 30762306a36Sopenharmony_ci usage(); 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci return do_search(search_term, func, data, wtr); 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic int __do_show(int fd, const struct bpf_map_info *info, void *data, 31462306a36Sopenharmony_ci struct json_writer *wtr) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci if (wtr) { 31762306a36Sopenharmony_ci jsonw_start_object(wtr); 31862306a36Sopenharmony_ci jsonw_uint_field(wtr, "id", info->id); 31962306a36Sopenharmony_ci jsonw_string_field(wtr, "name", info->name); 32062306a36Sopenharmony_ci jsonw_string_field(wtr, "kernel_struct_ops", 32162306a36Sopenharmony_ci get_kern_struct_ops_name(info)); 32262306a36Sopenharmony_ci jsonw_end_object(wtr); 32362306a36Sopenharmony_ci } else { 32462306a36Sopenharmony_ci printf("%u: %-15s %-32s\n", info->id, info->name, 32562306a36Sopenharmony_ci get_kern_struct_ops_name(info)); 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci return 0; 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_cistatic int do_show(int argc, char **argv) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci const char *search_type = NULL, *search_term = NULL; 33462306a36Sopenharmony_ci struct res res; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (argc && argc != 2) 33762306a36Sopenharmony_ci usage(); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci if (argc == 2) { 34062306a36Sopenharmony_ci search_type = GET_ARG(); 34162306a36Sopenharmony_ci search_term = GET_ARG(); 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci res = do_work_on_struct_ops(search_type, search_term, __do_show, 34562306a36Sopenharmony_ci NULL, json_wtr); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci return cmd_retval(&res, !!search_term); 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic int __do_dump(int fd, const struct bpf_map_info *info, void *data, 35162306a36Sopenharmony_ci struct json_writer *wtr) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci struct btf_dumper *d = (struct btf_dumper *)data; 35462306a36Sopenharmony_ci const struct btf_type *struct_ops_type; 35562306a36Sopenharmony_ci const struct btf *kern_btf = d->btf; 35662306a36Sopenharmony_ci const char *struct_ops_name; 35762306a36Sopenharmony_ci int zero = 0; 35862306a36Sopenharmony_ci void *value; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci /* note: d->jw == wtr */ 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci kern_btf = d->btf; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci /* The kernel supporting BPF_MAP_TYPE_STRUCT_OPS must have 36562306a36Sopenharmony_ci * btf_vmlinux_value_type_id. 36662306a36Sopenharmony_ci */ 36762306a36Sopenharmony_ci struct_ops_type = btf__type_by_id(kern_btf, 36862306a36Sopenharmony_ci info->btf_vmlinux_value_type_id); 36962306a36Sopenharmony_ci struct_ops_name = btf__name_by_offset(kern_btf, 37062306a36Sopenharmony_ci struct_ops_type->name_off); 37162306a36Sopenharmony_ci value = calloc(1, info->value_size); 37262306a36Sopenharmony_ci if (!value) { 37362306a36Sopenharmony_ci p_err("mem alloc failed"); 37462306a36Sopenharmony_ci return -1; 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci if (bpf_map_lookup_elem(fd, &zero, value)) { 37862306a36Sopenharmony_ci p_err("can't lookup struct_ops map %s id %u", 37962306a36Sopenharmony_ci info->name, info->id); 38062306a36Sopenharmony_ci free(value); 38162306a36Sopenharmony_ci return -1; 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci jsonw_start_object(wtr); 38562306a36Sopenharmony_ci jsonw_name(wtr, "bpf_map_info"); 38662306a36Sopenharmony_ci btf_dumper_type(d, map_info_type_id, (void *)info); 38762306a36Sopenharmony_ci jsonw_end_object(wtr); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci jsonw_start_object(wtr); 39062306a36Sopenharmony_ci jsonw_name(wtr, struct_ops_name); 39162306a36Sopenharmony_ci btf_dumper_type(d, info->btf_vmlinux_value_type_id, value); 39262306a36Sopenharmony_ci jsonw_end_object(wtr); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci free(value); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci return 0; 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_cistatic int do_dump(int argc, char **argv) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci const char *search_type = NULL, *search_term = NULL; 40262306a36Sopenharmony_ci json_writer_t *wtr = json_wtr; 40362306a36Sopenharmony_ci const struct btf *kern_btf; 40462306a36Sopenharmony_ci struct btf_dumper d = {}; 40562306a36Sopenharmony_ci struct res res; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci if (argc && argc != 2) 40862306a36Sopenharmony_ci usage(); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci if (argc == 2) { 41162306a36Sopenharmony_ci search_type = GET_ARG(); 41262306a36Sopenharmony_ci search_term = GET_ARG(); 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci kern_btf = get_btf_vmlinux(); 41662306a36Sopenharmony_ci if (!kern_btf) 41762306a36Sopenharmony_ci return -1; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci if (!json_output) { 42062306a36Sopenharmony_ci wtr = jsonw_new(stdout); 42162306a36Sopenharmony_ci if (!wtr) { 42262306a36Sopenharmony_ci p_err("can't create json writer"); 42362306a36Sopenharmony_ci return -1; 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci jsonw_pretty(wtr, true); 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci d.btf = kern_btf; 42962306a36Sopenharmony_ci d.jw = wtr; 43062306a36Sopenharmony_ci d.is_plain_text = !json_output; 43162306a36Sopenharmony_ci d.prog_id_as_func_ptr = true; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci res = do_work_on_struct_ops(search_type, search_term, __do_dump, &d, 43462306a36Sopenharmony_ci wtr); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci if (!json_output) 43762306a36Sopenharmony_ci jsonw_destroy(&wtr); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci return cmd_retval(&res, !!search_term); 44062306a36Sopenharmony_ci} 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_cistatic int __do_unregister(int fd, const struct bpf_map_info *info, void *data, 44362306a36Sopenharmony_ci struct json_writer *wtr) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci int zero = 0; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci if (bpf_map_delete_elem(fd, &zero)) { 44862306a36Sopenharmony_ci p_err("can't unload %s %s id %u: %s", 44962306a36Sopenharmony_ci get_kern_struct_ops_name(info), info->name, 45062306a36Sopenharmony_ci info->id, strerror(errno)); 45162306a36Sopenharmony_ci return -1; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci p_info("Unregistered %s %s id %u", 45562306a36Sopenharmony_ci get_kern_struct_ops_name(info), info->name, 45662306a36Sopenharmony_ci info->id); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci return 0; 45962306a36Sopenharmony_ci} 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_cistatic int do_unregister(int argc, char **argv) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci const char *search_type, *search_term; 46462306a36Sopenharmony_ci struct res res; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci if (argc != 2) 46762306a36Sopenharmony_ci usage(); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci search_type = GET_ARG(); 47062306a36Sopenharmony_ci search_term = GET_ARG(); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci res = do_work_on_struct_ops(search_type, search_term, 47362306a36Sopenharmony_ci __do_unregister, NULL, NULL); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci return cmd_retval(&res, true); 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cistatic int pin_link(struct bpf_link *link, const char *pindir, 47962306a36Sopenharmony_ci const char *name) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci char pinfile[PATH_MAX]; 48262306a36Sopenharmony_ci int err; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci err = pathname_concat(pinfile, sizeof(pinfile), pindir, name); 48562306a36Sopenharmony_ci if (err) 48662306a36Sopenharmony_ci return -1; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci return bpf_link__pin(link, pinfile); 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_cistatic int do_register(int argc, char **argv) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci LIBBPF_OPTS(bpf_object_open_opts, open_opts); 49462306a36Sopenharmony_ci __u32 link_info_len = sizeof(struct bpf_link_info); 49562306a36Sopenharmony_ci struct bpf_link_info link_info = {}; 49662306a36Sopenharmony_ci struct bpf_map_info info = {}; 49762306a36Sopenharmony_ci __u32 info_len = sizeof(info); 49862306a36Sopenharmony_ci int nr_errs = 0, nr_maps = 0; 49962306a36Sopenharmony_ci const char *linkdir = NULL; 50062306a36Sopenharmony_ci struct bpf_object *obj; 50162306a36Sopenharmony_ci struct bpf_link *link; 50262306a36Sopenharmony_ci struct bpf_map *map; 50362306a36Sopenharmony_ci const char *file; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci if (argc != 1 && argc != 2) 50662306a36Sopenharmony_ci usage(); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci file = GET_ARG(); 50962306a36Sopenharmony_ci if (argc == 1) 51062306a36Sopenharmony_ci linkdir = GET_ARG(); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci if (linkdir && mount_bpffs_for_pin(linkdir, true)) { 51362306a36Sopenharmony_ci p_err("can't mount bpffs for pinning"); 51462306a36Sopenharmony_ci return -1; 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci if (verifier_logs) 51862306a36Sopenharmony_ci /* log_level1 + log_level2 + stats, but not stable UAPI */ 51962306a36Sopenharmony_ci open_opts.kernel_log_level = 1 + 2 + 4; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci obj = bpf_object__open_file(file, &open_opts); 52262306a36Sopenharmony_ci if (!obj) 52362306a36Sopenharmony_ci return -1; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci set_max_rlimit(); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci if (bpf_object__load(obj)) { 52862306a36Sopenharmony_ci bpf_object__close(obj); 52962306a36Sopenharmony_ci return -1; 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci bpf_object__for_each_map(map, obj) { 53362306a36Sopenharmony_ci if (bpf_map__type(map) != BPF_MAP_TYPE_STRUCT_OPS) 53462306a36Sopenharmony_ci continue; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci link = bpf_map__attach_struct_ops(map); 53762306a36Sopenharmony_ci if (!link) { 53862306a36Sopenharmony_ci p_err("can't register struct_ops %s: %s", 53962306a36Sopenharmony_ci bpf_map__name(map), strerror(errno)); 54062306a36Sopenharmony_ci nr_errs++; 54162306a36Sopenharmony_ci continue; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci nr_maps++; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci if (bpf_map_get_info_by_fd(bpf_map__fd(map), &info, 54662306a36Sopenharmony_ci &info_len)) { 54762306a36Sopenharmony_ci /* Not p_err. The struct_ops was attached 54862306a36Sopenharmony_ci * successfully. 54962306a36Sopenharmony_ci */ 55062306a36Sopenharmony_ci p_info("Registered %s but can't find id: %s", 55162306a36Sopenharmony_ci bpf_map__name(map), strerror(errno)); 55262306a36Sopenharmony_ci goto clean_link; 55362306a36Sopenharmony_ci } 55462306a36Sopenharmony_ci if (!(bpf_map__map_flags(map) & BPF_F_LINK)) { 55562306a36Sopenharmony_ci p_info("Registered %s %s id %u", 55662306a36Sopenharmony_ci get_kern_struct_ops_name(&info), 55762306a36Sopenharmony_ci info.name, 55862306a36Sopenharmony_ci info.id); 55962306a36Sopenharmony_ci goto clean_link; 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci if (bpf_link_get_info_by_fd(bpf_link__fd(link), 56262306a36Sopenharmony_ci &link_info, 56362306a36Sopenharmony_ci &link_info_len)) { 56462306a36Sopenharmony_ci p_err("Registered %s but can't find link id: %s", 56562306a36Sopenharmony_ci bpf_map__name(map), strerror(errno)); 56662306a36Sopenharmony_ci nr_errs++; 56762306a36Sopenharmony_ci goto clean_link; 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci if (linkdir && pin_link(link, linkdir, info.name)) { 57062306a36Sopenharmony_ci p_err("can't pin link %u for %s: %s", 57162306a36Sopenharmony_ci link_info.id, info.name, 57262306a36Sopenharmony_ci strerror(errno)); 57362306a36Sopenharmony_ci nr_errs++; 57462306a36Sopenharmony_ci goto clean_link; 57562306a36Sopenharmony_ci } 57662306a36Sopenharmony_ci p_info("Registered %s %s map id %u link id %u", 57762306a36Sopenharmony_ci get_kern_struct_ops_name(&info), 57862306a36Sopenharmony_ci info.name, info.id, link_info.id); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ciclean_link: 58162306a36Sopenharmony_ci bpf_link__disconnect(link); 58262306a36Sopenharmony_ci bpf_link__destroy(link); 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci bpf_object__close(obj); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci if (nr_errs) 58862306a36Sopenharmony_ci return -1; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if (!nr_maps) { 59162306a36Sopenharmony_ci p_err("no struct_ops found in %s", file); 59262306a36Sopenharmony_ci return -1; 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci if (json_output) 59662306a36Sopenharmony_ci jsonw_null(json_wtr); 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci return 0; 59962306a36Sopenharmony_ci} 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_cistatic int do_help(int argc, char **argv) 60262306a36Sopenharmony_ci{ 60362306a36Sopenharmony_ci if (json_output) { 60462306a36Sopenharmony_ci jsonw_null(json_wtr); 60562306a36Sopenharmony_ci return 0; 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci fprintf(stderr, 60962306a36Sopenharmony_ci "Usage: %1$s %2$s { show | list } [STRUCT_OPS_MAP]\n" 61062306a36Sopenharmony_ci " %1$s %2$s dump [STRUCT_OPS_MAP]\n" 61162306a36Sopenharmony_ci " %1$s %2$s register OBJ [LINK_DIR]\n" 61262306a36Sopenharmony_ci " %1$s %2$s unregister STRUCT_OPS_MAP\n" 61362306a36Sopenharmony_ci " %1$s %2$s help\n" 61462306a36Sopenharmony_ci "\n" 61562306a36Sopenharmony_ci " STRUCT_OPS_MAP := [ id STRUCT_OPS_MAP_ID | name STRUCT_OPS_MAP_NAME ]\n" 61662306a36Sopenharmony_ci " " HELP_SPEC_OPTIONS " }\n" 61762306a36Sopenharmony_ci "", 61862306a36Sopenharmony_ci bin_name, argv[-2]); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci return 0; 62162306a36Sopenharmony_ci} 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_cistatic const struct cmd cmds[] = { 62462306a36Sopenharmony_ci { "show", do_show }, 62562306a36Sopenharmony_ci { "list", do_show }, 62662306a36Sopenharmony_ci { "register", do_register }, 62762306a36Sopenharmony_ci { "unregister", do_unregister }, 62862306a36Sopenharmony_ci { "dump", do_dump }, 62962306a36Sopenharmony_ci { "help", do_help }, 63062306a36Sopenharmony_ci { 0 } 63162306a36Sopenharmony_ci}; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ciint do_struct_ops(int argc, char **argv) 63462306a36Sopenharmony_ci{ 63562306a36Sopenharmony_ci int err; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci err = cmd_select(cmds, argc, argv, do_help); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci btf__free(btf_vmlinux); 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci return err; 64262306a36Sopenharmony_ci} 643