Skip to content

tools: bpftool: support creating outer maps #15

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
sudo: required
language: bash
dist: bionic
services:
- docker

env:
global:
- PROJECT_NAME='libbpf'
- AUTHOR_EMAIL="$(git log -1 --pretty=\"%aE\")"
- REPO_ROOT="$TRAVIS_BUILD_DIR"
- CI_ROOT="$REPO_ROOT/travis-ci"
- VMTEST_ROOT="$CI_ROOT/vmtest"

addons:
apt:
packages:
- qemu-kvm
- zstd
- binutils-dev
- elfutils
- libcap-dev
- libelf-dev
- libdw-dev

jobs:
include:
- stage: Builds & Tests
name: Kernel LATEST + selftests
language: bash
env: KERNEL=LATEST
script: $CI_ROOT/vmtest/run_vmtest.sh || travis_terminate 1
10 changes: 8 additions & 2 deletions tools/bpf/bpftool/Documentation/bpftool-map.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ MAP COMMANDS

| **bpftool** **map** { **show** | **list** } [*MAP*]
| **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*] [**inner_map** *MAP*] \
| [**dev** *NAME*]
| **bpftool** **map dump** *MAP*
| **bpftool** **map update** *MAP* [**key** *DATA*] [**value** *VALUE*] [*UPDATE_FLAGS*]
| **bpftool** **map lookup** *MAP* [**key** *DATA*]
Expand Down Expand Up @@ -67,14 +68,19 @@ DESCRIPTION
maps. On such kernels bpftool will automatically emit this
information as well.

**bpftool map create** *FILE* **type** *TYPE* **key** *KEY_SIZE* **value** *VALUE_SIZE* **entries** *MAX_ENTRIES* **name** *NAME* [**flags** *FLAGS*] [**dev** *NAME*]
**bpftool map create** *FILE* **type** *TYPE* **key** *KEY_SIZE* **value** *VALUE_SIZE* **entries** *MAX_ENTRIES* **name** *NAME* [**flags** *FLAGS*] [**inner_map** *MAP*] [**dev** *NAME*]
Create a new map with given parameters and pin it to *bpffs*
as *FILE*.

*FLAGS* should be an integer which is the combination of
desired flags, e.g. 1024 for **BPF_F_MMAPABLE** (see bpf.h
UAPI header for existing flags).

To create maps of type array-of-maps or hash-of-maps, the
**inner_map** keyword must be used to pass an inner map. The
kernel needs it to collect metadata related to the inner maps
that the new map will work with.

Keyword **dev** expects a network interface name, and is used
to request hardware offload for the map.

Expand Down
22 changes: 21 additions & 1 deletion tools/bpf/bpftool/bash-completion/bpftool
Original file line number Diff line number Diff line change
Expand Up @@ -709,16 +709,36 @@ _bpftool()
"$cur" ) )
return 0
;;
key|value|flags|name|entries)
key|value|flags|entries)
return 0
;;
inner_map)
COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )
return 0
;;
id)
_bpftool_get_map_ids
;;
name)
case $pprev in
inner_map)
_bpftool_get_map_names
;;
*)
return 0
;;
esac
;;
*)
_bpftool_once_attr 'type'
_bpftool_once_attr 'key'
_bpftool_once_attr 'value'
_bpftool_once_attr 'entries'
_bpftool_once_attr 'name'
_bpftool_once_attr 'flags'
if _bpftool_search_list 'array_of_maps' 'hash_of_maps'; then
_bpftool_once_attr 'inner_map'
fi
_bpftool_once_attr 'dev'
return 0
;;
Expand Down
149 changes: 85 additions & 64 deletions tools/bpf/bpftool/map.c
Original file line number Diff line number Diff line change
Expand Up @@ -213,8 +213,9 @@ static void print_entry_json(struct bpf_map_info *info, unsigned char *key,
jsonw_end_object(json_wtr);
}

