struct_ops.c 14 KB

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