struct_ops.c 13 KB

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