linux/tools/bpf/bpftool/struct_ops.c
<<
>>
Prefs
   1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
   2/* Copyright (C) 2020 Facebook */
   3
   4#include <errno.h>
   5#include <stdio.h>
   6#include <unistd.h>
   7
   8#include <linux/err.h>
   9
  10#include <bpf/bpf.h>
  11#include <bpf/btf.h>
  12#include <bpf/libbpf.h>
  13
  14#include "json_writer.h"
  15#include "main.h"
  16
  17#define STRUCT_OPS_VALUE_PREFIX "bpf_struct_ops_"
  18
  19static const struct btf_type *map_info_type;
  20static __u32 map_info_alloc_len;
  21static struct btf *btf_vmlinux;
  22static __s32 map_info_type_id;
  23
  24struct res {
  25        unsigned int nr_maps;
  26        unsigned int nr_errs;
  27};
  28
  29static const struct btf *get_btf_vmlinux(void)
  30{
  31        if (btf_vmlinux)
  32                return btf_vmlinux;
  33
  34        btf_vmlinux = libbpf_find_kernel_btf();
  35        if (IS_ERR(btf_vmlinux))
  36                p_err("struct_ops requires kernel CONFIG_DEBUG_INFO_BTF=y");
  37
  38        return btf_vmlinux;
  39}
  40
  41static const char *get_kern_struct_ops_name(const struct bpf_map_info *info)
  42{
  43        const struct btf *kern_btf;
  44        const struct btf_type *t;
  45        const char *st_ops_name;
  46
  47        kern_btf = get_btf_vmlinux();
  48        if (IS_ERR(kern_btf))
  49                return "<btf_vmlinux_not_found>";
  50
  51        t = btf__type_by_id(kern_btf, info->btf_vmlinux_value_type_id);
  52        st_ops_name = btf__name_by_offset(kern_btf, t->name_off);
  53        st_ops_name += strlen(STRUCT_OPS_VALUE_PREFIX);
  54
  55        return st_ops_name;
  56}
  57
  58static __s32 get_map_info_type_id(void)
  59{
  60        const struct btf *kern_btf;
  61
  62        if (map_info_type_id)
  63                return map_info_type_id;
  64
  65        kern_btf = get_btf_vmlinux();
  66        if (IS_ERR(kern_btf)) {
  67                map_info_type_id = PTR_ERR(kern_btf);
  68                return map_info_type_id;
  69        }
  70
  71        map_info_type_id = btf__find_by_name_kind(kern_btf, "bpf_map_info",
  72                                                  BTF_KIND_STRUCT);
  73        if (map_info_type_id < 0) {
  74                p_err("can't find bpf_map_info from btf_vmlinux");
  75                return map_info_type_id;
  76        }
  77        map_info_type = btf__type_by_id(kern_btf, map_info_type_id);
  78
  79        /* Ensure map_info_alloc() has at least what the bpftool needs */
  80        map_info_alloc_len = map_info_type->size;
  81        if (map_info_alloc_len < sizeof(struct bpf_map_info))
  82                map_info_alloc_len = sizeof(struct bpf_map_info);
  83
  84        return map_info_type_id;
  85}
  86
  87/* If the subcmd needs to print out the bpf_map_info,
  88 * it should always call map_info_alloc to allocate
  89 * a bpf_map_info object instead of allocating it
  90 * on the stack.
  91 *
  92 * map_info_alloc() will take the running kernel's btf
  93 * into account.  i.e. it will consider the
  94 * sizeof(struct bpf_map_info) of the running kernel.
  95 *
  96 * It will enable the "struct_ops" cmd to print the latest
  97 * "struct bpf_map_info".
  98 *
  99 * [ Recall that "struct_ops" requires the kernel's btf to
 100 *   be available ]
 101 */
 102static struct bpf_map_info *map_info_alloc(__u32 *alloc_len)
 103{
 104        struct bpf_map_info *info;
 105
 106        if (get_map_info_type_id() < 0)
 107                return NULL;
 108
 109        info = calloc(1, map_info_alloc_len);
 110        if (!info)
 111                p_err("mem alloc failed");
 112        else
 113                *alloc_len = map_info_alloc_len;
 114
 115        return info;
 116}
 117
 118/* It iterates all struct_ops maps of the system.
 119 * It returns the fd in "*res_fd" and map_info in "*info".
 120 * In the very first iteration, info->id should be 0.
 121 * An optional map "*name" filter can be specified.
 122 * The filter can be made more flexible in the future.
 123 * e.g. filter by kernel-struct-ops-name, regex-name, glob-name, ...etc.
 124 *
 125 * Return value:
 126 *     1: A struct_ops map found.  It is returned in "*res_fd" and "*info".
 127 *        The caller can continue to call get_next in the future.
 128 *     0: No struct_ops map is returned.
 129 *        All struct_ops map has been found.
 130 *    -1: Error and the caller should abort the iteration.
 131 */
 132static int get_next_struct_ops_map(const char *name, int *res_fd,
 133                                   struct bpf_map_info *info, __u32 info_len)
 134{
 135        __u32 id = info->id;
 136        int err, fd;
 137
 138        while (true) {
 139                err = bpf_map_get_next_id(id, &id);
 140                if (err) {
 141                        if (errno == ENOENT)
 142                                return 0;
 143                        p_err("can't get next map: %s", strerror(errno));
 144                        return -1;
 145                }
 146
 147                fd = bpf_map_get_fd_by_id(id);
 148                if (fd < 0) {
 149                        if (errno == ENOENT)
 150                                continue;
 151                        p_err("can't get map by id (%u): %s",
 152                              id, strerror(errno));
 153                        return -1;
 154                }
 155
 156                err = bpf_obj_get_info_by_fd(fd, info, &info_len);
 157                if (err) {
 158                        p_err("can't get map info: %s", strerror(errno));
 159                        close(fd);
 160                        return -1;
 161                }
 162
 163                if (info->type == BPF_MAP_TYPE_STRUCT_OPS &&
 164                    (!name || !strcmp(name, info->name))) {
 165                        *res_fd = fd;
 166                        return 1;
 167                }
 168                close(fd);
 169        }
 170}
 171
 172static int cmd_retval(const struct res *res, bool must_have_one_map)
 173{
 174        if (res->nr_errs || (!res->nr_maps && must_have_one_map))
 175                return -1;
 176
 177        return 0;
 178}
 179
 180/* "data" is the work_func private storage */
 181typedef int (*work_func)(int fd, const struct bpf_map_info *info, void *data,
 182                         struct json_writer *wtr);
 183
 184/* Find all struct_ops map in the system.
 185 * Filter out by "name" (if specified).
 186 * Then call "func(fd, info, data, wtr)" on each struct_ops map found.
 187 */
 188static struct res do_search(const char *name, work_func func, void *data,
 189                            struct json_writer *wtr)
 190{
 191        struct bpf_map_info *info;
 192        struct res res = {};
 193        __u32 info_len;
 194        int fd, err;
 195
 196        info = map_info_alloc(&info_len);
 197        if (!info) {
 198                res.nr_errs++;
 199                return res;
 200        }
 201
 202        if (wtr)
 203                jsonw_start_array(wtr);
 204        while ((err = get_next_struct_ops_map(name, &fd, info, info_len)) == 1) {
 205                res.nr_maps++;
 206                err = func(fd, info, data, wtr);
 207                if (err)
 208                        res.nr_errs++;
 209                close(fd);
 210        }
 211        if (wtr)
 212                jsonw_end_array(wtr);
 213
 214        if (err)
 215                res.nr_errs++;
 216
 217        if (!wtr && name && !res.nr_errs && !res.nr_maps)
 218                /* It is not printing empty [].
 219                 * Thus, needs to specifically say nothing found
 220                 * for "name" here.
 221                 */
 222                p_err("no struct_ops found for %s", name);
 223        else if (!wtr && json_output && !res.nr_errs)
 224                /* The "func()" above is not writing any json (i.e. !wtr
 225                 * test here).
 226                 *
 227                 * However, "-j" is enabled and there is no errs here,
 228                 * so call json_null() as the current convention of
 229                 * other cmds.
 230                 */
 231                jsonw_null(json_wtr);
 232
 233        free(info);
 234        return res;
 235}
 236
 237static struct res do_one_id(const char *id_str, work_func func, void *data,
 238                            struct json_writer *wtr)
 239{
 240        struct bpf_map_info *info;
 241        struct res res = {};
 242        unsigned long id;
 243        __u32 info_len;
 244        char *endptr;
 245        int fd;
 246
 247        id = strtoul(id_str, &endptr, 0);
 248        if (*endptr || !id || id > UINT32_MAX) {
 249                p_err("invalid id %s", id_str);
 250                res.nr_errs++;
 251                return res;
 252        }
 253
 254        fd = bpf_map_get_fd_by_id(id);
 255        if (fd == -1) {
 256                p_err("can't get map by id (%lu): %s", id, strerror(errno));
 257                res.nr_errs++;
 258                return res;
 259        }
 260
 261        info = map_info_alloc(&info_len);
 262        if (!info) {
 263                res.nr_errs++;
 264                goto done;
 265        }
 266
 267        if (bpf_obj_get_info_by_fd(fd, info, &info_len)) {
 268                p_err("can't get map info: %s", strerror(errno));
 269                res.nr_errs++;
 270                goto done;
 271        }
 272
 273        if (info->type != BPF_MAP_TYPE_STRUCT_OPS) {
 274                p_err("%s id %u is not a struct_ops map", info->name, info->id);
 275                res.nr_errs++;
 276                goto done;
 277        }
 278
 279        res.nr_maps++;
 280
 281        if (func(fd, info, data, wtr))
 282                res.nr_errs++;
 283        else if (!wtr && json_output)
 284                /* The "func()" above is not writing any json (i.e. !wtr
 285                 * test here).
 286                 *
 287                 * However, "-j" is enabled and there is no errs here,
 288                 * so call json_null() as the current convention of
 289                 * other cmds.
 290                 */
 291                jsonw_null(json_wtr);
 292
 293done:
 294        free(info);
 295        close(fd);
 296
 297        return res;
 298}
 299
 300static struct res do_work_on_struct_ops(const char *search_type,
 301                                        const char *search_term,
 302                                        work_func func, void *data,
 303                                        struct json_writer *wtr)
 304{
 305        if (search_type) {
 306                if (is_prefix(search_type, "id"))
 307                        return do_one_id(search_term, func, data, wtr);
 308                else if (!is_prefix(search_type, "name"))
 309                        usage();
 310        }
 311
 312        return do_search(search_term, func, data, wtr);
 313}
 314
 315static int __do_show(int fd, const struct bpf_map_info *info, void *data,
 316                     struct json_writer *wtr)
 317{
 318        if (wtr) {
 319                jsonw_start_object(wtr);
 320                jsonw_uint_field(wtr, "id", info->id);
 321                jsonw_string_field(wtr, "name", info->name);
 322                jsonw_string_field(wtr, "kernel_struct_ops",
 323                                   get_kern_struct_ops_name(info));
 324                jsonw_end_object(wtr);
 325        } else {
 326                printf("%u: %-15s %-32s\n", info->id, info->name,
 327                       get_kern_struct_ops_name(info));
 328        }
 329
 330        return 0;
 331}
 332
 333static int do_show(int argc, char **argv)
 334{
 335        const char *search_type = NULL, *search_term = NULL;
 336        struct res res;
 337
 338        if (argc && argc != 2)
 339                usage();
 340
 341        if (argc == 2) {
 342                search_type = GET_ARG();
 343                search_term = GET_ARG();
 344        }
 345
 346        res = do_work_on_struct_ops(search_type, search_term, __do_show,
 347                                    NULL, json_wtr);
 348
 349        return cmd_retval(&res, !!search_term);
 350}
 351
 352static int __do_dump(int fd, const struct bpf_map_info *info, void *data,
 353                     struct json_writer *wtr)
 354{
 355        struct btf_dumper *d = (struct btf_dumper *)data;
 356        const struct btf_type *struct_ops_type;
 357        const struct btf *kern_btf = d->btf;
 358        const char *struct_ops_name;
 359        int zero = 0;
 360        void *value;
 361
 362        /* note: d->jw == wtr */
 363
 364        kern_btf = d->btf;
 365
 366        /* The kernel supporting BPF_MAP_TYPE_STRUCT_OPS must have
 367         * btf_vmlinux_value_type_id.
 368         */
 369        struct_ops_type = btf__type_by_id(kern_btf,
 370                                          info->btf_vmlinux_value_type_id);
 371        struct_ops_name = btf__name_by_offset(kern_btf,
 372                                              struct_ops_type->name_off);
 373        value = calloc(1, info->value_size);
 374        if (!value) {
 375                p_err("mem alloc failed");
 376                return -1;
 377        }
 378
 379        if (bpf_map_lookup_elem(fd, &zero, value)) {
 380                p_err("can't lookup struct_ops map %s id %u",
 381                      info->name, info->id);
 382                free(value);
 383                return -1;
 384        }
 385
 386        jsonw_start_object(wtr);
 387        jsonw_name(wtr, "bpf_map_info");
 388        btf_dumper_type(d, map_info_type_id, (void *)info);
 389        jsonw_end_object(wtr);
 390
 391        jsonw_start_object(wtr);
 392        jsonw_name(wtr, struct_ops_name);
 393        btf_dumper_type(d, info->btf_vmlinux_value_type_id, value);
 394        jsonw_end_object(wtr);
 395
 396        free(value);
 397
 398        return 0;
 399}
 400
 401static int do_dump(int argc, char **argv)
 402{
 403        const char *search_type = NULL, *search_term = NULL;
 404        json_writer_t *wtr = json_wtr;
 405        const struct btf *kern_btf;
 406        struct btf_dumper d = {};
 407        struct res res;
 408
 409        if (argc && argc != 2)
 410                usage();
 411
 412        if (argc == 2) {
 413                search_type = GET_ARG();
 414                search_term = GET_ARG();
 415        }
 416
 417        kern_btf = get_btf_vmlinux();
 418        if (IS_ERR(kern_btf))
 419                return -1;
 420
 421        if (!json_output) {
 422                wtr = jsonw_new(stdout);
 423                if (!wtr) {
 424                        p_err("can't create json writer");
 425                        return -1;
 426                }
 427                jsonw_pretty(wtr, true);
 428        }
 429
 430        d.btf = kern_btf;
 431        d.jw = wtr;
 432        d.is_plain_text = !json_output;
 433        d.prog_id_as_func_ptr = true;
 434
 435        res = do_work_on_struct_ops(search_type, search_term, __do_dump, &d,
 436                                    wtr);
 437
 438        if (!json_output)
 439                jsonw_destroy(&wtr);
 440
 441        return cmd_retval(&res, !!search_term);
 442}
 443
 444static int __do_unregister(int fd, const struct bpf_map_info *info, void *data,
 445                           struct json_writer *wtr)
 446{
 447        int zero = 0;
 448
 449        if (bpf_map_delete_elem(fd, &zero)) {
 450                p_err("can't unload %s %s id %u: %s",
 451                      get_kern_struct_ops_name(info), info->name,
 452                      info->id, strerror(errno));
 453                return -1;
 454        }
 455
 456        p_info("Unregistered %s %s id %u",
 457               get_kern_struct_ops_name(info), info->name,
 458               info->id);
 459
 460        return 0;
 461}
 462
 463static int do_unregister(int argc, char **argv)
 464{
 465        const char *search_type, *search_term;
 466        struct res res;
 467
 468        if (argc != 2)
 469                usage();
 470
 471        search_type = GET_ARG();
 472        search_term = GET_ARG();
 473
 474        res = do_work_on_struct_ops(search_type, search_term,
 475                                    __do_unregister, NULL, NULL);
 476
 477        return cmd_retval(&res, true);
 478}
 479
 480static int do_register(int argc, char **argv)
 481{
 482        struct bpf_object_load_attr load_attr = {};
 483        const struct bpf_map_def *def;
 484        struct bpf_map_info info = {};
 485        __u32 info_len = sizeof(info);
 486        int nr_errs = 0, nr_maps = 0;
 487        struct bpf_object *obj;
 488        struct bpf_link *link;
 489        struct bpf_map *map;
 490        const char *file;
 491
 492        if (argc != 1)
 493                usage();
 494
 495        file = GET_ARG();
 496
 497        obj = bpf_object__open(file);
 498        if (IS_ERR_OR_NULL(obj))
 499                return -1;
 500
 501        set_max_rlimit();
 502
 503        load_attr.obj = obj;
 504        if (verifier_logs)
 505                /* log_level1 + log_level2 + stats, but not stable UAPI */
 506                load_attr.log_level = 1 + 2 + 4;
 507
 508        if (bpf_object__load_xattr(&load_attr)) {
 509                bpf_object__close(obj);
 510                return -1;
 511        }
 512
 513        bpf_object__for_each_map(map, obj) {
 514                def = bpf_map__def(map);
 515                if (def->type != BPF_MAP_TYPE_STRUCT_OPS)
 516                        continue;
 517
 518                link = bpf_map__attach_struct_ops(map);
 519                if (IS_ERR(link)) {
 520                        p_err("can't register struct_ops %s: %s",
 521                              bpf_map__name(map),
 522                              strerror(-PTR_ERR(link)));
 523                        nr_errs++;
 524                        continue;
 525                }
 526                nr_maps++;
 527
 528                bpf_link__disconnect(link);
 529                bpf_link__destroy(link);
 530
 531                if (!bpf_obj_get_info_by_fd(bpf_map__fd(map), &info,
 532                                            &info_len))
 533                        p_info("Registered %s %s id %u",
 534                               get_kern_struct_ops_name(&info),
 535                               bpf_map__name(map),
 536                               info.id);
 537                else
 538                        /* Not p_err.  The struct_ops was attached
 539                         * successfully.
 540                         */
 541                        p_info("Registered %s but can't find id: %s",
 542                               bpf_map__name(map), strerror(errno));
 543        }
 544
 545        bpf_object__close(obj);
 546
 547        if (nr_errs)
 548                return -1;
 549
 550        if (!nr_maps) {
 551                p_err("no struct_ops found in %s", file);
 552                return -1;
 553        }
 554
 555        if (json_output)
 556                jsonw_null(json_wtr);
 557
 558        return 0;
 559}
 560
 561static int do_help(int argc, char **argv)
 562{
 563        if (json_output) {
 564                jsonw_null(json_wtr);
 565                return 0;
 566        }
 567
 568        fprintf(stderr,
 569                "Usage: %1$s %2$s { show | list } [STRUCT_OPS_MAP]\n"
 570                "       %1$s %2$s dump [STRUCT_OPS_MAP]\n"
 571                "       %1$s %2$s register OBJ\n"
 572                "       %1$s %2$s unregister STRUCT_OPS_MAP\n"
 573                "       %1$s %2$s help\n"
 574                "\n"
 575                "       STRUCT_OPS_MAP := [ id STRUCT_OPS_MAP_ID | name STRUCT_OPS_MAP_NAME ]\n"
 576                "       " HELP_SPEC_OPTIONS " }\n"
 577                "",
 578                bin_name, argv[-2]);
 579
 580        return 0;
 581}
 582
 583static const struct cmd cmds[] = {
 584        { "show",       do_show },
 585        { "list",       do_show },
 586        { "register",   do_register },
 587        { "unregister", do_unregister },
 588        { "dump",       do_dump },
 589        { "help",       do_help },
 590        { 0 }
 591};
 592
 593int do_struct_ops(int argc, char **argv)
 594{
 595        int err;
 596
 597        err = cmd_select(cmds, argc, argv, do_help);
 598
 599        if (!IS_ERR(btf_vmlinux))
 600                btf__free(btf_vmlinux);
 601
 602        return err;
 603}
 604