Commit 4edc01b8 authored by Daniel Borkmann's avatar Daniel Borkmann
Browse files

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: default avatarJakub Kicinski <jakub.kicinski@netronome.com>
Signed-off-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
parents e13279e2 55c70bff
Loading
Loading
Loading
Loading
+24 −4
Original line number Original line Diff line number Diff line
@@ -25,12 +25,17 @@ MAP COMMANDS
|	**bpftool** **map create**     *FILE* **type** *TYPE* **key** *KEY_SIZE* **value** *VALUE_SIZE* \
|	**bpftool** **map create**     *FILE* **type** *TYPE* **key** *KEY_SIZE* **value** *VALUE_SIZE* \
|		**entries** *MAX_ENTRIES* **name** *NAME* [**flags** *FLAGS*] [**dev** *NAME*]
|		**entries** *MAX_ENTRIES* **name** *NAME* [**flags** *FLAGS*] [**dev** *NAME*]
|	**bpftool** **map dump**       *MAP*
|	**bpftool** **map dump**       *MAP*
|	**bpftool** **map update**     *MAP*  **key** *DATA*   **value** *VALUE* [*UPDATE_FLAGS*]
|	**bpftool** **map update**     *MAP* [**key** *DATA*] [**value** *VALUE*] [*UPDATE_FLAGS*]
|	**bpftool** **map lookup**     *MAP*  **key** *DATA*
|	**bpftool** **map lookup**     *MAP* [**key** *DATA*]
|	**bpftool** **map getnext**    *MAP* [**key** *DATA*]
|	**bpftool** **map getnext**    *MAP* [**key** *DATA*]
|	**bpftool** **map delete**     *MAP*  **key** *DATA*
|	**bpftool** **map delete**     *MAP*  **key** *DATA*
|	**bpftool** **map pin**        *MAP*  *FILE*
|	**bpftool** **map pin**        *MAP*  *FILE*
|	**bpftool** **map event_pipe** *MAP* [**cpu** *N* **index** *M*]
|	**bpftool** **map event_pipe** *MAP* [**cpu** *N* **index** *M*]
|	**bpftool** **map peek**       *MAP*
|	**bpftool** **map push**       *MAP* **value** *VALUE*
|	**bpftool** **map pop**        *MAP*
|	**bpftool** **map enqueue**    *MAP* **value** *VALUE*
|	**bpftool** **map dequeue**    *MAP*
|	**bpftool** **map help**
|	**bpftool** **map help**
|
|
|	*MAP* := { **id** *MAP_ID* | **pinned** *FILE* }
|	*MAP* := { **id** *MAP_ID* | **pinned** *FILE* }
@@ -62,7 +67,7 @@ DESCRIPTION
	**bpftool map dump**    *MAP*
	**bpftool map dump**    *MAP*
		  Dump all entries in a given *MAP*.
		  Dump all entries in a given *MAP*.


	**bpftool map update**  *MAP*  **key** *DATA*   **value** *VALUE* [*UPDATE_FLAGS*]
	**bpftool map update**  *MAP* [**key** *DATA*] [**value** *VALUE*] [*UPDATE_FLAGS*]
		  Update map entry for a given *KEY*.
		  Update map entry for a given *KEY*.


		  *UPDATE_FLAGS* can be one of: **any** update existing entry
		  *UPDATE_FLAGS* can be one of: **any** update existing entry
@@ -75,7 +80,7 @@ DESCRIPTION
		  the bytes are parsed as decimal values, unless a "0x" prefix
		  the bytes are parsed as decimal values, unless a "0x" prefix
		  (for hexadecimal) or a "0" prefix (for octal) is provided.
		  (for hexadecimal) or a "0" prefix (for octal) is provided.


	**bpftool map lookup**  *MAP*  **key** *DATA*
	**bpftool map lookup**  *MAP* [**key** *DATA*]
		  Lookup **key** in the map.
		  Lookup **key** in the map.


	**bpftool map getnext** *MAP* [**key** *DATA*]
	**bpftool map getnext** *MAP* [**key** *DATA*]
