History

tools: bpftool: allow reuse of maps with bpftool prog load

Add map parameter to prog load which will allow reuse of existing maps instead of creating new ones. We need feature detection and compat code for reallocarray, since it's not available in many libc versions.

Jakub Kicinski committed 8 years ago

Commit 3ff5a4d1 parent 26736eb5 files · +219 −23Browse files

Unified · Split

5 files
  1. tools/bpf/bpftool/Documentation/bpftool-prog.rst +18 −2
  2. tools/bpf/bpftool/bash-completion/bpftool +61 −6
  3. tools/bpf/bpftool/main.h +3 −0
  4. tools/bpf/bpftool/map.c +2 −2
  5. tools/bpf/bpftool/prog.c +135 −13
tools/bpf/bpftool/Documentation/bpftool-prog.rst+18−2
@@ -24,9 +24,10 @@ MAP COMMANDS · 23 unmodified lines
2424 | **bpftool** **prog dump xlated** *PROG* [{**file** *FILE* | **opcodes** | **visual**}]
2525 | **bpftool** **prog dump jited** *PROG* [{**file** *FILE* | **opcodes**}]
2626 | **bpftool** **prog pin** *PROG* *FILE*
27−| **bpftool** **prog load** *OBJ* *FILE* [**type** *TYPE*] [**dev** *NAME*]
27+| **bpftool** **prog load** *OBJ* *FILE* [**type** *TYPE*] [**map** {**idx** *IDX* | **name** *NAME*} *MAP*] [**dev** *NAME*]
2828 | **bpftool** **prog help**
2929 |
30+| *MAP* := { **id** *MAP_ID* | **pinned** *FILE* }
3031 | *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* }
3132 | *TYPE* := {
3233 | **socket** | **kprobe** | **kretprobe** | **classifier** | **action** |
@@ -73,10 +74,17 @@ DESCRIPTION · 40 unmodified lines
7374
7475 Note: *FILE* must be located in *bpffs* mount.
7576
76− **bpftool prog load** *OBJ* *FILE* [**type** *TYPE*] [**dev** *NAME*]
77+ **bpftool prog load** *OBJ* *FILE* [**type** *TYPE*] [**map** {**idx** *IDX* | **name** *NAME*} *MAP*] [**dev** *NAME*]
7778 Load bpf program from binary *OBJ* and pin as *FILE*.
7879 **type** is optional, if not specified program type will be
7980 inferred from section names.
81+ By default bpftool will create new maps as declared in the ELF
82+ object being loaded. **map** parameter allows for the reuse
83+ of existing maps. It can be specified multiple times, each
84+ time for a different map. *IDX* refers to index of the map
85+ to be replaced in the ELF file counting from 0, while *NAME*
86+ allows to replace a map by name. *MAP* specifies the map to
87+ use, referring to it by **id** or through a **pinned** file.
8088 If **dev** *NAME* is specified program will be loaded onto
8189 given networking device (offload).
8290
@@ -172,6 +180,14 @@ EXAMPLES · 89 unmodified lines
172180 mov %rbx,0x0(%rbp)
173181 48 89 5d 00
174182
183+|
184+| **# bpftool prog load xdp1_kern.o /sys/fs/bpf/xdp1 type xdp map name rxcnt id 7**
185+| **# bpftool prog show pinned /sys/fs/bpf/xdp1**
186+| 9: xdp name xdp_prog1 tag 539ec6ce11b52f98 gpl
187+| loaded_at 2018-06-25T16:17:31-0700 uid 0
188+| xlated 488B jited 336B memlock 4096B map_ids 7
189+| **# rm /sys/fs/bpf/xdp1**
190+|
175191
176192 SEE ALSO
177193 ========
tools/bpf/bpftool/bash-completion/bpftool+61−6
@@ -99,6 +99,29 @@ _bpftool_get_prog_tags() · 98 unmodified lines
9999 command sed -n 's/.*"tag": "\(.*\)",$/\1/p' )" -- "$cur" ) )
100100 }
101101
102+_bpftool_get_obj_map_names()
103+{
104+ local obj
105+
106+ obj=$1
107+
108+ maps=$(objdump -j maps -t $obj 2>/dev/null | \
109+ command awk '/g . maps/ {print $NF}')
110+
111+ COMPREPLY+=( $( compgen -W "$maps" -- "$cur" ) )
112+}
113+
114+_bpftool_get_obj_map_idxs()
115+{
116+ local obj
117+
118+ obj=$1
119+
120+ nmaps=$(objdump -j maps -t $obj 2>/dev/null | grep -c 'g . maps')
121+
122+ COMPREPLY+=( $( compgen -W "$(seq 0 $((nmaps - 1)))" -- "$cur" ) )
123+}
124+
102125 _sysfs_get_netdevs()
103126 {
104127 COMPREPLY+=( $( compgen -W "$( ls /sys/class/net 2>/dev/null )" -- \
@@ -220,12 +243,14 @@ _bpftool() · 115 unmodified lines
220243 # Completion depends on object and command in use
221244 case $object in
222245 prog)
223− case $prev in
224− id)
225− _bpftool_get_prog_ids
226− return 0
227− ;;
228− esac
246+ if [[ $command != "load" ]]; then
247+ case $prev in
248+ id)
249+ _bpftool_get_prog_ids
250+ return 0
251+ ;;
252+ esac
253+ fi
229254
230255 local PROG_TYPE='id pinned tag'
231256 case $command in
@@ -268,22 +293,52 @@ _bpftool() · 36 unmodified lines
268293 return 0
269294 ;;
270295 load)
296+ local obj
297+
271298 if [[ ${#words[@]} -lt 6 ]]; then
272299 _filedir
273300 return 0
274301 fi
275302
303+ obj=${words[3]}
304+
305+ if [[ ${words[-4]} == "map" ]]; then
306+ COMPREPLY=( $( compgen -W "id pinned" -- "$cur" ) )
307+ return 0
308+ fi
309+ if [[ ${words[-3]} == "map" ]]; then
310+ if [[ ${words[-2]} == "idx" ]]; then
311+ _bpftool_get_obj_map_idxs $obj
312+ elif [[ ${words[-2]} == "name" ]]; then
313+ _bpftool_get_obj_map_names $obj
314+ fi
315+ return 0
316+ fi
317+ if [[ ${words[-2]} == "map" ]]; then
318+ COMPREPLY=( $( compgen -W "idx name" -- "$cur" ) )
319+ return 0
320+ fi
321+
276322 case $prev in
277323 type)
278324 COMPREPLY=( $( compgen -W "socket kprobe kretprobe classifier action tracepoint raw_tracepoint xdp perf_event cgroup/skb cgroup/sock cgroup/dev lwt_in lwt_out lwt_xmit lwt_seg6local sockops sk_skb sk_msg lirc_mode2 cgroup/bind4 cgroup/bind6 cgroup/connect4 cgroup/connect6 cgroup/sendmsg4 cgroup/sendmsg6 cgroup/post_bind4 cgroup/post_bind6" -- \
279325 "$cur" ) )
280326 return 0
281327 ;;
328+ id)
329+ _bpftool_get_map_ids
330+ return 0
331+ ;;
332+ pinned)
333+ _filedir
334+ return 0
335+ ;;
282336 dev)
283337 _sysfs_get_netdevs
284338 return 0
285339 ;;
286340 *)
341+ COMPREPLY=( $( compgen -W "map" -- "$cur" ) )
287342 _bpftool_once_attr 'type'
288343 _bpftool_once_attr 'dev'
289344 return 0
tools/bpf/bpftool/main.h+3−0
@@ -75,6 +75,8 @@ · 74 unmodified lines
7575 "PROG := { id PROG_ID | pinned FILE | tag PROG_TAG }"
7676 #define HELP_SPEC_OPTIONS \
7777 "OPTIONS := { {-j|--json} [{-p|--pretty}] | {-f|--bpffs} }"
78+#define HELP_SPEC_MAP \
79+ "MAP := { id MAP_ID | pinned FILE }"
7880
7981 enum bpf_obj_type {
8082 BPF_OBJ_UNKNOWN,
@@ -136,6 +138,7 @@ int do_cgroup(int argc, char **arg); · 55 unmodified lines
136138 int do_perf(int argc, char **arg);
137139
138140 int prog_parse_fd(int *argc, char ***argv);
141+int map_parse_fd(int *argc, char ***argv);
139142 int map_parse_fd_and_info(int *argc, char ***argv, void *info, __u32 *info_len);
140143
141144 void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,
tools/bpf/bpftool/map.c+2−2
@@ -93,7 +93,7 @@ static void *alloc_value(struct bpf_map_info *info) · 92 unmodified lines
9393 return malloc(info->value_size);
9494 }
9595
96−static int map_parse_fd(int *argc, char ***argv)
96+int map_parse_fd(int *argc, char ***argv)
9797 {
9898 int fd;
9999
@@ -824,7 +824,7 @@ static int do_help(int argc, char **argv) · 724 unmodified lines
824824 " %s %s event_pipe MAP [cpu N index M]\n"
825825 " %s %s help\n"
826826 "\n"
827− " MAP := { id MAP_ID | pinned FILE }\n"
827+ " " HELP_SPEC_MAP "\n"
828828 " DATA := { [hex] BYTES }\n"
829829 " " HELP_SPEC_PROGRAM "\n"
830830 " VALUE := { DATA | MAP | PROG }\n"
tools/bpf/bpftool/prog.c+135−13
@@ -31,6 +31,7 @@ · 30 unmodified lines
3131 * SOFTWARE.
3232 */
3333
34+#define _GNU_SOURCE
3435 #include <errno.h>
3536 #include <fcntl.h>
3637 #include <stdarg.h>
@@ -682,18 +683,34 @@ static int do_pin(int argc, char **argv) · 645 unmodified lines
682683 return err;
683684 }
684685
686+struct map_replace {
687+ int idx;
688+ int fd;
689+ char *name;
690+};
691+
692+int map_replace_compar(const void *p1, const void *p2)
693+{
694+ const struct map_replace *a = p1, *b = p2;
695+
696+ return a->idx - b->idx;
697+}
698+
685699 static int do_load(int argc, char **argv)
686700 {
687701 enum bpf_attach_type expected_attach_type;
688702 struct bpf_object_open_attr attr = {
689703 .prog_type = BPF_PROG_TYPE_UNSPEC,
690704 };
705+ struct map_replace *map_replace = NULL;
706+ unsigned int old_map_fds = 0;
691707 struct bpf_program *prog;
692708 struct bpf_object *obj;
693709 struct bpf_map *map;
694710 const char *pinfile;
711+ unsigned int i, j;
695712 __u32 ifindex = 0;
696− int err;
713+ int idx, err;
697714
698715 if (!REQ_ARGS(2))
699716 return -1;
@@ -708,16 +725,16 @@ static int do_load(int argc, char **argv) · 8 unmodified lines
708725
709726 if (attr.prog_type != BPF_PROG_TYPE_UNSPEC) {
710727 p_err("program type already specified");
711− return -1;
728+ goto err_free_reuse_maps;
712729 }
713730 if (!REQ_ARGS(1))
714− return -1;
731+ goto err_free_reuse_maps;
715732
716733 /* Put a '/' at the end of type to appease libbpf */
717734 type = malloc(strlen(*argv) + 2);
718735 if (!type) {
719736 p_err("mem alloc failed");
720− return -1;
737+ goto err_free_reuse_maps;
721738 }
722739 *type = 0;
723740 strcat(type, *argv);
@@ -728,37 +745,81 @@ static int do_load(int argc, char **argv) · 4 unmodified lines
728745 free(type);
729746 if (err < 0) {
730747 p_err("unknown program type '%s'", *argv);
731− return err;
748+ goto err_free_reuse_maps;
732749 }
733750 NEXT_ARG();
751+ } else if (is_prefix(*argv, "map")) {
752+ char *endptr, *name;
753+ int fd;
754+
755+ NEXT_ARG();
756+
757+ if (!REQ_ARGS(4))
758+ goto err_free_reuse_maps;
759+
760+ if (is_prefix(*argv, "idx")) {
761+ NEXT_ARG();
762+
763+ idx = strtoul(*argv, &endptr, 0);
764+ if (*endptr) {
765+ p_err("can't parse %s as IDX", *argv);
766+ goto err_free_reuse_maps;
767+ }
768+ name = NULL;
769+ } else if (is_prefix(*argv, "name")) {
770+ NEXT_ARG();
771+
772+ name = *argv;
773+ idx = -1;
774+ } else {
775+ p_err("expected 'idx' or 'name', got: '%s'?",
776+ *argv);
777+ goto err_free_reuse_maps;
778+ }
779+ NEXT_ARG();
780+
781+ fd = map_parse_fd(&argc, &argv);
782+ if (fd < 0)
783+ goto err_free_reuse_maps;
784+
785+ map_replace = reallocarray(map_replace, old_map_fds + 1,
786+ sizeof(*map_replace));
787+ if (!map_replace) {
788+ p_err("mem alloc failed");
789+ goto err_free_reuse_maps;
790+ }
791+ map_replace[old_map_fds].idx = idx;
792+ map_replace[old_map_fds].name = name;
793+ map_replace[old_map_fds].fd = fd;
794+ old_map_fds++;
734795 } else if (is_prefix(*argv, "dev")) {
735796 NEXT_ARG();
736797
737798 if (ifindex) {
738799 p_err("offload device already specified");
739− return -1;
800+ goto err_free_reuse_maps;
740801 }
741802 if (!REQ_ARGS(1))
742− return -1;
803+ goto err_free_reuse_maps;
743804
744805 ifindex = if_nametoindex(*argv);
745806 if (!ifindex) {
746807 p_err("unrecognized netdevice '%s': %s",
747808 *argv, strerror(errno));
748− return -1;
809+ goto err_free_reuse_maps;
749810 }
750811 NEXT_ARG();
751812 } else {
752− p_err("expected no more arguments, 'type' or 'dev', got: '%s'?",
813+ p_err("expected no more arguments, 'type', 'map' or 'dev', got: '%s'?",
753814 *argv);
754− return -1;
815+ goto err_free_reuse_maps;
755816 }
756817 }
757818
758819 obj = bpf_object__open_xattr(&attr);
759820 if (IS_ERR_OR_NULL(obj)) {
760821 p_err("failed to open object file");
761− return -1;
822+ goto err_free_reuse_maps;
762823 }
763824
764825 prog = bpf_program__next(NULL, obj);
@@ -782,10 +843,62 @@ static int do_load(int argc, char **argv) · 17 unmodified lines
782843 bpf_program__set_type(prog, attr.prog_type);
783844 bpf_program__set_expected_attach_type(prog, expected_attach_type);
784845
785− bpf_map__for_each(map, obj)
846+ qsort(map_replace, old_map_fds, sizeof(*map_replace),
847+ map_replace_compar);
848+
849+ /* After the sort maps by name will be first on the list, because they
850+ * have idx == -1. Resolve them.
851+ */
852+ j = 0;
853+ while (j < old_map_fds && map_replace[j].name) {
854+ i = 0;
855+ bpf_map__for_each(map, obj) {
856+ if (!strcmp(bpf_map__name(map), map_replace[j].name)) {
857+ map_replace[j].idx = i;
858+ break;
859+ }
860+ i++;
861+ }
862+ if (map_replace[j].idx == -1) {
863+ p_err("unable to find map '%s'", map_replace[j].name);
864+ goto err_close_obj;
865+ }
866+ j++;
867+ }
868+ /* Resort if any names were resolved */
869+ if (j)
870+ qsort(map_replace, old_map_fds, sizeof(*map_replace),
871+ map_replace_compar);
872+
873+ /* Set ifindex and name reuse */
874+ j = 0;
875+ idx = 0;
876+ bpf_map__for_each(map, obj) {
786877 if (!bpf_map__is_offload_neutral(map))
787878 bpf_map__set_ifindex(map, ifindex);
788879
880+ if (j < old_map_fds && idx == map_replace[j].idx) {
881+ err = bpf_map__reuse_fd(map, map_replace[j++].fd);
882+ if (err) {
883+ p_err("unable to set up map reuse: %d", err);
884+ goto err_close_obj;
885+ }
886+
887+ /* Next reuse wants to apply to the same map */
888+ if (j < old_map_fds && map_replace[j].idx == idx) {
889+ p_err("replacement for map idx %d specified more than once",
890+ idx);
891+ goto err_close_obj;
892+ }
893+ }
894+
895+ idx++;
896+ }
897+ if (j < old_map_fds) {
898+ p_err("map idx '%d' not used", map_replace[j].idx);
899+ goto err_close_obj;
900+ }
901+
789902 err = bpf_object__load(obj);
790903 if (err) {
791904 p_err("failed to load object file");
@@ -799,11 +912,18 @@ static int do_load(int argc, char **argv) · 7 unmodified lines
799912 jsonw_null(json_wtr);
800913
801914 bpf_object__close(obj);
915+ for (i = 0; i < old_map_fds; i++)
916+ close(map_replace[i].fd);
917+ free(map_replace);
802918
803919 return 0;
804920
805921 err_close_obj:
806922 bpf_object__close(obj);
923+err_free_reuse_maps:
924+ for (i = 0; i < old_map_fds; i++)
925+ close(map_replace[i].fd);
926+ free(map_replace);
807927 return -1;
808928 }
809929
@@ -819,9 +939,11 @@ static int do_help(int argc, char **argv) · 9 unmodified lines
819939 " %s %s dump xlated PROG [{ file FILE | opcodes | visual }]\n"
820940 " %s %s dump jited PROG [{ file FILE | opcodes }]\n"
821941 " %s %s pin PROG FILE\n"
822− " %s %s load OBJ FILE [type TYPE] [dev NAME]\n"
942+ " %s %s load OBJ FILE [type TYPE] [dev NAME] \\\n"
943+ " [map { idx IDX | name NAME } MAP]\n"
823944 " %s %s help\n"
824945 "\n"
946+ " " HELP_SPEC_MAP "\n"
825947 " " HELP_SPEC_PROGRAM "\n"
826948 " TYPE := { socket | kprobe | kretprobe | classifier | action |\n"
827949 " tracepoint | raw_tracepoint | xdp | perf_event | cgroup/skb |\n"