Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
0002 /* Copyright (C) 2020 Facebook */
0003 
0004 #include <errno.h>
0005 #include <stdio.h>
0006 #include <unistd.h>
0007 
0008 #include <linux/err.h>
0009 
0010 #include <bpf/bpf.h>
0011 #include <bpf/btf.h>
0012 #include <bpf/libbpf.h>
0013 
0014 #include "json_writer.h"
0015 #include "main.h"
0016 
0017 #define STRUCT_OPS_VALUE_PREFIX "bpf_struct_ops_"
0018 
0019 static const struct btf_type *map_info_type;
0020 static __u32 map_info_alloc_len;
0021 static struct btf *btf_vmlinux;
0022 static __s32 map_info_type_id;
0023 
0024 struct res {
0025     unsigned int nr_maps;
0026     unsigned int nr_errs;
0027 };
0028 
0029 static const struct btf *get_btf_vmlinux(void)
0030 {
0031     if (btf_vmlinux)
0032         return btf_vmlinux;
0033 
0034     btf_vmlinux = libbpf_find_kernel_btf();
0035     if (libbpf_get_error(btf_vmlinux))
0036         p_err("struct_ops requires kernel CONFIG_DEBUG_INFO_BTF=y");
0037 
0038     return btf_vmlinux;
0039 }
0040 
0041 static const char *get_kern_struct_ops_name(const struct bpf_map_info *info)
0042 {
0043     const struct btf *kern_btf;
0044     const struct btf_type *t;
0045     const char *st_ops_name;
0046 
0047     kern_btf = get_btf_vmlinux();
0048     if (libbpf_get_error(kern_btf))
0049         return "<btf_vmlinux_not_found>";
0050 
0051     t = btf__type_by_id(kern_btf, info->btf_vmlinux_value_type_id);
0052     st_ops_name = btf__name_by_offset(kern_btf, t->name_off);
0053     st_ops_name += strlen(STRUCT_OPS_VALUE_PREFIX);
0054 
0055     return st_ops_name;
0056 }
0057 
0058 static __s32 get_map_info_type_id(void)
0059 {
0060     const struct btf *kern_btf;
0061 
0062     if (map_info_type_id)
0063         return map_info_type_id;
0064 
0065     kern_btf = get_btf_vmlinux();
0066     if (libbpf_get_error(kern_btf)) {
0067         map_info_type_id = PTR_ERR(kern_btf);
0068         return map_info_type_id;
0069     }
0070 
0071     map_info_type_id = btf__find_by_name_kind(kern_btf, "bpf_map_info",
0072                           BTF_KIND_STRUCT);
0073     if (map_info_type_id < 0) {
0074         p_err("can't find bpf_map_info from btf_vmlinux");
0075         return map_info_type_id;
0076     }
0077     map_info_type = btf__type_by_id(kern_btf, map_info_type_id);
0078 
0079     /* Ensure map_info_alloc() has at least what the bpftool needs */
0080     map_info_alloc_len = map_info_type->size;
0081     if (map_info_alloc_len < sizeof(struct bpf_map_info))
0082         map_info_alloc_len = sizeof(struct bpf_map_info);
0083 
0084     return map_info_type_id;
0085 }
0086 
0087 /* If the subcmd needs to print out the bpf_map_info,
0088  * it should always call map_info_alloc to allocate
0089  * a bpf_map_info object instead of allocating it
0090  * on the stack.
0091  *
0092  * map_info_alloc() will take the running kernel's btf
0093  * into account.  i.e. it will consider the
0094  * sizeof(struct bpf_map_info) of the running kernel.
0095  *
0096  * It will enable the "struct_ops" cmd to print the latest
0097  * "struct bpf_map_info".
0098  *
0099  * [ Recall that "struct_ops" requires the kernel's btf to
0100  *   be available ]
0101  */
0102 static struct bpf_map_info *map_info_alloc(__u32 *alloc_len)
0103 {
0104     struct bpf_map_info *info;
0105 
0106     if (get_map_info_type_id() < 0)
0107         return NULL;
0108 
0109     info = calloc(1, map_info_alloc_len);
0110     if (!info)
0111         p_err("mem alloc failed");
0112     else
0113         *alloc_len = map_info_alloc_len;
0114 
0115     return info;
0116 }
0117 
0118 /* It iterates all struct_ops maps of the system.
0119  * It returns the fd in "*res_fd" and map_info in "*info".
0120  * In the very first iteration, info->id should be 0.
0121  * An optional map "*name" filter can be specified.
0122  * The filter can be made more flexible in the future.
0123  * e.g. filter by kernel-struct-ops-name, regex-name, glob-name, ...etc.
0124  *
0125  * Return value:
0126  *     1: A struct_ops map found.  It is returned in "*res_fd" and "*info".
0127  *    The caller can continue to call get_next in the future.
0128  *     0: No struct_ops map is returned.
0129  *        All struct_ops map has been found.
0130  *    -1: Error and the caller should abort the iteration.
0131  */
0132 static int get_next_struct_ops_map(const char *name, int *res_fd,
0133                    struct bpf_map_info *info, __u32 info_len)
0134 {
0135     __u32 id = info->id;
0136     int err, fd;
0137 
0138     while (true) {
0139         err = bpf_map_get_next_id(id, &id);
0140         if (err) {
0141             if (errno == ENOENT)
0142                 return 0;
0143             p_err("can't get next map: %s", strerror(errno));
0144             return -1;
0145         }
0146 
0147         fd = bpf_map_get_fd_by_id(id);
0148         if (fd < 0) {
0149             if (errno == ENOENT)
0150                 continue;
0151             p_err("can't get map by id (%u): %s",
0152                   id, strerror(errno));
0153             return -1;
0154         }
0155 
0156         err = bpf_obj_get_info_by_fd(fd, info, &info_len);
0157         if (err) {
0158             p_err("can't get map info: %s", strerror(errno));
0159             close(fd);
0160             return -1;
0161         }
0162 
0163         if (info->type == BPF_MAP_TYPE_STRUCT_OPS &&
0164             (!name || !strcmp(name, info->name))) {
0165             *res_fd = fd;
0166             return 1;
0167         }
0168         close(fd);
0169     }
0170 }
0171 
0172 static int cmd_retval(const struct res *res, bool must_have_one_map)
0173 {
0174     if (res->nr_errs || (!res->nr_maps && must_have_one_map))
0175         return -1;
0176 
0177     return 0;
0178 }
0179 
0180 /* "data" is the work_func private storage */
0181 typedef int (*work_func)(int fd, const struct bpf_map_info *info, void *data,
0182              struct json_writer *wtr);
0183 
0184 /* Find all struct_ops map in the system.
0185  * Filter out by "name" (if specified).
0186  * Then call "func(fd, info, data, wtr)" on each struct_ops map found.
0187  */
0188 static struct res do_search(const char *name, work_func func, void *data,
0189                 struct json_writer *wtr)
0190 {
0191     struct bpf_map_info *info;
0192     struct res res = {};
0193     __u32 info_len;
0194     int fd, err;
0195 
0196     info = map_info_alloc(&info_len);
0197     if (!info) {
0198         res.nr_errs++;
0199         return res;
0200     }
0201 
0202     if (wtr)
0203         jsonw_start_array(wtr);
0204     while ((err = get_next_struct_ops_map(name, &fd, info, info_len)) == 1) {
0205         res.nr_maps++;
0206         err = func(fd, info, data, wtr);
0207         if (err)
0208             res.nr_errs++;
0209         close(fd);
0210     }
0211     if (wtr)
0212         jsonw_end_array(wtr);
0213 
0214     if (err)
0215         res.nr_errs++;
0216 
0217     if (!wtr && name && !res.nr_errs && !res.nr_maps)
0218         /* It is not printing empty [].
0219          * Thus, needs to specifically say nothing found
0220          * for "name" here.
0221          */
0222         p_err("no struct_ops found for %s", name);
0223     else if (!wtr && json_output && !res.nr_errs)
0224         /* The "func()" above is not writing any json (i.e. !wtr
0225          * test here).
0226          *
0227          * However, "-j" is enabled and there is no errs here,
0228          * so call json_null() as the current convention of
0229          * other cmds.
0230          */
0231         jsonw_null(json_wtr);
0232 
0233     free(info);
0234     return res;
0235 }
0236 
0237 static struct res do_one_id(const char *id_str, work_func func, void *data,
0238                 struct json_writer *wtr)
0239 {
0240     struct bpf_map_info *info;
0241     struct res res = {};
0242     unsigned long id;
0243     __u32 info_len;
0244     char *endptr;
0245     int fd;
0246 
0247     id = strtoul(id_str, &endptr, 0);
0248     if (*endptr || !id || id > UINT32_MAX) {
0249         p_err("invalid id %s", id_str);
0250         res.nr_errs++;
0251         return res;
0252     }
0253 
0254     fd = bpf_map_get_fd_by_id(id);
0255     if (fd < 0) {
0256         p_err("can't get map by id (%lu): %s", id, strerror(errno));
0257         res.nr_errs++;
0258         return res;
0259     }
0260 
0261     info = map_info_alloc(&info_len);
0262     if (!info) {
0263         res.nr_errs++;
0264         goto done;
0265     }
0266 
0267     if (bpf_obj_get_info_by_fd(fd, info, &info_len)) {
0268         p_err("can't get map info: %s", strerror(errno));
0269         res.nr_errs++;
0270         goto done;
0271     }
0272 
0273     if (info->type != BPF_MAP_TYPE_STRUCT_OPS) {
0274         p_err("%s id %u is not a struct_ops map", info->name, info->id);
0275         res.nr_errs++;
0276         goto done;
0277     }
0278 
0279     res.nr_maps++;
0280 
0281     if (func(fd, info, data, wtr))
0282         res.nr_errs++;
0283     else if (!wtr && json_output)
0284         /* The "func()" above is not writing any json (i.e. !wtr
0285          * test here).
0286          *
0287          * However, "-j" is enabled and there is no errs here,
0288          * so call json_null() as the current convention of
0289          * other cmds.
0290          */
0291         jsonw_null(json_wtr);
0292 
0293 done:
0294     free(info);
0295     close(fd);
0296 
0297     return res;
0298 }
0299 
0300 static struct res do_work_on_struct_ops(const char *search_type,
0301                     const char *search_term,
0302                     work_func func, void *data,
0303                     struct json_writer *wtr)
0304 {
0305     if (search_type) {
0306         if (is_prefix(search_type, "id"))
0307             return do_one_id(search_term, func, data, wtr);
0308         else if (!is_prefix(search_type, "name"))
0309             usage();
0310     }
0311 
0312     return do_search(search_term, func, data, wtr);
0313 }
0314 
0315 static int __do_show(int fd, const struct bpf_map_info *info, void *data,
0316              struct json_writer *wtr)
0317 {
0318     if (wtr) {
0319         jsonw_start_object(wtr);
0320         jsonw_uint_field(wtr, "id", info->id);
0321         jsonw_string_field(wtr, "name", info->name);
0322         jsonw_string_field(wtr, "kernel_struct_ops",
0323                    get_kern_struct_ops_name(info));
0324         jsonw_end_object(wtr);
0325     } else {
0326         printf("%u: %-15s %-32s\n", info->id, info->name,
0327                get_kern_struct_ops_name(info));
0328     }
0329 
0330     return 0;
0331 }
0332 
0333 static int do_show(int argc, char **argv)
0334 {
0335     const char *search_type = NULL, *search_term = NULL;
0336     struct res res;
0337 
0338     if (argc && argc != 2)
0339         usage();
0340 
0341     if (argc == 2) {
0342         search_type = GET_ARG();
0343         search_term = GET_ARG();
0344     }
0345 
0346     res = do_work_on_struct_ops(search_type, search_term, __do_show,
0347                     NULL, json_wtr);
0348 
0349     return cmd_retval(&res, !!search_term);
0350 }
0351 
0352 static int __do_dump(int fd, const struct bpf_map_info *info, void *data,
0353              struct json_writer *wtr)
0354 {
0355     struct btf_dumper *d = (struct btf_dumper *)data;
0356     const struct btf_type *struct_ops_type;
0357     const struct btf *kern_btf = d->btf;
0358     const char *struct_ops_name;
0359     int zero = 0;
0360     void *value;
0361 
0362     /* note: d->jw == wtr */
0363 
0364     kern_btf = d->btf;
0365 
0366     /* The kernel supporting BPF_MAP_TYPE_STRUCT_OPS must have
0367      * btf_vmlinux_value_type_id.
0368      */
0369     struct_ops_type = btf__type_by_id(kern_btf,
0370                       info->btf_vmlinux_value_type_id);
0371     struct_ops_name = btf__name_by_offset(kern_btf,
0372                           struct_ops_type->name_off);
0373     value = calloc(1, info->value_size);
0374     if (!value) {
0375         p_err("mem alloc failed");
0376         return -1;
0377     }
0378 
0379     if (bpf_map_lookup_elem(fd, &zero, value)) {
0380         p_err("can't lookup struct_ops map %s id %u",
0381               info->name, info->id);
0382         free(value);
0383         return -1;
0384     }
0385 
0386     jsonw_start_object(wtr);
0387     jsonw_name(wtr, "bpf_map_info");
0388     btf_dumper_type(d, map_info_type_id, (void *)info);
0389     jsonw_end_object(wtr);
0390 
0391     jsonw_start_object(wtr);
0392     jsonw_name(wtr, struct_ops_name);
0393     btf_dumper_type(d, info->btf_vmlinux_value_type_id, value);
0394     jsonw_end_object(wtr);
0395 
0396     free(value);
0397 
0398     return 0;
0399 }
0400 
0401 static int do_dump(int argc, char **argv)
0402 {
0403     const char *search_type = NULL, *search_term = NULL;
0404     json_writer_t *wtr = json_wtr;
0405     const struct btf *kern_btf;
0406     struct btf_dumper d = {};
0407     struct res res;
0408 
0409     if (argc && argc != 2)
0410         usage();
0411 
0412     if (argc == 2) {
0413         search_type = GET_ARG();
0414         search_term = GET_ARG();
0415     }
0416 
0417     kern_btf = get_btf_vmlinux();
0418     if (libbpf_get_error(kern_btf))
0419         return -1;
0420 
0421     if (!json_output) {
0422         wtr = jsonw_new(stdout);
0423         if (!wtr) {
0424             p_err("can't create json writer");
0425             return -1;
0426         }
0427         jsonw_pretty(wtr, true);
0428     }
0429 
0430     d.btf = kern_btf;
0431     d.jw = wtr;
0432     d.is_plain_text = !json_output;
0433     d.prog_id_as_func_ptr = true;
0434 
0435     res = do_work_on_struct_ops(search_type, search_term, __do_dump, &d,
0436                     wtr);
0437 
0438     if (!json_output)
0439         jsonw_destroy(&wtr);
0440 
0441     return cmd_retval(&res, !!search_term);
0442 }
0443 
0444 static int __do_unregister(int fd, const struct bpf_map_info *info, void *data,
0445                struct json_writer *wtr)
0446 {
0447     int zero = 0;
0448 
0449     if (bpf_map_delete_elem(fd, &zero)) {
0450         p_err("can't unload %s %s id %u: %s",
0451               get_kern_struct_ops_name(info), info->name,
0452               info->id, strerror(errno));
0453         return -1;
0454     }
0455 
0456     p_info("Unregistered %s %s id %u",
0457            get_kern_struct_ops_name(info), info->name,
0458            info->id);
0459 
0460     return 0;
0461 }
0462 
0463 static int do_unregister(int argc, char **argv)
0464 {
0465     const char *search_type, *search_term;
0466     struct res res;
0467 
0468     if (argc != 2)
0469         usage();
0470 
0471     search_type = GET_ARG();
0472     search_term = GET_ARG();
0473 
0474     res = do_work_on_struct_ops(search_type, search_term,
0475                     __do_unregister, NULL, NULL);
0476 
0477     return cmd_retval(&res, true);
0478 }
0479 
0480 static int do_register(int argc, char **argv)
0481 {
0482     LIBBPF_OPTS(bpf_object_open_opts, open_opts);
0483     struct bpf_map_info info = {};
0484     __u32 info_len = sizeof(info);
0485     int nr_errs = 0, nr_maps = 0;
0486     struct bpf_object *obj;
0487     struct bpf_link *link;
0488     struct bpf_map *map;
0489     const char *file;
0490 
0491     if (argc != 1)
0492         usage();
0493 
0494     file = GET_ARG();
0495 
0496     if (verifier_logs)
0497         /* log_level1 + log_level2 + stats, but not stable UAPI */
0498         open_opts.kernel_log_level = 1 + 2 + 4;
0499 
0500     obj = bpf_object__open_file(file, &open_opts);
0501     if (libbpf_get_error(obj))
0502         return -1;
0503 
0504     set_max_rlimit();
0505 
0506     if (bpf_object__load(obj)) {
0507         bpf_object__close(obj);
0508         return -1;
0509     }
0510 
0511     bpf_object__for_each_map(map, obj) {
0512         if (bpf_map__type(map) != BPF_MAP_TYPE_STRUCT_OPS)
0513             continue;
0514 
0515         link = bpf_map__attach_struct_ops(map);
0516         if (libbpf_get_error(link)) {
0517             p_err("can't register struct_ops %s: %s",
0518                   bpf_map__name(map),
0519                   strerror(-PTR_ERR(link)));
0520             nr_errs++;
0521             continue;
0522         }
0523         nr_maps++;
0524 
0525         bpf_link__disconnect(link);
0526         bpf_link__destroy(link);
0527 
0528         if (!bpf_obj_get_info_by_fd(bpf_map__fd(map), &info,
0529                         &info_len))
0530             p_info("Registered %s %s id %u",
0531                    get_kern_struct_ops_name(&info),
0532                    bpf_map__name(map),
0533                    info.id);
0534         else
0535             /* Not p_err.  The struct_ops was attached
0536              * successfully.
0537              */
0538             p_info("Registered %s but can't find id: %s",
0539                    bpf_map__name(map), strerror(errno));
0540     }
0541 
0542     bpf_object__close(obj);
0543 
0544     if (nr_errs)
0545         return -1;
0546 
0547     if (!nr_maps) {
0548         p_err("no struct_ops found in %s", file);
0549         return -1;
0550     }
0551 
0552     if (json_output)
0553         jsonw_null(json_wtr);
0554 
0555     return 0;
0556 }
0557 
0558 static int do_help(int argc, char **argv)
0559 {
0560     if (json_output) {
0561         jsonw_null(json_wtr);
0562         return 0;
0563     }
0564 
0565     fprintf(stderr,
0566         "Usage: %1$s %2$s { show | list } [STRUCT_OPS_MAP]\n"
0567         "       %1$s %2$s dump [STRUCT_OPS_MAP]\n"
0568         "       %1$s %2$s register OBJ\n"
0569         "       %1$s %2$s unregister STRUCT_OPS_MAP\n"
0570         "       %1$s %2$s help\n"
0571         "\n"
0572         "       STRUCT_OPS_MAP := [ id STRUCT_OPS_MAP_ID | name STRUCT_OPS_MAP_NAME ]\n"
0573         "       " HELP_SPEC_OPTIONS " }\n"
0574         "",
0575         bin_name, argv[-2]);
0576 
0577     return 0;
0578 }
0579 
0580 static const struct cmd cmds[] = {
0581     { "show",   do_show },
0582     { "list",   do_show },
0583     { "register",   do_register },
0584     { "unregister", do_unregister },
0585     { "dump",   do_dump },
0586     { "help",   do_help },
0587     { 0 }
0588 };
0589 
0590 int do_struct_ops(int argc, char **argv)
0591 {
0592     int err;
0593 
0594     err = cmd_select(cmds, argc, argv, do_help);
0595 
0596     if (!libbpf_get_error(btf_vmlinux))
0597         btf__free(btf_vmlinux);
0598 
0599     return err;
0600 }