@@ -107,6 +112,21 @@ DESCRIPTION
		  replace any existing ring.  Any other application will stop
		  replace any existing ring.  Any other application will stop
		  receiving events if it installed its rings earlier.
		  receiving events if it installed its rings earlier.


	**bpftool map peek**  *MAP*
		  Peek next **value** in the queue or stack.

	**bpftool map push**  *MAP* **value** *VALUE*
		  Push **value** onto the stack.

	**bpftool map pop**  *MAP*
		  Pop and print **value** from the stack.

	**bpftool map enqueue**  *MAP* **value** *VALUE*
		  Enqueue **value** into the queue.

	**bpftool map dequeue**  *MAP*
		  Dequeue and print **value** from the queue.

	**bpftool map help**
	**bpftool map help**
		  Print short help message.
		  Print short help message.


+73 −18
Original line number Original line Diff line number Diff line
@@ -50,14 +50,15 @@ _bpftool_get_map_ids()
        command sed -n 's/.*"id": \(.*\),$/\1/p' )" -- "$cur" ) )
        command sed -n 's/.*"id": \(.*\),$/\1/p' )" -- "$cur" ) )
}
}


_bpftool_get_perf_map_ids()
# Takes map type and adds matching map ids to the list of suggestions.
_bpftool_get_map_ids_for_type()
{
{
    local type="$1"
    COMPREPLY+=( $( compgen -W "$( bpftool -jp map  2>&1 | \
    COMPREPLY+=( $( compgen -W "$( bpftool -jp map  2>&1 | \
        command grep -C2 perf_event_array | \
        command grep -C2 "$type" | \
        command sed -n 's/.*"id": \(.*\),$/\1/p' )" -- "$cur" ) )
        command sed -n 's/.*"id": \(.*\),$/\1/p' )" -- "$cur" ) )
}
}



_bpftool_get_prog_ids()
_bpftool_get_prog_ids()
{
{
    COMPREPLY+=( $( compgen -W "$( bpftool -jp prog 2>&1 | \
    COMPREPLY+=( $( compgen -W "$( bpftool -jp prog 2>&1 | \
@@ -99,15 +100,25 @@ _sysfs_get_netdevs()
        "$cur" ) )
        "$cur" ) )
}
}


# For bpftool map update: retrieve type of the map to update.
# Retrieve type of the map that we are operating on.
_bpftool_map_update_map_type()
_bpftool_map_guess_map_type()
{
{
    local keyword ref
    local keyword ref
    for (( idx=3; idx < ${#words[@]}-1; idx++ )); do
    for (( idx=3; idx < ${#words[@]}-1; idx++ )); do
        if [[ ${words[$((idx-2))]} == "update" ]]; then
        case "${words[$((idx-2))]}" in
            lookup|update)
                keyword=${words[$((idx-1))]}
                keyword=${words[$((idx-1))]}
                ref=${words[$((idx))]}
                ref=${words[$((idx))]}
        fi
                ;;
            push)
                printf "stack"
                return 0
                ;;
            enqueue)
                printf "queue"
                return 0
                ;;
        esac
    done
    done
    [[ -z $ref ]] && return 0
    [[ -z $ref ]] && return 0


@@ -119,6 +130,8 @@ _bpftool_map_update_map_type()


_bpftool_map_update_get_id()
_bpftool_map_update_get_id()
{
{
    local command="$1"

    # Is it the map to update, or a map to insert into the map to update?
    # Is it the map to update, or a map to insert into the map to update?
    # Search for "value" keyword.
    # Search for "value" keyword.
    local idx value
    local idx value
@@ -128,11 +141,24 @@ _bpftool_map_update_get_id()
            break
            break
        fi
        fi
    done
    done
    [[ $value -eq 0 ]] && _bpftool_get_map_ids && return 0
    if [[ $value -eq 0 ]]; then
        case "$command" in
            push)
                _bpftool_get_map_ids_for_type stack
                ;;
            enqueue)
                _bpftool_get_map_ids_for_type queue
                ;;
            *)
                _bpftool_get_map_ids
                ;;
        esac
        return 0
    fi


    # Id to complete is for a value. It can be either prog id or map id. This
    # Id to complete is for a value. It can be either prog id or map id. This
    # depends on the type of the map to update.
    # depends on the type of the map to update.
    local type=$(_bpftool_map_update_map_type)
    local type=$(_bpftool_map_guess_map_type)
    case $type in
    case $type in
        array_of_maps|hash_of_maps)
        array_of_maps|hash_of_maps)
            _bpftool_get_map_ids
            _bpftool_get_map_ids
@@ -382,14 +408,28 @@ _bpftool()
        map)
        map)
            local MAP_TYPE='id pinned'
            local MAP_TYPE='id pinned'
            case $command in
            case $command in
                show|list|dump)
                show|list|dump|peek|pop|dequeue)
                    case $prev in
                    case $prev in
                        $command)
                        $command)
                            COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )
                            COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )
                            return 0
                            return 0
                            ;;
                            ;;
                        id)
                        id)
                            case "$command" in
                                peek)
                                    _bpftool_get_map_ids_for_type stack
                                    _bpftool_get_map_ids_for_type queue
                                    ;;
                                pop)
                                    _bpftool_get_map_ids_for_type stack
                                    ;;
                                dequeue)
                                    _bpftool_get_map_ids_for_type queue
                                    ;;
                                *)
                                    _bpftool_get_map_ids
                                    _bpftool_get_map_ids
                                    ;;
                            esac
                            return 0
                            return 0
                            ;;
                            ;;
                        *)
                        *)
