Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge branch 'bpf-bpftool-queue-stack'

Stanislav Fomichev says:

====================
This patch series add support for queue/stack manipulations.

It goes like this:

commands by permitting empty keys.

v2:
* removed unneeded jsonw_null from patch #6
* improved bash completions (and moved them into separate patch #7)
====================

Reviewed-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>

+254 -90
+24 -4
tools/bpf/bpftool/Documentation/bpftool-map.rst
··· 25 25 | **bpftool** **map create** *FILE* **type** *TYPE* **key** *KEY_SIZE* **value** *VALUE_SIZE* \ 26 26 | **entries** *MAX_ENTRIES* **name** *NAME* [**flags** *FLAGS*] [**dev** *NAME*] 27 27 | **bpftool** **map dump** *MAP* 28 - | **bpftool** **map update** *MAP* **key** *DATA* **value** *VALUE* [*UPDATE_FLAGS*] 29 - | **bpftool** **map lookup** *MAP* **key** *DATA* 28 + | **bpftool** **map update** *MAP* [**key** *DATA*] [**value** *VALUE*] [*UPDATE_FLAGS*] 29 + | **bpftool** **map lookup** *MAP* [**key** *DATA*] 30 30 | **bpftool** **map getnext** *MAP* [**key** *DATA*] 31 31 | **bpftool** **map delete** *MAP* **key** *DATA* 32 32 | **bpftool** **map pin** *MAP* *FILE* 33 33 | **bpftool** **map event_pipe** *MAP* [**cpu** *N* **index** *M*] 34 + | **bpftool** **map peek** *MAP* 35 + | **bpftool** **map push** *MAP* **value** *VALUE* 36 + | **bpftool** **map pop** *MAP* 37 + | **bpftool** **map enqueue** *MAP* **value** *VALUE* 38 + | **bpftool** **map dequeue** *MAP* 34 39 | **bpftool** **map help** 35 40 | 36 41 | *MAP* := { **id** *MAP_ID* | **pinned** *FILE* } ··· 67 62 **bpftool map dump** *MAP* 68 63 Dump all entries in a given *MAP*. 69 64 70 - **bpftool map update** *MAP* **key** *DATA* **value** *VALUE* [*UPDATE_FLAGS*] 65 + **bpftool map update** *MAP* [**key** *DATA*] [**value** *VALUE*] [*UPDATE_FLAGS*] 71 66 Update map entry for a given *KEY*. 72 67 73 68 *UPDATE_FLAGS* can be one of: **any** update existing entry ··· 80 75 the bytes are parsed as decimal values, unless a "0x" prefix 81 76 (for hexadecimal) or a "0" prefix (for octal) is provided. 82 77 83 - **bpftool map lookup** *MAP* **key** *DATA* 78 + **bpftool map lookup** *MAP* [**key** *DATA*] 84 79 Lookup **key** in the map. 85 80 86 81 **bpftool map getnext** *MAP* [**key** *DATA*] ··· 111 106 Note that installing a perf ring into an array will silently 112 107 replace any existing ring. Any other application will stop 113 108 receiving events if it installed its rings earlier. 109 + 110 + **bpftool map peek** *MAP* 111 + Peek next **value** in the queue or stack. 112 + 113 + **bpftool map push** *MAP* **value** *VALUE* 114 + Push **value** onto the stack. 115 + 116 + **bpftool map pop** *MAP* 117 + Pop and print **value** from the stack. 118 + 119 + **bpftool map enqueue** *MAP* **value** *VALUE* 120 + Enqueue **value** into the queue. 121 + 122 + **bpftool map dequeue** *MAP* 123 + Dequeue and print **value** from the queue. 114 124 115 125 **bpftool map help** 116 126 Print short help message.
+73 -18
tools/bpf/bpftool/bash-completion/bpftool
··· 50 50 command sed -n 's/.*"id": \(.*\),$/\1/p' )" -- "$cur" ) ) 51 51 } 52 52 53 - _bpftool_get_perf_map_ids() 53 + # Takes map type and adds matching map ids to the list of suggestions. 54 + _bpftool_get_map_ids_for_type() 54 55 { 56 + local type="$1" 55 57 COMPREPLY+=( $( compgen -W "$( bpftool -jp map 2>&1 | \ 56 - command grep -C2 perf_event_array | \ 58 + command grep -C2 "$type" | \ 57 59 command sed -n 's/.*"id": \(.*\),$/\1/p' )" -- "$cur" ) ) 58 60 } 59 - 60 61 61 62 _bpftool_get_prog_ids() 62 63 { ··· 100 99 "$cur" ) ) 101 100 } 102 101 103 - # For bpftool map update: retrieve type of the map to update. 104 - _bpftool_map_update_map_type() 102 + # Retrieve type of the map that we are operating on. 103 + _bpftool_map_guess_map_type() 105 104 { 106 105 local keyword ref 107 106 for (( idx=3; idx < ${#words[@]}-1; idx++ )); do 108 - if [[ ${words[$((idx-2))]} == "update" ]]; then 109 - keyword=${words[$((idx-1))]} 110 - ref=${words[$((idx))]} 111 - fi 107 + case "${words[$((idx-2))]}" in 108 + lookup|update) 109 + keyword=${words[$((idx-1))]} 110 + ref=${words[$((idx))]} 111 + ;; 112 + push) 113 + printf "stack" 114 + return 0 115 + ;; 116 + enqueue) 117 + printf "queue" 118 + return 0 119 + ;; 120 + esac 112 121 done 113 122 [[ -z $ref ]] && return 0 114 123 ··· 130 119 131 120 _bpftool_map_update_get_id() 132 121 { 122 + local command="$1" 123 + 133 124 # Is it the map to update, or a map to insert into the map to update? 134 125 # Search for "value" keyword. 135 126 local idx value ··· 141 128 break 142 129 fi 143 130 done 144 - [[ $value -eq 0 ]] && _bpftool_get_map_ids && return 0 131 + if [[ $value -eq 0 ]]; then 132 + case "$command" in 133 + push) 134 + _bpftool_get_map_ids_for_type stack 135 + ;; 136 + enqueue) 137 + _bpftool_get_map_ids_for_type queue 138 + ;; 139 + *) 140 + _bpftool_get_map_ids 141 + ;; 142 + esac 143 + return 0 144 + fi 145 145 146 146 # Id to complete is for a value. It can be either prog id or map id. This 147 147 # depends on the type of the map to update. 148 - local type=$(_bpftool_map_update_map_type) 148 + local type=$(_bpftool_map_guess_map_type) 149 149 case $type in 150 150 array_of_maps|hash_of_maps) 151 151 _bpftool_get_map_ids ··· 408 382 map) 409 383 local MAP_TYPE='id pinned' 410 384 case $command in 411 - show|list|dump) 385 + show|list|dump|peek|pop|dequeue) 412 386 case $prev in 413 387 $command) 414 388 COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) ) 415 389 return 0 416 390 ;; 417 391 id) 418 - _bpftool_get_map_ids 392 + case "$command" in 393 + peek) 394 + _bpftool_get_map_ids_for_type stack 395 + _bpftool_get_map_ids_for_type queue 396 + ;; 397 + pop) 398 + _bpftool_get_map_ids_for_type stack 399 + ;; 400 + dequeue) 401 + _bpftool_get_map_ids_for_type queue 402 + ;; 403 + *) 404 + _bpftool_get_map_ids 405 + ;; 406 + esac 419 407 return 0 420 408 ;; 421 409 *) ··· 487 447 COMPREPLY+=( $( compgen -W 'hex' -- "$cur" ) ) 488 448 ;; 489 449 *) 450 + case $(_bpftool_map_guess_map_type) in 451 + queue|stack) 452 + return 0 453 + ;; 454 + esac 455 + 490 456 _bpftool_once_attr 'key' 491 457 return 0 492 458 ;; 493 459 esac 494 460 ;; 495 - update) 461 + update|push|enqueue) 496 462 case $prev in 497 463 $command) 498 464 COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) ) 499 465 return 0 500 466 ;; 501 467 id) 502 - _bpftool_map_update_get_id 468 + _bpftool_map_update_get_id $command 503 469 return 0 504 470 ;; 505 471 key) ··· 514 468 value) 515 469 # We can have bytes, or references to a prog or a 516 470 # map, depending on the type of the map to update. 517 - case $(_bpftool_map_update_map_type) in 471 + case "$(_bpftool_map_guess_map_type)" in 518 472 array_of_maps|hash_of_maps) 519 473 local MAP_TYPE='id pinned' 520 474 COMPREPLY+=( $( compgen -W "$MAP_TYPE" \ ··· 536 490 return 0 537 491 ;; 538 492 *) 493 + case $(_bpftool_map_guess_map_type) in 494 + queue|stack) 495 + _bpftool_once_attr 'value' 496 + return 0; 497 + ;; 498 + esac 499 + 539 500 _bpftool_once_attr 'key' 540 501 local UPDATE_FLAGS='any exist noexist' 541 502 for (( idx=3; idx < ${#words[@]}-1; idx++ )); do ··· 561 508 return 0 562 509 fi 563 510 done 511 + 564 512 return 0 565 513 ;; 566 514 esac ··· 581 527 return 0 582 528 ;; 583 529 id) 584 - _bpftool_get_perf_map_ids 530 + _bpftool_get_map_ids_for_type perf_event_array 585 531 return 0 586 532 ;; 587 533 cpu) ··· 600 546 *) 601 547 [[ $prev == $object ]] && \ 602 548 COMPREPLY=( $( compgen -W 'delete dump getnext help \ 603 - lookup pin event_pipe show list update create' -- \ 549 + lookup pin event_pipe show list update create \ 550 + peek push enqueue pop dequeue' -- \ 604 551 "$cur" ) ) 605 552 ;; 606 553 esac
+157 -68
tools/bpf/bpftool/map.c
··· 285 285 single_line = info->key_size + info->value_size <= 24 && 286 286 !break_names; 287 287 288 - printf("key:%c", break_names ? '\n' : ' '); 289 - fprint_hex(stdout, key, info->key_size, " "); 288 + if (info->key_size) { 289 + printf("key:%c", break_names ? '\n' : ' '); 290 + fprint_hex(stdout, key, info->key_size, " "); 290 291 291 - printf(single_line ? " " : "\n"); 292 + printf(single_line ? " " : "\n"); 293 + } 292 294 293 - printf("value:%c", break_names ? '\n' : ' '); 294 - if (value) 295 - fprint_hex(stdout, value, info->value_size, " "); 296 - else 297 - printf("<no entry>"); 295 + if (info->value_size) { 296 + printf("value:%c", break_names ? '\n' : ' '); 297 + if (value) 298 + fprint_hex(stdout, value, info->value_size, 299 + " "); 300 + else 301 + printf("<no entry>"); 302 + } 298 303 299 304 printf("\n"); 300 305 } else { ··· 308 303 n = get_possible_cpus(); 309 304 step = round_up(info->value_size, 8); 310 305 311 - printf("key:\n"); 312 - fprint_hex(stdout, key, info->key_size, " "); 313 - printf("\n"); 314 - for (i = 0; i < n; i++) { 315 - printf("value (CPU %02d):%c", 316 - i, info->value_size > 16 ? '\n' : ' '); 317 - if (value) 318 - fprint_hex(stdout, value + i * step, 319 - info->value_size, " "); 320 - else 321 - printf("<no entry>"); 306 + if (info->key_size) { 307 + printf("key:\n"); 308 + fprint_hex(stdout, key, info->key_size, " "); 322 309 printf("\n"); 310 + } 311 + if (info->value_size) { 312 + for (i = 0; i < n; i++) { 313 + printf("value (CPU %02d):%c", 314 + i, info->value_size > 16 ? '\n' : ' '); 315 + if (value) 316 + fprint_hex(stdout, value + i * step, 317 + info->value_size, " "); 318 + else 319 + printf("<no entry>"); 320 + printf("\n"); 321 + } 323 322 } 324 323 } 325 324 } ··· 788 779 return err; 789 780 } 790 781 782 + static int alloc_key_value(struct bpf_map_info *info, void **key, void **value) 783 + { 784 + *key = NULL; 785 + *value = NULL; 786 + 787 + if (info->key_size) { 788 + *key = malloc(info->key_size); 789 + if (!*key) { 790 + p_err("key mem alloc failed"); 791 + return -1; 792 + } 793 + } 794 + 795 + if (info->value_size) { 796 + *value = alloc_value(info); 797 + if (!*value) { 798 + p_err("value mem alloc failed"); 799 + free(*key); 800 + *key = NULL; 801 + return -1; 802 + } 803 + } 804 + 805 + return 0; 806 + } 807 + 791 808 static int do_update(int argc, char **argv) 792 809 { 793 810 struct bpf_map_info info = {}; ··· 830 795 if (fd < 0) 831 796 return -1; 832 797 833 - key = malloc(info.key_size); 834 - value = alloc_value(&info); 835 - if (!key || !value) { 836 - p_err("mem alloc failed"); 837 - err = -1; 798 + err = alloc_key_value(&info, &key, &value); 799 + if (err) 838 800 goto exit_free; 839 - } 840 801 841 802 err = parse_elem(argv, &info, key, value, info.key_size, 842 803 info.value_size, &flags, &value_fd); ··· 857 826 return err; 858 827 } 859 828 829 + static void print_key_value(struct bpf_map_info *info, void *key, 830 + void *value) 831 + { 832 + json_writer_t *btf_wtr; 833 + struct btf *btf = NULL; 834 + int err; 835 + 836 + err = btf__get_from_id(info->btf_id, &btf); 837 + if (err) { 838 + p_err("failed to get btf"); 839 + return; 840 + } 841 + 842 + if (json_output) { 843 + print_entry_json(info, key, value, btf); 844 + } else if (btf) { 845 + /* if here json_wtr wouldn't have been initialised, 846 + * so let's create separate writer for btf 847 + */ 848 + btf_wtr = get_btf_writer(); 849 + if (!btf_wtr) { 850 + p_info("failed to create json writer for btf. falling back to plain output"); 851 + btf__free(btf); 852 + btf = NULL; 853 + print_entry_plain(info, key, value); 854 + } else { 855 + struct btf_dumper d = { 856 + .btf = btf, 857 + .jw = btf_wtr, 858 + .is_plain_text = true, 859 + }; 860 + 861 + do_dump_btf(&d, info, key, value); 862 + jsonw_destroy(&btf_wtr); 863 + } 864 + } else { 865 + print_entry_plain(info, key, value); 866 + } 867 + btf__free(btf); 868 + } 869 + 860 870 static int do_lookup(int argc, char **argv) 861 871 { 862 872 struct bpf_map_info info = {}; 863 873 __u32 len = sizeof(info); 864 - json_writer_t *btf_wtr; 865 - struct btf *btf = NULL; 866 874 void *key, *value; 867 875 int err; 868 876 int fd; ··· 913 843 if (fd < 0) 914 844 return -1; 915 845 916 - key = malloc(info.key_size); 917 - value = alloc_value(&info); 918 - if (!key || !value) { 919 - p_err("mem alloc failed"); 920 - err = -1; 846 + err = alloc_key_value(&info, &key, &value); 847 + if (err) 921 848 goto exit_free; 922 - } 923 849 924 850 err = parse_elem(argv, &info, key, NULL, info.key_size, 0, NULL, NULL); 925 851 if (err) ··· 939 873 } 940 874 941 875 /* here means bpf_map_lookup_elem() succeeded */ 942 - err = btf__get_from_id(info.btf_id, &btf); 943 - if (err) { 944 - p_err("failed to get btf"); 945 - goto exit_free; 946 - } 947 - 948 - if (json_output) { 949 - print_entry_json(&info, key, value, btf); 950 - } else if (btf) { 951 - /* if here json_wtr wouldn't have been initialised, 952 - * so let's create separate writer for btf 953 - */ 954 - btf_wtr = get_btf_writer(); 955 - if (!btf_wtr) { 956 - p_info("failed to create json writer for btf. falling back to plain output"); 957 - btf__free(btf); 958 - btf = NULL; 959 - print_entry_plain(&info, key, value); 960 - } else { 961 - struct btf_dumper d = { 962 - .btf = btf, 963 - .jw = btf_wtr, 964 - .is_plain_text = true, 965 - }; 966 - 967 - do_dump_btf(&d, &info, key, value); 968 - jsonw_destroy(&btf_wtr); 969 - } 970 - } else { 971 - print_entry_plain(&info, key, value); 972 - } 876 + print_key_value(&info, key, value); 973 877 974 878 exit_free: 975 879 free(key); 976 880 free(value); 977 881 close(fd); 978 - btf__free(btf); 979 882 980 883 return err; 981 884 } ··· 1157 1122 return 0; 1158 1123 } 1159 1124 1125 + static int do_pop_dequeue(int argc, char **argv) 1126 + { 1127 + struct bpf_map_info info = {}; 1128 + __u32 len = sizeof(info); 1129 + void *key, *value; 1130 + int err; 1131 + int fd; 1132 + 1133 + if (argc < 2) 1134 + usage(); 1135 + 1136 + fd = map_parse_fd_and_info(&argc, &argv, &info, &len); 1137 + if (fd < 0) 1138 + return -1; 1139 + 1140 + err = alloc_key_value(&info, &key, &value); 1141 + if (err) 1142 + goto exit_free; 1143 + 1144 + err = bpf_map_lookup_and_delete_elem(fd, key, value); 1145 + if (err) { 1146 + if (errno == ENOENT) { 1147 + if (json_output) 1148 + jsonw_null(json_wtr); 1149 + else 1150 + printf("Error: empty map\n"); 1151 + } else { 1152 + p_err("pop failed: %s", strerror(errno)); 1153 + } 1154 + 1155 + goto exit_free; 1156 + } 1157 + 1158 + print_key_value(&info, key, value); 1159 + 1160 + exit_free: 1161 + free(key); 1162 + free(value); 1163 + close(fd); 1164 + 1165 + return err; 1166 + } 1167 + 1160 1168 static int do_help(int argc, char **argv) 1161 1169 { 1162 1170 if (json_output) { ··· 1213 1135 " entries MAX_ENTRIES name NAME [flags FLAGS] \\\n" 1214 1136 " [dev NAME]\n" 1215 1137 " %s %s dump MAP\n" 1216 - " %s %s update MAP key DATA value VALUE [UPDATE_FLAGS]\n" 1217 - " %s %s lookup MAP key DATA\n" 1138 + " %s %s update MAP [key DATA] [value VALUE] [UPDATE_FLAGS]\n" 1139 + " %s %s lookup MAP [key DATA]\n" 1218 1140 " %s %s getnext MAP [key DATA]\n" 1219 1141 " %s %s delete MAP key DATA\n" 1220 1142 " %s %s pin MAP FILE\n" 1221 1143 " %s %s event_pipe MAP [cpu N index M]\n" 1144 + " %s %s peek MAP\n" 1145 + " %s %s push MAP value VALUE\n" 1146 + " %s %s pop MAP\n" 1147 + " %s %s enqueue MAP value VALUE\n" 1148 + " %s %s dequeue MAP\n" 1222 1149 " %s %s help\n" 1223 1150 "\n" 1224 1151 " " HELP_SPEC_MAP "\n" ··· 1241 1158 bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], 1242 1159 bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], 1243 1160 bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], 1244 - bin_name, argv[-2]); 1161 + bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], 1162 + bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2]); 1245 1163 1246 1164 return 0; 1247 1165 } ··· 1259 1175 { "pin", do_pin }, 1260 1176 { "event_pipe", do_event_pipe }, 1261 1177 { "create", do_create }, 1178 + { "peek", do_lookup }, 1179 + { "push", do_update }, 1180 + { "enqueue", do_update }, 1181 + { "pop", do_pop_dequeue }, 1182 + { "dequeue", do_pop_dequeue }, 1262 1183 { 0 } 1263 1184 }; 1264 1185