static void print_entry_error(struct bpf_map_info *info, unsigned char *key,
const char *error_msg)
static void
print_entry_error_msg(struct bpf_map_info *info, unsigned char *key,
const char *error_msg)
{
int msg_size = strlen(error_msg);
bool single_line, break_names;
Expand All @@ -232,6 +233,40 @@ static void print_entry_error(struct bpf_map_info *info, unsigned char *key,
printf("\n");
}

static void
print_entry_error(struct bpf_map_info *map_info, void *key, int lookup_errno)
{
/* For prog_array maps or arrays of maps, failure to lookup the value
* means there is no entry for that key. Do not print an error message
* in that case.
*/
if (map_is_map_of_maps(map_info->type) ||
map_is_map_of_progs(map_info->type))
return;

if (json_output) {
jsonw_start_object(json_wtr); /* entry */
jsonw_name(json_wtr, "key");
print_hex_data_json(key, map_info->key_size);
jsonw_name(json_wtr, "value");
jsonw_start_object(json_wtr); /* error */
jsonw_string_field(json_wtr, "error", strerror(lookup_errno));
jsonw_end_object(json_wtr); /* error */
jsonw_end_object(json_wtr); /* entry */
} else {
const char *msg = NULL;

if (lookup_errno == ENOENT)
msg = "<no entry>";
else if (lookup_errno == ENOSPC &&
map_info->type == BPF_MAP_TYPE_REUSEPORT_SOCKARRAY)
msg = "<cannot read>";

print_entry_error_msg(map_info, key,
msg ? : strerror(lookup_errno));
}
}

static void print_entry_plain(struct bpf_map_info *info, unsigned char *key,
unsigned char *value)
{
Expand Down Expand Up @@ -713,56 +748,23 @@ static int dump_map_elem(int fd, void *key, void *value,
struct bpf_map_info *map_info, struct btf *btf,
json_writer_t *btf_wtr)
{
int num_elems = 0;
int lookup_errno;

if (!bpf_map_lookup_elem(fd, key, value)) {
if (json_output) {
print_entry_json(map_info, key, value, btf);
} else {
if (btf) {
struct btf_dumper d = {
.btf = btf,
.jw = btf_wtr,
.is_plain_text = true,
};

do_dump_btf(&d, map_info, key, value);
} else {
print_entry_plain(map_info, key, value);
}
num_elems++;
}
return num_elems;
if (bpf_map_lookup_elem(fd, key, value)) {
print_entry_error(map_info, key, errno);
return -1;
}

/* lookup error handling */
lookup_errno = errno;

if (map_is_map_of_maps(map_info->type) ||
map_is_map_of_progs(map_info->type))
return 0;

if (json_output) {
jsonw_start_object(json_wtr);
jsonw_name(json_wtr, "key");
print_hex_data_json(key, map_info->key_size);
jsonw_name(json_wtr, "value");
jsonw_start_object(json_wtr);
jsonw_string_field(json_wtr, "error", strerror(lookup_errno));
jsonw_end_object(json_wtr);
jsonw_end_object(json_wtr);
} else {
const char *msg = NULL;

if (lookup_errno == ENOENT)
msg = "<no entry>";
else if (lookup_errno == ENOSPC &&
map_info->type == BPF_MAP_TYPE_REUSEPORT_SOCKARRAY)
msg = "<cannot read>";
print_entry_json(map_info, key, value, btf);
} else if (btf) {
struct btf_dumper d = {
.btf = btf,
.jw = btf_wtr,
.is_plain_text = true,
};

print_entry_error(map_info, key,
msg ? : strerror(lookup_errno));
do_dump_btf(&d, map_info, key, value);
} else {
print_entry_plain(map_info, key, value);
}

return 0;
Expand Down Expand Up @@ -873,7 +875,8 @@ map_dump(int fd, struct bpf_map_info *info, json_writer_t *wtr,
err = 0;
break;
}
num_elems += dump_map_elem(fd, key, value, info, btf, wtr);
if (!dump_map_elem(fd, key, value, info, btf, wtr))
num_elems++;
prev_key = key;
}

Expand Down Expand Up @@ -1247,7 +1250,7 @@ static int do_create(int argc, char **argv)
{
struct bpf_create_map_attr attr = { NULL, };
const char *pinfile;
int err, fd;
int err = -1, fd;

if (!REQ_ARGS(7))
return -1;
Expand All @@ -1262,13 +1265,13 @@ static int do_create(int argc, char **argv)

if (attr.map_type) {
p_err("map type already specified");
return -1;
goto exit;
}

attr.map_type = map_type_from_str(*argv);
if ((int)attr.map_type < 0) {
p_err("unrecognized map type: %s", *argv);
return -1;
goto exit;
}
NEXT_ARG();
} else if (is_prefix(*argv, "name")) {
Expand All @@ -1277,61 +1280,79 @@ static int do_create(int argc, char **argv)
} else if (is_prefix(*argv, "key")) {
if (parse_u32_arg(&argc, &argv, &attr.key_size,
"key size"))
return -1;
goto exit;
} else if (is_prefix(*argv, "value")) {
if (parse_u32_arg(&argc, &argv, &attr.value_size,
"value size"))
return -1;
goto exit;
} else if (is_prefix(*argv, "entries")) {
if (parse_u32_arg(&argc, &argv, &attr.max_entries,
"max entries"))
return -1;
goto exit;
} else if (is_prefix(*argv, "flags")) {
if (parse_u32_arg(&argc, &argv, &attr.map_flags,
"flags"))
return -1;
goto exit;
} else if (is_prefix(*argv, "dev")) {
NEXT_ARG();

if (attr.map_ifindex) {
p_err("offload device already specified");
return -1;
goto exit;
}

attr.map_ifindex = if_nametoindex(*argv);
if (!attr.map_ifindex) {
p_err("unrecognized netdevice '%s': %s",
*argv, strerror(errno));
return -1;
goto exit;
}
NEXT_ARG();
} else if (is_prefix(*argv, "inner_map")) {
struct bpf_map_info info = {};
__u32 len = sizeof(info);
int inner_map_fd;

NEXT_ARG();
if (!REQ_ARGS(2))
usage();
inner_map_fd = map_parse_fd_and_info(&argc, &argv,
&info, &len);
if (inner_map_fd < 0)
return -1;
attr.inner_map_fd = inner_map_fd;
} else {
p_err("unknown arg %s", *argv);
return -1;
goto exit;
}
}

if (!attr.name) {
p_err("map name not specified");
return -1;
goto exit;
}

set_max_rlimit();

fd = bpf_create_map_xattr(&attr);
if (fd < 0) {
p_err("map create failed: %s", strerror(errno));
return -1;
goto exit;
}

err = do_pin_fd(fd, pinfile);
close(fd);
if (err)
return err;
goto exit;

if (json_output)
jsonw_null(json_wtr);
return 0;

exit:
if (attr.inner_map_fd > 0)
close(attr.inner_map_fd);

return err;
}

static int do_pop_dequeue(int argc, char **argv)
Expand Down Expand Up @@ -1417,7 +1438,7 @@ static int do_help(int argc, char **argv)
"Usage: %1$s %2$s { show | list } [MAP]\n"
" %1$s %2$s create FILE type TYPE key KEY_SIZE value VALUE_SIZE \\\n"
" entries MAX_ENTRIES name NAME [flags FLAGS] \\\n"
" [dev NAME]\n"
" [inner_map MAP] [dev NAME]\n"
" %1$s %2$s dump MAP\n"
" %1$s %2$s update MAP [key DATA] [value VALUE] [UPDATE_FLAGS]\n"
" %1$s %2$s lookup MAP [key DATA]\n"
Expand Down
Loading