@@ -447,19 +487,25 @@ _bpftool()
                            COMPREPLY+=( $( compgen -W 'hex' -- "$cur" ) )
                            COMPREPLY+=( $( compgen -W 'hex' -- "$cur" ) )
                            ;;
                            ;;
                        *)
                        *)
                            case $(_bpftool_map_guess_map_type) in
                                queue|stack)
                                    return 0
                                    ;;
                            esac

                            _bpftool_once_attr 'key'
                            _bpftool_once_attr 'key'
                            return 0
                            return 0
                            ;;
                            ;;
                    esac
                    esac
                    ;;
                    ;;
                update)
                update|push|enqueue)
                    case $prev in
                    case $prev in
                        $command)
                        $command)
                            COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )
                            COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )
                            return 0
                            return 0
                            ;;
                            ;;
                        id)
                        id)
                            _bpftool_map_update_get_id
                            _bpftool_map_update_get_id $command
                            return 0
                            return 0
                            ;;
                            ;;
                        key)
                        key)
@@ -468,7 +514,7 @@ _bpftool()
                        value)
                        value)
                            # We can have bytes, or references to a prog or a
                            # We can have bytes, or references to a prog or a
                            # map, depending on the type of the map to update.
                            # map, depending on the type of the map to update.
                            case $(_bpftool_map_update_map_type) in
                            case "$(_bpftool_map_guess_map_type)" in
                                array_of_maps|hash_of_maps)
                                array_of_maps|hash_of_maps)
                                    local MAP_TYPE='id pinned'
                                    local MAP_TYPE='id pinned'
                                    COMPREPLY+=( $( compgen -W "$MAP_TYPE" \
                                    COMPREPLY+=( $( compgen -W "$MAP_TYPE" \
@@ -490,6 +536,13 @@ _bpftool()
                            return 0
                            return 0
                            ;;
                            ;;
                        *)
                        *)
                            case $(_bpftool_map_guess_map_type) in
                                queue|stack)
                                    _bpftool_once_attr 'value'
                                    return 0;
                                    ;;
                            esac

                            _bpftool_once_attr 'key'
                            _bpftool_once_attr 'key'
                            local UPDATE_FLAGS='any exist noexist'
                            local UPDATE_FLAGS='any exist noexist'
                            for (( idx=3; idx < ${#words[@]}-1; idx++ )); do
                            for (( idx=3; idx < ${#words[@]}-1; idx++ )); do
@@ -508,6 +561,7 @@ _bpftool()
                                    return 0
                                    return 0
                                fi
                                fi
                            done
                            done

                            return 0
                            return 0
                            ;;
                            ;;
                    esac
                    esac
@@ -527,7 +581,7 @@ _bpftool()
                            return 0
                            return 0
                            ;;
                            ;;
                        id)
                        id)
                            _bpftool_get_perf_map_ids
                            _bpftool_get_map_ids_for_type perf_event_array
                            return 0
                            return 0
                            ;;
                            ;;
                        cpu)
                        cpu)
@@ -546,7 +600,8 @@ _bpftool()
                *)
                *)
                    [[ $prev == $object ]] && \
                    [[ $prev == $object ]] && \
                        COMPREPLY=( $( compgen -W 'delete dump getnext help \
                        COMPREPLY=( $( compgen -W 'delete dump getnext help \
                            lookup pin event_pipe show list update create' -- \
                            lookup pin event_pipe show list update create \
                            peek push enqueue pop dequeue' -- \
                            "$cur" ) )
                            "$cur" ) )
                    ;;
                    ;;
            esac
            esac
+157 −68
Original line number Original line Diff line number Diff line
@@ -285,16 +285,21 @@ static void print_entry_plain(struct bpf_map_info *info, unsigned char *key,
		single_line = info->key_size + info->value_size <= 24 &&
		single_line = info->key_size + info->value_size <= 24 &&
			!break_names;
			!break_names;


		if (info->key_size) {
			printf("key:%c", break_names ? '\n' : ' ');
			printf("key:%c", break_names ? '\n' : ' ');
			fprint_hex(stdout, key, info->key_size, " ");
			fprint_hex(stdout, key, info->key_size, " ");


			printf(single_line ? "  " : "\n");
			printf(single_line ? "  " : "\n");
		}


		if (info->value_size) {
			printf("value:%c", break_names ? '\n' : ' ');
			printf("value:%c", break_names ? '\n' : ' ');
			if (value)
			if (value)
			fprint_hex(stdout, value, info->value_size, " ");
				fprint_hex(stdout, value, info->value_size,
					   " ");
			else
			else
				printf("<no entry>");
				printf("<no entry>");
		}


		printf("\n");
		printf("\n");
	} else {
	} else {
@@ -303,9 +308,12 @@ static void print_entry_plain(struct bpf_map_info *info, unsigned char *key,
		n = get_possible_cpus();
		n = get_possible_cpus();
		step = round_up(info->value_size, 8);
		step = round_up(info->value_size, 8);


		if (info->key_size) {
			printf("key:\n");
			printf("key:\n");
			fprint_hex(stdout, key, info->key_size, " ");
			fprint_hex(stdout, key, info->key_size, " ");
			printf("\n");
			printf("\n");
		}
		if (info->value_size) {
			for (i = 0; i < n; i++) {
			for (i = 0; i < n; i++) {
				printf("value (CPU %02d):%c",
				printf("value (CPU %02d):%c",
				       i, info->value_size > 16 ? '\n' : ' ');
				       i, info->value_size > 16 ? '\n' : ' ');
@@ -318,6 +326,7 @@ static void print_entry_plain(struct bpf_map_info *info, unsigned char *key,
			}
			}
		}
		}
	}
	}
}


static char **parse_bytes(char **argv, const char *name, unsigned char *val,
static char **parse_bytes(char **argv, const char *name, unsigned char *val,
			  unsigned int n)
			  unsigned int n)
@@ -779,6 +788,32 @@ static int do_dump(int argc, char **argv)
	return err;
	return err;
}
}


static int alloc_key_value(struct bpf_map_info *info, void **key, void **value)
{
	*key = NULL;
	*value = NULL;

	if (info->key_size) {
		*key = malloc(info->key_size);
		if (!*key) {
			p_err("key mem alloc failed");
			return -1;
		}
	}

	if (info->value_size) {
		*value = alloc_value(info);
		if (!*value) {
			p_err("value mem alloc failed");
			free(*key);
			*key = NULL;
			return -1;
		}
	}

	return 0;
}

static int do_update(int argc, char **argv)
static int do_update(int argc, char **argv)
{
{
	struct bpf_map_info info = {};
	struct bpf_map_info info = {};
@@ -795,13 +830,9 @@ static int do_update(int argc, char **argv)
	if (fd < 0)
	if (fd < 0)
		return -1;
		return -1;


	key = malloc(info.key_size);
	err = alloc_key_value(&info, &key, &value);
	value = alloc_value(&info);
	if (err)
	if (!key || !value) {
		p_err("mem alloc failed");
		err = -1;
		goto exit_free;
		goto exit_free;
	}


	err = parse_elem(argv, &info, key, value, info.key_size,
	err = parse_elem(argv, &info, key, value, info.key_size,
			 info.value_size, &flags, &value_fd);
			 info.value_size, &flags, &value_fd);
@@ -826,12 +857,51 @@ static int do_update(int argc, char **argv)
	return err;
	return err;
}
}


static void print_key_value(struct bpf_map_info *info, void *key,
			    void *value)
{
	json_writer_t *btf_wtr;
	struct btf *btf = NULL;
	int err;

	err = btf__get_from_id(info->btf_id, &btf);
	if (err) {
		p_err("failed to get btf");
		return;
	}

	if (json_output) {
		print_entry_json(info, key, value, btf);
	} else if (btf) {
		/* if here json_wtr wouldn't have been initialised,
		 * so let's create separate writer for btf
		 */
		btf_wtr = get_btf_writer();
		if (!btf_wtr) {
			p_info("failed to create json writer for btf. falling back to plain output");
			btf__free(btf);
			btf = NULL;
			print_entry_plain(info, key, value);
		} else {
			struct btf_dumper d = {
				.btf = btf,
				.jw = btf_wtr,
				.is_plain_text = true,
			};

			do_dump_btf(&d, info, key, value);
			jsonw_destroy(&btf_wtr);
		}
	} else {
		print_entry_plain(info, key, value);
	}
	btf__free(btf);
}

static int do_lookup(int argc, char **argv)
static int do_lookup(int argc, char **argv)
{
{
	struct bpf_map_info info = {};
	struct bpf_map_info info = {};
	__u32 len = sizeof(info);
	__u32 len = sizeof(info);
	json_writer_t *btf_wtr;
	struct btf *btf = NULL;
	void *key, *value;
	void *key, *value;
	int err;
	int err;
	int fd;
	int fd;
@@ -843,13 +913,9 @@ static int do_lookup(int argc, char **argv)
	if (fd < 0)
	if (fd < 0)
		return -1;
		return -1;


	key = malloc(info.key_size);
	err = alloc_key_value(&info, &key, &value);
	value = alloc_value(&info);
	if (err)
	if (!key || !value) {
		p_err("mem alloc failed");
		err = -1;
		goto exit_free;
		goto exit_free;
	}


	err = parse_elem(argv, &info, key, NULL, info.key_size, 0, NULL, NULL);
	err = parse_elem(argv, &info, key, NULL, info.key_size, 0, NULL, NULL);
	if (err)
	if (err)
@@ -873,43 +939,12 @@ static int do_lookup(int argc, char **argv)
	}
	}


	/* here means bpf_map_lookup_elem() succeeded */
	/* here means bpf_map_lookup_elem() succeeded */
	err = btf__get_from_id(info.btf_id, &btf);
	print_key_value(&info, key, value);
	if (err) {
		p_err("failed to get btf");
		goto exit_free;
	}

	if (json_output) {
		print_entry_json(&info, key, value, btf);
	} else if (btf) {
		/* if here json_wtr wouldn't have been initialised,
		 * so let's create separate writer for btf
		 */
		btf_wtr = get_btf_writer();
		if (!btf_wtr) {
			p_info("failed to create json writer for btf. falling back to plain output");
			btf__free(btf);
			btf = NULL;
			print_entry_plain(&info, key, value);
		} else {
			struct btf_dumper d = {
				.btf = btf,
				.jw = btf_wtr,
				.is_plain_text = true,
			};

			do_dump_btf(&d, &info, key, value);
			jsonw_destroy(&btf_wtr);
		}
	} else {
		print_entry_plain(&info, key, value);
	}


exit_free:
exit_free:
	free(key);
	free(key);
	free(value);
	free(value);
	close(fd);
	close(fd);
	btf__free(btf);


	return err;
	return err;
}
}
@@ -1122,6 +1157,49 @@ static int do_create(int argc, char **argv)
	return 0;
	return 0;
}
}


static int do_pop_dequeue(int argc, char **argv)
{
	struct bpf_map_info info = {};
	__u32 len = sizeof(info);
	void *key, *value;
	int err;
	int fd;

	if (argc < 2)
		usage();

	fd = map_parse_fd_and_info(&argc, &argv, &info, &len);
	if (fd < 0)
		return -1;

	err = alloc_key_value(&info, &key, &value);
	if (err)
		goto exit_free;

	err = bpf_map_lookup_and_delete_elem(fd, key, value);
	if (err) {
		if (errno == ENOENT) {
			if (json_output)
				jsonw_null(json_wtr);
			else
				printf("Error: empty map\n");
		} else {
			p_err("pop failed: %s", strerror(errno));
		}

		goto exit_free;
	}

	print_key_value(&info, key, value);

exit_free:
	free(key);
	free(value);
	close(fd);

	return err;
}

static int do_help(int argc, char **argv)
static int do_help(int argc, char **argv)
{
{
	if (json_output) {
	if (json_output) {
@@ -1135,12 +1213,17 @@ static int do_help(int argc, char **argv)
		"                              entries MAX_ENTRIES name NAME [flags FLAGS] \\\n"
		"                              entries MAX_ENTRIES name NAME [flags FLAGS] \\\n"
		"                              [dev NAME]\n"
		"                              [dev NAME]\n"
		"       %s %s dump       MAP\n"
		"       %s %s dump       MAP\n"
		"       %s %s update     MAP  key DATA value VALUE [UPDATE_FLAGS]\n"
		"       %s %s update     MAP [key DATA] [value VALUE] [UPDATE_FLAGS]\n"
		"       %s %s lookup     MAP  key DATA\n"
		"       %s %s lookup     MAP [key DATA]\n"
		"       %s %s getnext    MAP [key DATA]\n"
		"       %s %s getnext    MAP [key DATA]\n"
		"       %s %s delete     MAP  key DATA\n"
		"       %s %s delete     MAP  key DATA\n"
		"       %s %s pin        MAP  FILE\n"
		"       %s %s pin        MAP  FILE\n"
		"       %s %s event_pipe MAP [cpu N index M]\n"
		"       %s %s event_pipe MAP [cpu N index M]\n"
		"       %s %s peek       MAP\n"
		"       %s %s push       MAP value VALUE\n"
		"       %s %s pop        MAP\n"
		"       %s %s enqueue    MAP value VALUE\n"
		"       %s %s dequeue    MAP\n"
		"       %s %s help\n"
		"       %s %s help\n"
		"\n"
		"\n"
		"       " HELP_SPEC_MAP "\n"
		"       " HELP_SPEC_MAP "\n"
@@ -1158,7 +1241,8 @@ static int do_help(int argc, char **argv)
		bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
		bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
		bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
		bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
		bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
		bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
		bin_name, argv[-2]);
		bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
		bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2]);


	return 0;
	return 0;
}
}
@@ -1175,6 +1259,11 @@ static const struct cmd cmds[] = {
	{ "pin",	do_pin },
	{ "pin",	do_pin },
	{ "event_pipe",	do_event_pipe },
	{ "event_pipe",	do_event_pipe },
	{ "create",	do_create },
	{ "create",	do_create },
	{ "peek",	do_lookup },
	{ "push",	do_update },
	{ "enqueue",	do_update },
	{ "pop",	do_pop_dequeue },
	{ "dequeue",	do_pop_dequeue },
	{ 0 }
	{ 0 }
};
};