netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH bpf-next v2 00/12] tools: bpf: extend bpftool prog load
@ 2018-07-09 17:59 Jakub Kicinski
  2018-07-09 17:59 ` [PATCH bpf-next v2 01/12] selftests/bpf: remove duplicated word from test offloads Jakub Kicinski
                   ` (11 more replies)
  0 siblings, 12 replies; 24+ messages in thread
From: Jakub Kicinski @ 2018-07-09 17:59 UTC (permalink / raw)
  To: alexei.starovoitov, daniel; +Cc: oss-drivers, netdev, Jakub Kicinski

Hi!

This series starts with two minor clean ups to test_offload.py
selftest script.

The next 9 patches extend the abilities of bpftool prog load
beyond the simple cgroup use cases.  Three new parameters are
added:

 - type - allows specifying program type, independent of how
   code sections are named;
 - map  - allows reusing existing maps, instead of creating a new
   map on every program load;
 - dev  - offload/binding to a device.

A number of changes to libbpf is required to accomplish the task.
The section - program type logic mapping is exposed.  We should
probably aim to use the libbpf program section naming everywhere.
For reuse of maps we need to allow users to set FD for bpf map
object in libbpf.

Examples

Load program my_xdp.o and pin it as /sys/fs/bpf/my_xdp, for xdp
program type:

$ bpftool prog load my_xdp.o /sys/fs/bpf/my_xdp \
  type xdp

As above but for offload:

$ bpftool prog load my_xdp.o /sys/fs/bpf/my_xdp \
  type xdp \
  dev netdevsim0

Load program my_maps.o, but for the first map reuse map id 17,
and for the map called "other_map" reuse pinned map /sys/fs/bpf/map0:

$ bpftool prog load my_maps.o /sys/fs/bpf/prog \
  map idx 0 id 17 \
  map name other_map pinned /sys/fs/bpf/map0

---
v2:
 - add compat for reallocarray().
 
Jakub Kicinski (12):
  selftests/bpf: remove duplicated word from test offloads
  selftests/bpf: add Error: prefix in check_extack helper
  tools: bpftool: refactor argument parsing for prog load
  tools: bpftool: add support for loading programs for offload
  tools: libbpf: expose the prog type guessing from section name logic
  tools: bpftool: allow users to specify program type for prog load
  tools: libbpf: recognize offload neutral maps
  tools: libbpf: add extended attributes version of bpf_object__open()
  tools: bpftool: reimplement bpf_prog_load() for prog load
  tools: bpf: make use of reallocarray
  tools: libbpf: allow map reuse
  tools: bpftool: allow reuse of maps with bpftool prog load

 .../bpftool/Documentation/bpftool-prog.rst    |  33 ++-
 tools/bpf/bpftool/Makefile                    |   6 +-
 tools/bpf/bpftool/bash-completion/bpftool     |  96 +++++-
 tools/bpf/bpftool/main.h                      |  19 ++
 tools/bpf/bpftool/map.c                       |   4 +-
 tools/bpf/bpftool/prog.c                      | 245 ++++++++++++++-
 tools/bpf/bpftool/xlated_dumper.c             |   6 +-
 tools/build/feature/Makefile                  |   4 +
 tools/build/feature/test-reallocarray.c       |   8 +
 tools/include/linux/compiler-gcc.h            |   4 +
 tools/include/linux/overflow.h                | 278 ++++++++++++++++++
 tools/include/tools/libc_compat.h             |  23 ++
 tools/lib/bpf/Makefile                        |   6 +-
 tools/lib/bpf/libbpf.c                        | 116 ++++++--
 tools/lib/bpf/libbpf.h                        |  11 +
 tools/testing/selftests/bpf/test_offload.py   |  10 +-
 16 files changed, 812 insertions(+), 57 deletions(-)
 create mode 100644 tools/build/feature/test-reallocarray.c
 create mode 100644 tools/include/linux/overflow.h
 create mode 100644 tools/include/tools/libc_compat.h

-- 
2.17.1

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH bpf-next v2 01/12] selftests/bpf: remove duplicated word from test offloads
  2018-07-09 17:59 [PATCH bpf-next v2 00/12] tools: bpf: extend bpftool prog load Jakub Kicinski
@ 2018-07-09 17:59 ` Jakub Kicinski
  2018-07-09 17:59 ` [PATCH bpf-next v2 02/12] selftests/bpf: add Error: prefix in check_extack helper Jakub Kicinski
                   ` (10 subsequent siblings)
  11 siblings, 0 replies; 24+ messages in thread
From: Jakub Kicinski @ 2018-07-09 17:59 UTC (permalink / raw)
  To: alexei.starovoitov, daniel; +Cc: oss-drivers, netdev, Jakub Kicinski

Trivial removal of duplicated "mode" in error message.

Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com>
---
 tools/testing/selftests/bpf/test_offload.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/testing/selftests/bpf/test_offload.py b/tools/testing/selftests/bpf/test_offload.py
index be800d0e7a84..a257e4b08392 100755
--- a/tools/testing/selftests/bpf/test_offload.py
+++ b/tools/testing/selftests/bpf/test_offload.py
@@ -830,7 +830,7 @@ netns = []
     check_extack_nsim(err, "program loaded with different flags.", args)
     ret, _, err = sim.unset_xdp("", force=True,
                                 fail=False, include_stderr=True)
-    fail(ret == 0, "Removed program with a bad mode mode")
+    fail(ret == 0, "Removed program with a bad mode")
     check_extack_nsim(err, "program loaded with different flags.", args)
 
     start_test("Test MTU restrictions...")
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH bpf-next v2 02/12] selftests/bpf: add Error: prefix in check_extack helper
  2018-07-09 17:59 [PATCH bpf-next v2 00/12] tools: bpf: extend bpftool prog load Jakub Kicinski
  2018-07-09 17:59 ` [PATCH bpf-next v2 01/12] selftests/bpf: remove duplicated word from test offloads Jakub Kicinski
@ 2018-07-09 17:59 ` Jakub Kicinski
  2018-07-09 17:59 ` [PATCH bpf-next v2 03/12] tools: bpftool: refactor argument parsing for prog load Jakub Kicinski
                   ` (9 subsequent siblings)
  11 siblings, 0 replies; 24+ messages in thread
From: Jakub Kicinski @ 2018-07-09 17:59 UTC (permalink / raw)
  To: alexei.starovoitov, daniel; +Cc: oss-drivers, netdev, Jakub Kicinski

Currently the test only checks errors, not warnings, so save typing
and prefix the extack messages with "Error:" inside the check helper.

Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com>
---
 tools/testing/selftests/bpf/test_offload.py | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/tools/testing/selftests/bpf/test_offload.py b/tools/testing/selftests/bpf/test_offload.py
index a257e4b08392..f8d9bd81d9a4 100755
--- a/tools/testing/selftests/bpf/test_offload.py
+++ b/tools/testing/selftests/bpf/test_offload.py
@@ -547,11 +547,11 @@ netns = [] # net namespaces to be removed
     if skip_extack:
         return
     lines = output.split("\n")
-    comp = len(lines) >= 2 and lines[1] == reference
+    comp = len(lines) >= 2 and lines[1] == 'Error: ' + reference
     fail(not comp, "Missing or incorrect netlink extack message")
 
 def check_extack_nsim(output, reference, args):
-    check_extack(output, "Error: netdevsim: " + reference, args)
+    check_extack(output, "netdevsim: " + reference, args)
 
 def check_no_extack(res, needle):
     fail((res[1] + res[2]).count(needle) or (res[1] + res[2]).count("Warning:"),
@@ -654,7 +654,7 @@ netns = []
     ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True,
                                          fail=False, include_stderr=True)
     fail(ret == 0, "TC filter loaded without enabling TC offloads")
-    check_extack(err, "Error: TC offload is disabled on net device.", args)
+    check_extack(err, "TC offload is disabled on net device.", args)
     sim.wait_for_flush()
 
     sim.set_ethtool_tc_offloads(True)
@@ -694,7 +694,7 @@ netns = []
                                          skip_sw=True,
                                          fail=False, include_stderr=True)
     fail(ret == 0, "Offloaded a filter to chain other than 0")
-    check_extack(err, "Error: Driver supports only offload of chain 0.", args)
+    check_extack(err, "Driver supports only offload of chain 0.", args)
     sim.tc_flush_filters()
 
     start_test("Test TC replace...")
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH bpf-next v2 03/12] tools: bpftool: refactor argument parsing for prog load
  2018-07-09 17:59 [PATCH bpf-next v2 00/12] tools: bpf: extend bpftool prog load Jakub Kicinski
  2018-07-09 17:59 ` [PATCH bpf-next v2 01/12] selftests/bpf: remove duplicated word from test offloads Jakub Kicinski
  2018-07-09 17:59 ` [PATCH bpf-next v2 02/12] selftests/bpf: add Error: prefix in check_extack helper Jakub Kicinski
@ 2018-07-09 17:59 ` Jakub Kicinski
  2018-07-09 17:59 ` [PATCH bpf-next v2 04/12] tools: bpftool: add support for loading programs for offload Jakub Kicinski
                   ` (8 subsequent siblings)
  11 siblings, 0 replies; 24+ messages in thread
From: Jakub Kicinski @ 2018-07-09 17:59 UTC (permalink / raw)
  To: alexei.starovoitov, daniel; +Cc: oss-drivers, netdev, Jakub Kicinski

Add a new macro for printing more informative message than straight
usage() when parameters are missing, and use it for prog do_load().
Save the object and pin path argument to variables for clarity.

Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com>
---
 tools/bpf/bpftool/main.h | 15 +++++++++++++++
 tools/bpf/bpftool/prog.c | 11 +++++++----
 2 files changed, 22 insertions(+), 4 deletions(-)

diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h
index d39f7ef01d23..15b6c49ae533 100644
--- a/tools/bpf/bpftool/main.h
+++ b/tools/bpf/bpftool/main.h
@@ -50,6 +50,21 @@
 #define NEXT_ARG()	({ argc--; argv++; if (argc < 0) usage(); })
 #define NEXT_ARGP()	({ (*argc)--; (*argv)++; if (*argc < 0) usage(); })
 #define BAD_ARG()	({ p_err("what is '%s'?", *argv); -1; })
+#define GET_ARG()	({ argc--; *argv++; })
+#define REQ_ARGS(cnt)							\
+	({								\
+		int _cnt = (cnt);					\
+		bool _res;						\
+									\
+		if (argc < _cnt) {					\
+			p_err("'%s' needs at least %d arguments, %d found", \
+			      argv[-1], _cnt, argc);			\
+			_res = false;					\
+		} else {						\
+			_res = true;					\
+		}							\
+		_res;							\
+	})
 
 #define ERR_MAX_LEN	1024
 
diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c
index a740da99d477..a5ef46c59029 100644
--- a/tools/bpf/bpftool/prog.c
+++ b/tools/bpf/bpftool/prog.c
@@ -681,18 +681,21 @@ static int do_pin(int argc, char **argv)
 
 static int do_load(int argc, char **argv)
 {
+	const char *objfile, *pinfile;
 	struct bpf_object *obj;
 	int prog_fd;
 
-	if (argc != 2)
-		usage();
+	if (!REQ_ARGS(2))
+		return -1;
+	objfile = GET_ARG();
+	pinfile = GET_ARG();
 
-	if (bpf_prog_load(argv[0], BPF_PROG_TYPE_UNSPEC, &obj, &prog_fd)) {
+	if (bpf_prog_load(objfile, BPF_PROG_TYPE_UNSPEC, &obj, &prog_fd)) {
 		p_err("failed to load program");
 		return -1;
 	}
 
-	if (do_pin_fd(prog_fd, argv[1]))
+	if (do_pin_fd(prog_fd, pinfile))
 		goto err_close_obj;
 
 	if (json_output)
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH bpf-next v2 04/12] tools: bpftool: add support for loading programs for offload
  2018-07-09 17:59 [PATCH bpf-next v2 00/12] tools: bpf: extend bpftool prog load Jakub Kicinski
                   ` (2 preceding siblings ...)
  2018-07-09 17:59 ` [PATCH bpf-next v2 03/12] tools: bpftool: refactor argument parsing for prog load Jakub Kicinski
@ 2018-07-09 17:59 ` Jakub Kicinski
  2018-07-09 17:59 ` [PATCH bpf-next v2 05/12] tools: libbpf: expose the prog type guessing from section name logic Jakub Kicinski
                   ` (7 subsequent siblings)
  11 siblings, 0 replies; 24+ messages in thread
From: Jakub Kicinski @ 2018-07-09 17:59 UTC (permalink / raw)
  To: alexei.starovoitov, daniel; +Cc: oss-drivers, netdev, Jakub Kicinski

Extend the bpftool prog load command to also accept "dev"
parameter, which will allow us to load programs onto devices.

Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com>
---
 .../bpftool/Documentation/bpftool-prog.rst    |  6 ++--
 tools/bpf/bpftool/bash-completion/bpftool     | 23 ++++++++++--
 tools/bpf/bpftool/prog.c                      | 35 +++++++++++++++++--
 3 files changed, 58 insertions(+), 6 deletions(-)

diff --git a/tools/bpf/bpftool/Documentation/bpftool-prog.rst b/tools/bpf/bpftool/Documentation/bpftool-prog.rst
index 43d34a5c3ec5..41723c6acaa6 100644
--- a/tools/bpf/bpftool/Documentation/bpftool-prog.rst
+++ b/tools/bpf/bpftool/Documentation/bpftool-prog.rst
@@ -24,7 +24,7 @@ MAP COMMANDS
 |	**bpftool** **prog dump xlated** *PROG* [{**file** *FILE* | **opcodes** | **visual**}]
 |	**bpftool** **prog dump jited**  *PROG* [{**file** *FILE* | **opcodes**}]
 |	**bpftool** **prog pin** *PROG* *FILE*
-|	**bpftool** **prog load** *OBJ* *FILE*
+|	**bpftool** **prog load** *OBJ* *FILE* [**dev** *NAME*]
 |	**bpftool** **prog help**
 |
 |	*PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* }
@@ -64,8 +64,10 @@ DESCRIPTION
 
 		  Note: *FILE* must be located in *bpffs* mount.
 
-	**bpftool prog load** *OBJ* *FILE*
+	**bpftool prog load** *OBJ* *FILE* [**dev** *NAME*]
 		  Load bpf program from binary *OBJ* and pin as *FILE*.
+		  If **dev** *NAME* is specified program will be loaded onto
+		  given networking device (offload).
 
 		  Note: *FILE* must be located in *bpffs* mount.
 
diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool
index ce0bc0cda361..238c2f80092a 100644
--- a/tools/bpf/bpftool/bash-completion/bpftool
+++ b/tools/bpf/bpftool/bash-completion/bpftool
@@ -99,6 +99,12 @@ _bpftool_get_prog_tags()
         command sed -n 's/.*"tag": "\(.*\)",$/\1/p' )" -- "$cur" ) )
 }
 
+_sysfs_get_netdevs()
+{
+    COMPREPLY+=( $( compgen -W "$( ls /sys/class/net 2>/dev/null )" -- \
+        "$cur" ) )
+}
+
 # For bpftool map update: retrieve type of the map to update.
 _bpftool_map_update_map_type()
 {
@@ -262,8 +268,21 @@ _bpftool()
                     return 0
                     ;;
                 load)
-                    _filedir
-                    return 0
+                    if [[ ${#words[@]} -lt 6 ]]; then
+                        _filedir
+                        return 0
+                    fi
+
+                    case $prev in
+                        dev)
+                            _sysfs_get_netdevs
+                            return 0
+                            ;;
+                        *)
+                            _bpftool_once_attr 'dev'
+                            return 0
+                            ;;
+                    esac
                     ;;
                 *)
                     [[ $prev == $object ]] && \
diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c
index a5ef46c59029..21c74de7156f 100644
--- a/tools/bpf/bpftool/prog.c
+++ b/tools/bpf/bpftool/prog.c
@@ -39,6 +39,7 @@
 #include <string.h>
 #include <time.h>
 #include <unistd.h>
+#include <net/if.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 
@@ -681,6 +682,9 @@ static int do_pin(int argc, char **argv)
 
 static int do_load(int argc, char **argv)
 {
+	struct bpf_prog_load_attr attr = {
+		.prog_type	= BPF_PROG_TYPE_UNSPEC,
+	};
 	const char *objfile, *pinfile;
 	struct bpf_object *obj;
 	int prog_fd;
@@ -690,7 +694,34 @@ static int do_load(int argc, char **argv)
 	objfile = GET_ARG();
 	pinfile = GET_ARG();
 
-	if (bpf_prog_load(objfile, BPF_PROG_TYPE_UNSPEC, &obj, &prog_fd)) {
+	while (argc) {
+		if (is_prefix(*argv, "dev")) {
+			NEXT_ARG();
+
+			if (attr.ifindex) {
+				p_err("offload device already specified");
+				return -1;
+			}
+			if (!REQ_ARGS(1))
+				return -1;
+
+			attr.ifindex = if_nametoindex(*argv);
+			if (!attr.ifindex) {
+				p_err("unrecognized netdevice '%s': %s",
+				      *argv, strerror(errno));
+				return -1;
+			}
+			NEXT_ARG();
+		} else {
+			p_err("expected no more arguments or 'dev', got: '%s'?",
+			      *argv);
+			return -1;
+		}
+	}
+
+	attr.file = objfile;
+
+	if (bpf_prog_load_xattr(&attr, &obj, &prog_fd)) {
 		p_err("failed to load program");
 		return -1;
 	}
@@ -722,7 +753,7 @@ static int do_help(int argc, char **argv)
 		"       %s %s dump xlated PROG [{ file FILE | opcodes | visual }]\n"
 		"       %s %s dump jited  PROG [{ file FILE | opcodes }]\n"
 		"       %s %s pin   PROG FILE\n"
-		"       %s %s load  OBJ  FILE\n"
+		"       %s %s load  OBJ  FILE [dev NAME]\n"
 		"       %s %s help\n"
 		"\n"
 		"       " HELP_SPEC_PROGRAM "\n"
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH bpf-next v2 05/12] tools: libbpf: expose the prog type guessing from section name logic
  2018-07-09 17:59 [PATCH bpf-next v2 00/12] tools: bpf: extend bpftool prog load Jakub Kicinski
                   ` (3 preceding siblings ...)
  2018-07-09 17:59 ` [PATCH bpf-next v2 04/12] tools: bpftool: add support for loading programs for offload Jakub Kicinski
@ 2018-07-09 17:59 ` Jakub Kicinski
  2018-07-10  5:10   ` Andrey Ignatov
  2018-07-09 17:59 ` [PATCH bpf-next v2 06/12] tools: bpftool: allow users to specify program type for prog load Jakub Kicinski
                   ` (6 subsequent siblings)
  11 siblings, 1 reply; 24+ messages in thread
From: Jakub Kicinski @ 2018-07-09 17:59 UTC (permalink / raw)
  To: alexei.starovoitov, daniel; +Cc: oss-drivers, netdev, Jakub Kicinski

libbpf can guess program type based on ELF section names.  As libbpf
becomes more popular its association between section name strings and
types becomes more of a standard.  Allow libbpf users to use the same
logic for matching strings to types, e.g. when the string originates
from command line.

Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com>
---
 tools/lib/bpf/libbpf.c | 43 ++++++++++++++++++++++++------------------
 tools/lib/bpf/libbpf.h |  3 +++
 2 files changed, 28 insertions(+), 18 deletions(-)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 38ed3e92e393..30f3e58bd563 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -2081,25 +2081,33 @@ static const struct {
 #undef BPF_S_PROG_SEC
 #undef BPF_SA_PROG_SEC
 
-static int bpf_program__identify_section(struct bpf_program *prog)
+int libbpf_prog_type_by_string(const char *name, enum bpf_prog_type *prog_type,
+			       enum bpf_attach_type *expected_attach_type)
 {
 	int i;
 
-	if (!prog->section_name)
-		goto err;
-
-	for (i = 0; i < ARRAY_SIZE(section_names); i++)
-		if (strncmp(prog->section_name, section_names[i].sec,
-			    section_names[i].len) == 0)
-			return i;
-
-err:
-	pr_warning("failed to guess program type based on section name %s\n",
-		   prog->section_name);
+	if (!name)
+		return -1;
 
+	for (i = 0; i < ARRAY_SIZE(section_names); i++) {
+		if (strncmp(name, section_names[i].sec, section_names[i].len))
+			continue;
+		*prog_type = section_names[i].prog_type;
+		*expected_attach_type = section_names[i].expected_attach_type;
+		return 0;
+	}
 	return -1;
 }
 
+static int
+bpf_program__identify_section(struct bpf_program *prog,
+			      enum bpf_prog_type *prog_type,
+			      enum bpf_attach_type *expected_attach_type)
+{
+	return libbpf_prog_type_by_string(prog->section_name, prog_type,
+					  expected_attach_type);
+}
+
 int bpf_map__fd(struct bpf_map *map)
 {
 	return map ? map->fd : -EINVAL;
@@ -2230,7 +2238,6 @@ int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr,
 	enum bpf_prog_type prog_type;
 	struct bpf_object *obj;
 	struct bpf_map *map;
-	int section_idx;
 	int err;
 
 	if (!attr)
@@ -2252,14 +2259,14 @@ int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr,
 		prog->prog_ifindex = attr->ifindex;
 		expected_attach_type = attr->expected_attach_type;
 		if (prog_type == BPF_PROG_TYPE_UNSPEC) {
-			section_idx = bpf_program__identify_section(prog);
-			if (section_idx < 0) {
+			err = bpf_program__identify_section(prog, &prog_type,
+							    &expected_attach_type);
+			if (err < 0) {
+				pr_warning("failed to guess program type based on section name %s\n",
+					   prog->section_name);
 				bpf_object__close(obj);
 				return -EINVAL;
 			}
-			prog_type = section_names[section_idx].prog_type;
-			expected_attach_type =
-				section_names[section_idx].expected_attach_type;
 		}
 
 		bpf_program__set_type(prog, prog_type);
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index 564f4be9bae0..617dacfc6704 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -92,6 +92,9 @@ int bpf_object__set_priv(struct bpf_object *obj, void *priv,
 			 bpf_object_clear_priv_t clear_priv);
 void *bpf_object__priv(struct bpf_object *prog);
 
+int libbpf_prog_type_by_string(const char *name, enum bpf_prog_type *prog_type,
+			       enum bpf_attach_type *expected_attach_type);
+
 /* Accessors of bpf_program */
 struct bpf_program;
 struct bpf_program *bpf_program__next(struct bpf_program *prog,
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH bpf-next v2 06/12] tools: bpftool: allow users to specify program type for prog load
  2018-07-09 17:59 [PATCH bpf-next v2 00/12] tools: bpf: extend bpftool prog load Jakub Kicinski
                   ` (4 preceding siblings ...)
  2018-07-09 17:59 ` [PATCH bpf-next v2 05/12] tools: libbpf: expose the prog type guessing from section name logic Jakub Kicinski
@ 2018-07-09 17:59 ` Jakub Kicinski
  2018-07-09 17:59 ` [PATCH bpf-next v2 07/12] tools: libbpf: recognize offload neutral maps Jakub Kicinski
                   ` (5 subsequent siblings)
  11 siblings, 0 replies; 24+ messages in thread
From: Jakub Kicinski @ 2018-07-09 17:59 UTC (permalink / raw)
  To: alexei.starovoitov, daniel; +Cc: oss-drivers, netdev, Jakub Kicinski

Sometimes program section names don't match with libbpf's expectation.
In particular XDP's default section names differ between libbpf and
iproute2.  Allow users to pass program type on command line.  Name
the types like the libbpf expected section names.

Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com>
---
 .../bpftool/Documentation/bpftool-prog.rst    | 15 ++++++-
 tools/bpf/bpftool/bash-completion/bpftool     |  6 +++
 tools/bpf/bpftool/prog.c                      | 44 +++++++++++++++++--
 3 files changed, 60 insertions(+), 5 deletions(-)

diff --git a/tools/bpf/bpftool/Documentation/bpftool-prog.rst b/tools/bpf/bpftool/Documentation/bpftool-prog.rst
index 41723c6acaa6..e53e1ad2caf0 100644
--- a/tools/bpf/bpftool/Documentation/bpftool-prog.rst
+++ b/tools/bpf/bpftool/Documentation/bpftool-prog.rst
@@ -24,10 +24,19 @@ MAP COMMANDS
 |	**bpftool** **prog dump xlated** *PROG* [{**file** *FILE* | **opcodes** | **visual**}]
 |	**bpftool** **prog dump jited**  *PROG* [{**file** *FILE* | **opcodes**}]
 |	**bpftool** **prog pin** *PROG* *FILE*
-|	**bpftool** **prog load** *OBJ* *FILE* [**dev** *NAME*]
+|	**bpftool** **prog load** *OBJ* *FILE* [**type** *TYPE*] [**dev** *NAME*]
 |	**bpftool** **prog help**
 |
 |	*PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* }
+|	*TYPE* := {
+|		**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/post_bind4** | **cgroup/post_bind6** |
+|		**cgroup/connect4** | **cgroup/connect6** | **cgroup/sendmsg4** | **cgroup/sendmsg6**
+|	}
+
 
 DESCRIPTION
 ===========
@@ -64,8 +73,10 @@ DESCRIPTION
 
 		  Note: *FILE* must be located in *bpffs* mount.
 
-	**bpftool prog load** *OBJ* *FILE* [**dev** *NAME*]
+	**bpftool prog load** *OBJ* *FILE* [**type** *TYPE*] [**dev** *NAME*]
 		  Load bpf program from binary *OBJ* and pin as *FILE*.
+		  **type** is optional, if not specified program type will be
+		  inferred from section names.
 		  If **dev** *NAME* is specified program will be loaded onto
 		  given networking device (offload).
 
diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool
index 238c2f80092a..caf8711993be 100644
--- a/tools/bpf/bpftool/bash-completion/bpftool
+++ b/tools/bpf/bpftool/bash-completion/bpftool
@@ -274,11 +274,17 @@ _bpftool()
                     fi
 
                     case $prev in
+                        type)
+                            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" -- \
+                                                   "$cur" ) )
+                            return 0
+                            ;;
                         dev)
                             _sysfs_get_netdevs
                             return 0
                             ;;
                         *)
+                            _bpftool_once_attr 'type'
                             _bpftool_once_attr 'dev'
                             return 0
                             ;;
diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c
index 21c74de7156f..7a06fd4c5d27 100644
--- a/tools/bpf/bpftool/prog.c
+++ b/tools/bpf/bpftool/prog.c
@@ -688,6 +688,7 @@ static int do_load(int argc, char **argv)
 	const char *objfile, *pinfile;
 	struct bpf_object *obj;
 	int prog_fd;
+	int err;
 
 	if (!REQ_ARGS(2))
 		return -1;
@@ -695,7 +696,37 @@ static int do_load(int argc, char **argv)
 	pinfile = GET_ARG();
 
 	while (argc) {
-		if (is_prefix(*argv, "dev")) {
+		if (is_prefix(*argv, "type")) {
+			char *type;
+
+			NEXT_ARG();
+
+			if (attr.prog_type != BPF_PROG_TYPE_UNSPEC) {
+				p_err("program type already specified");
+				return -1;
+			}
+			if (!REQ_ARGS(1))
+				return -1;
+
+			/* Put a '/' at the end of type to appease libbpf */
+			type = malloc(strlen(*argv) + 2);
+			if (!type) {
+				p_err("mem alloc failed");
+				return -1;
+			}
+			*type = 0;
+			strcat(type, *argv);
+			strcat(type, "/");
+
+			err = libbpf_prog_type_by_string(type, &attr.prog_type,
+							 &attr.expected_attach_type);
+			free(type);
+			if (err < 0) {
+				p_err("unknown program type '%s'", *argv);
+				return err;
+			}
+			NEXT_ARG();
+		} else if (is_prefix(*argv, "dev")) {
 			NEXT_ARG();
 
 			if (attr.ifindex) {
@@ -713,7 +744,7 @@ static int do_load(int argc, char **argv)
 			}
 			NEXT_ARG();
 		} else {
-			p_err("expected no more arguments or 'dev', got: '%s'?",
+			p_err("expected no more arguments, 'type' or 'dev', got: '%s'?",
 			      *argv);
 			return -1;
 		}
@@ -753,10 +784,17 @@ static int do_help(int argc, char **argv)
 		"       %s %s dump xlated PROG [{ file FILE | opcodes | visual }]\n"
 		"       %s %s dump jited  PROG [{ file FILE | opcodes }]\n"
 		"       %s %s pin   PROG FILE\n"
-		"       %s %s load  OBJ  FILE [dev NAME]\n"
+		"       %s %s load  OBJ  FILE [type TYPE] [dev NAME]\n"
 		"       %s %s help\n"
 		"\n"
 		"       " HELP_SPEC_PROGRAM "\n"
+		"       TYPE := { socket | kprobe | kretprobe | classifier | action |\n"
+		"                 tracepoint | raw_tracepoint | xdp | perf_event | cgroup/skb |\n"
+		"                 cgroup/sock | cgroup/dev | lwt_in | lwt_out | lwt_xmit |\n"
+		"                 lwt_seg6local | sockops | sk_skb | sk_msg | lirc_mode2 |\n"
+		"                 cgroup/bind4 | cgroup/bind6 | cgroup/post_bind4 |\n"
+		"                 cgroup/post_bind6 | cgroup/connect4 | cgroup/connect6 |\n"
+		"                 cgroup/sendmsg4 | cgroup/sendmsg6 }\n"
 		"       " HELP_SPEC_OPTIONS "\n"
 		"",
 		bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH bpf-next v2 07/12] tools: libbpf: recognize offload neutral maps
  2018-07-09 17:59 [PATCH bpf-next v2 00/12] tools: bpf: extend bpftool prog load Jakub Kicinski
                   ` (5 preceding siblings ...)
  2018-07-09 17:59 ` [PATCH bpf-next v2 06/12] tools: bpftool: allow users to specify program type for prog load Jakub Kicinski
@ 2018-07-09 17:59 ` Jakub Kicinski
  2018-07-09 17:59 ` [PATCH bpf-next v2 08/12] tools: libbpf: add extended attributes version of bpf_object__open() Jakub Kicinski
                   ` (4 subsequent siblings)
  11 siblings, 0 replies; 24+ messages in thread
From: Jakub Kicinski @ 2018-07-09 17:59 UTC (permalink / raw)
  To: alexei.starovoitov, daniel; +Cc: oss-drivers, netdev, Jakub Kicinski

Add helper to libbpf for recognizing maps which should not have
ifindex set when program is loaded.  These maps only contain
host metadata and therefore are not marked for offload, e.g.
the perf event map.

Use this helper in bpf_prog_load_xattr().

Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com>
---
 tools/lib/bpf/libbpf.c | 8 +++++++-
 tools/lib/bpf/libbpf.h | 1 +
 2 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 30f3e58bd563..edc3b0b3737d 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -2154,6 +2154,11 @@ void *bpf_map__priv(struct bpf_map *map)
 	return map ? map->priv : ERR_PTR(-EINVAL);
 }
 
+bool bpf_map__is_offload_neutral(struct bpf_map *map)
+{
+	return map->def.type == BPF_MAP_TYPE_PERF_EVENT_ARRAY;
+}
+
 void bpf_map__set_ifindex(struct bpf_map *map, __u32 ifindex)
 {
 	map->map_ifindex = ifindex;
@@ -2278,7 +2283,8 @@ int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr,
 	}
 
 	bpf_map__for_each(map, obj) {
-		map->map_ifindex = attr->ifindex;
+		if (!bpf_map__is_offload_neutral(map))
+			map->map_ifindex = attr->ifindex;
 	}
 
 	if (!first_prog) {
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index 617dacfc6704..3122d74f2643 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -255,6 +255,7 @@ typedef void (*bpf_map_clear_priv_t)(struct bpf_map *, void *);
 int bpf_map__set_priv(struct bpf_map *map, void *priv,
 		      bpf_map_clear_priv_t clear_priv);
 void *bpf_map__priv(struct bpf_map *map);
+bool bpf_map__is_offload_neutral(struct bpf_map *map);
 void bpf_map__set_ifindex(struct bpf_map *map, __u32 ifindex);
 int bpf_map__pin(struct bpf_map *map, const char *path);
 
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH bpf-next v2 08/12] tools: libbpf: add extended attributes version of bpf_object__open()
  2018-07-09 17:59 [PATCH bpf-next v2 00/12] tools: bpf: extend bpftool prog load Jakub Kicinski
                   ` (6 preceding siblings ...)
  2018-07-09 17:59 ` [PATCH bpf-next v2 07/12] tools: libbpf: recognize offload neutral maps Jakub Kicinski
@ 2018-07-09 17:59 ` Jakub Kicinski
  2018-07-10  5:39   ` Andrey Ignatov
  2018-07-09 17:59 ` [PATCH bpf-next v2 09/12] tools: bpftool: reimplement bpf_prog_load() for prog load Jakub Kicinski
                   ` (3 subsequent siblings)
  11 siblings, 1 reply; 24+ messages in thread
From: Jakub Kicinski @ 2018-07-09 17:59 UTC (permalink / raw)
  To: alexei.starovoitov, daniel; +Cc: oss-drivers, netdev, Jakub Kicinski

Similarly to bpf_prog_load() users of bpf_object__open() may need
to specify the expected program type.  Program type is needed at
open to avoid the kernel version check for program types which don't
require it.

Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com>
---
 tools/lib/bpf/libbpf.c | 21 +++++++++++++++++----
 tools/lib/bpf/libbpf.h |  6 ++++++
 2 files changed, 23 insertions(+), 4 deletions(-)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index edc3b0b3737d..5b0e84fbcf71 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -1520,7 +1520,8 @@ __bpf_object__open(const char *path, void *obj_buf, size_t obj_buf_sz,
 	return ERR_PTR(err);
 }
 
-struct bpf_object *bpf_object__open(const char *path)
+struct bpf_object *bpf_object__open_xattr(const char *path,
+					  struct bpf_object_open_attr *attr)
 {
 	/* param validation */
 	if (!path)
@@ -1528,7 +1529,17 @@ struct bpf_object *bpf_object__open(const char *path)
 
 	pr_debug("loading %s\n", path);
 
-	return __bpf_object__open(path, NULL, 0, true);
+	return __bpf_object__open(path, NULL, 0,
+				  bpf_prog_type__needs_kver(attr->prog_type));
+}
+
+struct bpf_object *bpf_object__open(const char *path)
+{
+	struct bpf_object_open_attr attr = {
+		.prog_type	= BPF_PROG_TYPE_UNSPEC,
+	};
+
+	return bpf_object__open_xattr(path, &attr);
 }
 
 struct bpf_object *bpf_object__open_buffer(void *obj_buf,
@@ -2238,6 +2249,9 @@ int bpf_prog_load(const char *file, enum bpf_prog_type type,
 int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr,
 			struct bpf_object **pobj, int *prog_fd)
 {
+	struct bpf_object_open_attr open_attr = {
+		.prog_type	= attr->prog_type,
+	};
 	struct bpf_program *prog, *first_prog = NULL;
 	enum bpf_attach_type expected_attach_type;
 	enum bpf_prog_type prog_type;
@@ -2250,8 +2264,7 @@ int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr,
 	if (!attr->file)
 		return -EINVAL;
 
-	obj = __bpf_object__open(attr->file, NULL, 0,
-				 bpf_prog_type__needs_kver(attr->prog_type));
+	obj = bpf_object__open_xattr(attr->file, &open_attr);
 	if (IS_ERR_OR_NULL(obj))
 		return -ENOENT;
 
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index 3122d74f2643..60593ac44700 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -66,7 +66,13 @@ void libbpf_set_print(libbpf_print_fn_t warn,
 /* Hide internal to user */
 struct bpf_object;
 
+struct bpf_object_open_attr {
+	enum bpf_prog_type prog_type;
+};
+
 struct bpf_object *bpf_object__open(const char *path);
+struct bpf_object *bpf_object__open_xattr(const char *path,
+					  struct bpf_object_open_attr *attr);
 struct bpf_object *bpf_object__open_buffer(void *obj_buf,
 					   size_t obj_buf_sz,
 					   const char *name);
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH bpf-next v2 09/12] tools: bpftool: reimplement bpf_prog_load() for prog load
  2018-07-09 17:59 [PATCH bpf-next v2 00/12] tools: bpf: extend bpftool prog load Jakub Kicinski
                   ` (7 preceding siblings ...)
  2018-07-09 17:59 ` [PATCH bpf-next v2 08/12] tools: libbpf: add extended attributes version of bpf_object__open() Jakub Kicinski
@ 2018-07-09 17:59 ` Jakub Kicinski
  2018-07-09 19:40   ` Alexei Starovoitov
  2018-07-09 17:59 ` [PATCH bpf-next v2 10/12] tools: bpf: make use of reallocarray Jakub Kicinski
                   ` (2 subsequent siblings)
  11 siblings, 1 reply; 24+ messages in thread
From: Jakub Kicinski @ 2018-07-09 17:59 UTC (permalink / raw)
  To: alexei.starovoitov, daniel; +Cc: oss-drivers, netdev, Jakub Kicinski

bpf_prog_load() is a very useful helper but it doesn't give us full
flexibility of modifying the BPF objects before loading.  Open code
bpf_prog_load() in bpftool so we can add extra logic in following
commits.

Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com>
---
 tools/bpf/bpftool/prog.c | 57 ++++++++++++++++++++++++++++++++--------
 1 file changed, 46 insertions(+), 11 deletions(-)

diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c
index 7a06fd4c5d27..267d653c93f5 100644
--- a/tools/bpf/bpftool/prog.c
+++ b/tools/bpf/bpftool/prog.c
@@ -43,6 +43,8 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 
+#include <linux/err.h>
+
 #include <bpf.h>
 #include <libbpf.h>
 
@@ -682,12 +684,15 @@ static int do_pin(int argc, char **argv)
 
 static int do_load(int argc, char **argv)
 {
-	struct bpf_prog_load_attr attr = {
+	enum bpf_attach_type expected_attach_type;
+	struct bpf_object_open_attr attr = {
 		.prog_type	= BPF_PROG_TYPE_UNSPEC,
 	};
 	const char *objfile, *pinfile;
+	struct bpf_program *prog;
 	struct bpf_object *obj;
-	int prog_fd;
+	struct bpf_map *map;
+	__u32 ifindex = 0;
 	int err;
 
 	if (!REQ_ARGS(2))
@@ -719,7 +724,7 @@ static int do_load(int argc, char **argv)
 			strcat(type, "/");
 
 			err = libbpf_prog_type_by_string(type, &attr.prog_type,
-							 &attr.expected_attach_type);
+							 &expected_attach_type);
 			free(type);
 			if (err < 0) {
 				p_err("unknown program type '%s'", *argv);
@@ -729,15 +734,15 @@ static int do_load(int argc, char **argv)
 		} else if (is_prefix(*argv, "dev")) {
 			NEXT_ARG();
 
-			if (attr.ifindex) {
+			if (ifindex) {
 				p_err("offload device already specified");
 				return -1;
 			}
 			if (!REQ_ARGS(1))
 				return -1;
 
-			attr.ifindex = if_nametoindex(*argv);
-			if (!attr.ifindex) {
+			ifindex = if_nametoindex(*argv);
+			if (!ifindex) {
 				p_err("unrecognized netdevice '%s': %s",
 				      *argv, strerror(errno));
 				return -1;
@@ -750,14 +755,44 @@ static int do_load(int argc, char **argv)
 		}
 	}
 
-	attr.file = objfile;
-
-	if (bpf_prog_load_xattr(&attr, &obj, &prog_fd)) {
-		p_err("failed to load program");
+	obj = bpf_object__open_xattr(objfile, &attr);
+	if (IS_ERR_OR_NULL(obj)) {
+		p_err("failed to open object file");
 		return -1;
 	}
 
-	if (do_pin_fd(prog_fd, pinfile))
+	prog = bpf_program__next(NULL, obj);
+	if (!prog) {
+		p_err("object file doesn't contain any bpf program");
+		goto err_close_obj;
+	}
+
+	bpf_program__set_ifindex(prog, ifindex);
+	if (attr.prog_type == BPF_PROG_TYPE_UNSPEC) {
+		const char *sec_name = bpf_program__title(prog, false);
+
+		err = libbpf_prog_type_by_string(sec_name, &attr.prog_type,
+						 &expected_attach_type);
+		if (err < 0) {
+			p_err("failed to guess program type based on section name %s\n",
+			      sec_name);
+			goto err_close_obj;
+		}
+	}
+	bpf_program__set_type(prog, attr.prog_type);
+	bpf_program__set_expected_attach_type(prog, expected_attach_type);
+
+	bpf_map__for_each(map, obj)
+		if (!bpf_map__is_offload_neutral(map))
+			bpf_map__set_ifindex(map, ifindex);
+
+	err = bpf_object__load(obj);
+	if (err) {
+		p_err("failed to load object file");
+		goto err_close_obj;
+	}
+
+	if (do_pin_fd(bpf_program__fd(prog), pinfile))
 		goto err_close_obj;
 
 	if (json_output)
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH bpf-next v2 10/12] tools: bpf: make use of reallocarray
  2018-07-09 17:59 [PATCH bpf-next v2 00/12] tools: bpf: extend bpftool prog load Jakub Kicinski
                   ` (8 preceding siblings ...)
  2018-07-09 17:59 ` [PATCH bpf-next v2 09/12] tools: bpftool: reimplement bpf_prog_load() for prog load Jakub Kicinski
@ 2018-07-09 17:59 ` Jakub Kicinski
  2018-07-09 17:59 ` [PATCH bpf-next v2 11/12] tools: libbpf: allow map reuse Jakub Kicinski
  2018-07-09 17:59 ` [PATCH bpf-next v2 12/12] tools: bpftool: allow reuse of maps with bpftool prog load Jakub Kicinski
  11 siblings, 0 replies; 24+ messages in thread
From: Jakub Kicinski @ 2018-07-09 17:59 UTC (permalink / raw)
  To: alexei.starovoitov, daniel; +Cc: oss-drivers, netdev, Jakub Kicinski

reallocarray() is a safer variant of realloc which checks for
multiplication overflow in case of array allocation.  Since it's
not available in Glibc < 2.26 import kernel's overflow.h and
add a static inline implementation when needed.  Use feature
detection to probe for existence of reallocarray.

Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com>
Reviewed-by: Jiong Wang <jiong.wang@netronome.com>
---
 tools/bpf/bpftool/Makefile              |   6 +-
 tools/bpf/bpftool/main.h                |   1 +
 tools/bpf/bpftool/xlated_dumper.c       |   6 +-
 tools/build/feature/Makefile            |   4 +
 tools/build/feature/test-reallocarray.c |   8 +
 tools/include/linux/compiler-gcc.h      |   4 +
 tools/include/linux/overflow.h          | 278 ++++++++++++++++++++++++
 tools/include/tools/libc_compat.h       |  23 ++
 tools/lib/bpf/Makefile                  |   6 +-
 tools/lib/bpf/libbpf.c                  |   9 +-
 10 files changed, 336 insertions(+), 9 deletions(-)
 create mode 100644 tools/build/feature/test-reallocarray.c
 create mode 100644 tools/include/linux/overflow.h
 create mode 100644 tools/include/tools/libc_compat.h

diff --git a/tools/bpf/bpftool/Makefile b/tools/bpf/bpftool/Makefile
index 0911b00b25cc..6c4830e18879 100644
--- a/tools/bpf/bpftool/Makefile
+++ b/tools/bpf/bpftool/Makefile
@@ -52,7 +52,7 @@ INSTALL ?= install
 RM ?= rm -f
 
 FEATURE_USER = .bpftool
-FEATURE_TESTS = libbfd disassembler-four-args
+FEATURE_TESTS = libbfd disassembler-four-args reallocarray
 FEATURE_DISPLAY = libbfd disassembler-four-args
 
 check_feat := 1
@@ -75,6 +75,10 @@ ifeq ($(feature-disassembler-four-args), 1)
 CFLAGS += -DDISASM_FOUR_ARGS_SIGNATURE
 endif
 
+ifeq ($(feature-reallocarray), 0)
+CFLAGS += -DCOMPAT_NEED_REALLOCARRAY
+endif
+
 include $(wildcard $(OUTPUT)*.d)
 
 all: $(OUTPUT)bpftool
diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h
index 15b6c49ae533..1e02e4031693 100644
--- a/tools/bpf/bpftool/main.h
+++ b/tools/bpf/bpftool/main.h
@@ -42,6 +42,7 @@
 #include <linux/compiler.h>
 #include <linux/kernel.h>
 #include <linux/hashtable.h>
+#include <tools/libc_compat.h>
 
 #include "json_writer.h"
 
diff --git a/tools/bpf/bpftool/xlated_dumper.c b/tools/bpf/bpftool/xlated_dumper.c
index b97f1da60dd1..3284759df98a 100644
--- a/tools/bpf/bpftool/xlated_dumper.c
+++ b/tools/bpf/bpftool/xlated_dumper.c
@@ -35,6 +35,7 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
+#define _GNU_SOURCE
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -66,9 +67,8 @@ void kernel_syms_load(struct dump_data *dd)
 	while (!feof(fp)) {
 		if (!fgets(buff, sizeof(buff), fp))
 			break;
-		tmp = realloc(dd->sym_mapping,
-			      (dd->sym_count + 1) *
-			      sizeof(*dd->sym_mapping));
+		tmp = reallocarray(dd->sym_mapping, dd->sym_count + 1,
+				   sizeof(*dd->sym_mapping));
 		if (!tmp) {
 out:
 			free(dd->sym_mapping);
diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile
index dac9563b5470..0516259be70f 100644
--- a/tools/build/feature/Makefile
+++ b/tools/build/feature/Makefile
@@ -14,6 +14,7 @@ FILES=                                          \
          test-libaudit.bin                      \
          test-libbfd.bin                        \
          test-disassembler-four-args.bin        \
+         test-reallocarray.bin			\
          test-liberty.bin                       \
          test-liberty-z.bin                     \
          test-cplus-demangle.bin                \
@@ -204,6 +205,9 @@ FLAGS_PERL_EMBED=$(PERL_EMBED_CCOPTS) $(PERL_EMBED_LDOPTS)
 $(OUTPUT)test-disassembler-four-args.bin:
 	$(BUILD) -DPACKAGE='"perf"' -lbfd -lopcodes
 
+$(OUTPUT)test-reallocarray.bin:
+	$(BUILD)
+
 $(OUTPUT)test-liberty.bin:
 	$(CC) $(CFLAGS) -Wall -Werror -o $@ test-libbfd.c -DPACKAGE='"perf"' $(LDFLAGS) -lbfd -ldl -liberty
 
diff --git a/tools/build/feature/test-reallocarray.c b/tools/build/feature/test-reallocarray.c
new file mode 100644
index 000000000000..8170de35150d
--- /dev/null
+++ b/tools/build/feature/test-reallocarray.c
@@ -0,0 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
+#define _GNU_SOURCE
+#include <stdlib.h>
+
+int main(void)
+{
+	return !!reallocarray(NULL, 1, 1);
+}
diff --git a/tools/include/linux/compiler-gcc.h b/tools/include/linux/compiler-gcc.h
index 70fe61295733..0d35f18006a1 100644
--- a/tools/include/linux/compiler-gcc.h
+++ b/tools/include/linux/compiler-gcc.h
@@ -36,3 +36,7 @@
 #endif
 #define __printf(a, b)	__attribute__((format(printf, a, b)))
 #define __scanf(a, b)	__attribute__((format(scanf, a, b)))
+
+#if GCC_VERSION >= 50100
+#define COMPILER_HAS_GENERIC_BUILTIN_OVERFLOW 1
+#endif
diff --git a/tools/include/linux/overflow.h b/tools/include/linux/overflow.h
new file mode 100644
index 000000000000..8712ff70995f
--- /dev/null
+++ b/tools/include/linux/overflow.h
@@ -0,0 +1,278 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+#ifndef __LINUX_OVERFLOW_H
+#define __LINUX_OVERFLOW_H
+
+#include <linux/compiler.h>
+
+/*
+ * In the fallback code below, we need to compute the minimum and
+ * maximum values representable in a given type. These macros may also
+ * be useful elsewhere, so we provide them outside the
+ * COMPILER_HAS_GENERIC_BUILTIN_OVERFLOW block.
+ *
+ * It would seem more obvious to do something like
+ *
+ * #define type_min(T) (T)(is_signed_type(T) ? (T)1 << (8*sizeof(T)-1) : 0)
+ * #define type_max(T) (T)(is_signed_type(T) ? ((T)1 << (8*sizeof(T)-1)) - 1 : ~(T)0)
+ *
+ * Unfortunately, the middle expressions, strictly speaking, have
+ * undefined behaviour, and at least some versions of gcc warn about
+ * the type_max expression (but not if -fsanitize=undefined is in
+ * effect; in that case, the warning is deferred to runtime...).
+ *
+ * The slightly excessive casting in type_min is to make sure the
+ * macros also produce sensible values for the exotic type _Bool. [The
+ * overflow checkers only almost work for _Bool, but that's
+ * a-feature-not-a-bug, since people shouldn't be doing arithmetic on
+ * _Bools. Besides, the gcc builtins don't allow _Bool* as third
+ * argument.]
+ *
+ * Idea stolen from
+ * https://mail-index.netbsd.org/tech-misc/2007/02/05/0000.html -
+ * credit to Christian Biere.
+ */
+#define is_signed_type(type)       (((type)(-1)) < (type)1)
+#define __type_half_max(type) ((type)1 << (8*sizeof(type) - 1 - is_signed_type(type)))
+#define type_max(T) ((T)((__type_half_max(T) - 1) + __type_half_max(T)))
+#define type_min(T) ((T)((T)-type_max(T)-(T)1))
+
+
+#ifdef COMPILER_HAS_GENERIC_BUILTIN_OVERFLOW
+/*
+ * For simplicity and code hygiene, the fallback code below insists on
+ * a, b and *d having the same type (similar to the min() and max()
+ * macros), whereas gcc's type-generic overflow checkers accept
+ * different types. Hence we don't just make check_add_overflow an
+ * alias for __builtin_add_overflow, but add type checks similar to
+ * below.
+ */
+#define check_add_overflow(a, b, d) ({		\
+	typeof(a) __a = (a);			\
+	typeof(b) __b = (b);			\
+	typeof(d) __d = (d);			\
+	(void) (&__a == &__b);			\
+	(void) (&__a == __d);			\
+	__builtin_add_overflow(__a, __b, __d);	\
+})
+
+#define check_sub_overflow(a, b, d) ({		\
+	typeof(a) __a = (a);			\
+	typeof(b) __b = (b);			\
+	typeof(d) __d = (d);			\
+	(void) (&__a == &__b);			\
+	(void) (&__a == __d);			\
+	__builtin_sub_overflow(__a, __b, __d);	\
+})
+
+#define check_mul_overflow(a, b, d) ({		\
+	typeof(a) __a = (a);			\
+	typeof(b) __b = (b);			\
+	typeof(d) __d = (d);			\
+	(void) (&__a == &__b);			\
+	(void) (&__a == __d);			\
+	__builtin_mul_overflow(__a, __b, __d);	\
+})
+
+#else
+
+
+/* Checking for unsigned overflow is relatively easy without causing UB. */
+#define __unsigned_add_overflow(a, b, d) ({	\
+	typeof(a) __a = (a);			\
+	typeof(b) __b = (b);			\
+	typeof(d) __d = (d);			\
+	(void) (&__a == &__b);			\
+	(void) (&__a == __d);			\
+	*__d = __a + __b;			\
+	*__d < __a;				\
+})
+#define __unsigned_sub_overflow(a, b, d) ({	\
+	typeof(a) __a = (a);			\
+	typeof(b) __b = (b);			\
+	typeof(d) __d = (d);			\
+	(void) (&__a == &__b);			\
+	(void) (&__a == __d);			\
+	*__d = __a - __b;			\
+	__a < __b;				\
+})
+/*
+ * If one of a or b is a compile-time constant, this avoids a division.
+ */
+#define __unsigned_mul_overflow(a, b, d) ({		\
+	typeof(a) __a = (a);				\
+	typeof(b) __b = (b);				\
+	typeof(d) __d = (d);				\
+	(void) (&__a == &__b);				\
+	(void) (&__a == __d);				\
+	*__d = __a * __b;				\
+	__builtin_constant_p(__b) ?			\
+	  __b > 0 && __a > type_max(typeof(__a)) / __b : \
+	  __a > 0 && __b > type_max(typeof(__b)) / __a;	 \
+})
+
+/*
+ * For signed types, detecting overflow is much harder, especially if
+ * we want to avoid UB. But the interface of these macros is such that
+ * we must provide a result in *d, and in fact we must produce the
+ * result promised by gcc's builtins, which is simply the possibly
+ * wrapped-around value. Fortunately, we can just formally do the
+ * operations in the widest relevant unsigned type (u64) and then
+ * truncate the result - gcc is smart enough to generate the same code
+ * with and without the (u64) casts.
+ */
+
+/*
+ * Adding two signed integers can overflow only if they have the same
+ * sign, and overflow has happened iff the result has the opposite
+ * sign.
+ */
+#define __signed_add_overflow(a, b, d) ({	\
+	typeof(a) __a = (a);			\
+	typeof(b) __b = (b);			\
+	typeof(d) __d = (d);			\
+	(void) (&__a == &__b);			\
+	(void) (&__a == __d);			\
+	*__d = (u64)__a + (u64)__b;		\
+	(((~(__a ^ __b)) & (*__d ^ __a))	\
+		& type_min(typeof(__a))) != 0;	\
+})
+
+/*
+ * Subtraction is similar, except that overflow can now happen only
+ * when the signs are opposite. In this case, overflow has happened if
+ * the result has the opposite sign of a.
+ */
+#define __signed_sub_overflow(a, b, d) ({	\
+	typeof(a) __a = (a);			\
+	typeof(b) __b = (b);			\
+	typeof(d) __d = (d);			\
+	(void) (&__a == &__b);			\
+	(void) (&__a == __d);			\
+	*__d = (u64)__a - (u64)__b;		\
+	((((__a ^ __b)) & (*__d ^ __a))		\
+		& type_min(typeof(__a))) != 0;	\
+})
+
+/*
+ * Signed multiplication is rather hard. gcc always follows C99, so
+ * division is truncated towards 0. This means that we can write the
+ * overflow check like this:
+ *
+ * (a > 0 && (b > MAX/a || b < MIN/a)) ||
+ * (a < -1 && (b > MIN/a || b < MAX/a) ||
+ * (a == -1 && b == MIN)
+ *
+ * The redundant casts of -1 are to silence an annoying -Wtype-limits
+ * (included in -Wextra) warning: When the type is u8 or u16, the
+ * __b_c_e in check_mul_overflow obviously selects
+ * __unsigned_mul_overflow, but unfortunately gcc still parses this
+ * code and warns about the limited range of __b.
+ */
+
+#define __signed_mul_overflow(a, b, d) ({				\
+	typeof(a) __a = (a);						\
+	typeof(b) __b = (b);						\
+	typeof(d) __d = (d);						\
+	typeof(a) __tmax = type_max(typeof(a));				\
+	typeof(a) __tmin = type_min(typeof(a));				\
+	(void) (&__a == &__b);						\
+	(void) (&__a == __d);						\
+	*__d = (u64)__a * (u64)__b;					\
+	(__b > 0   && (__a > __tmax/__b || __a < __tmin/__b)) ||	\
+	(__b < (typeof(__b))-1  && (__a > __tmin/__b || __a < __tmax/__b)) || \
+	(__b == (typeof(__b))-1 && __a == __tmin);			\
+})
+
+
+#define check_add_overflow(a, b, d)					\
+	__builtin_choose_expr(is_signed_type(typeof(a)),		\
+			__signed_add_overflow(a, b, d),			\
+			__unsigned_add_overflow(a, b, d))
+
+#define check_sub_overflow(a, b, d)					\
+	__builtin_choose_expr(is_signed_type(typeof(a)),		\
+			__signed_sub_overflow(a, b, d),			\
+			__unsigned_sub_overflow(a, b, d))
+
+#define check_mul_overflow(a, b, d)					\
+	__builtin_choose_expr(is_signed_type(typeof(a)),		\
+			__signed_mul_overflow(a, b, d),			\
+			__unsigned_mul_overflow(a, b, d))
+
+
+#endif /* COMPILER_HAS_GENERIC_BUILTIN_OVERFLOW */
+
+/**
+ * array_size() - Calculate size of 2-dimensional array.
+ *
+ * @a: dimension one
+ * @b: dimension two
+ *
+ * Calculates size of 2-dimensional array: @a * @b.
+ *
+ * Returns: number of bytes needed to represent the array or SIZE_MAX on
+ * overflow.
+ */
+static inline __must_check size_t array_size(size_t a, size_t b)
+{
+	size_t bytes;
+
+	if (check_mul_overflow(a, b, &bytes))
+		return SIZE_MAX;
+
+	return bytes;
+}
+
+/**
+ * array3_size() - Calculate size of 3-dimensional array.
+ *
+ * @a: dimension one
+ * @b: dimension two
+ * @c: dimension three
+ *
+ * Calculates size of 3-dimensional array: @a * @b * @c.
+ *
+ * Returns: number of bytes needed to represent the array or SIZE_MAX on
+ * overflow.
+ */
+static inline __must_check size_t array3_size(size_t a, size_t b, size_t c)
+{
+	size_t bytes;
+
+	if (check_mul_overflow(a, b, &bytes))
+		return SIZE_MAX;
+	if (check_mul_overflow(bytes, c, &bytes))
+		return SIZE_MAX;
+
+	return bytes;
+}
+
+static inline __must_check size_t __ab_c_size(size_t n, size_t size, size_t c)
+{
+	size_t bytes;
+
+	if (check_mul_overflow(n, size, &bytes))
+		return SIZE_MAX;
+	if (check_add_overflow(bytes, c, &bytes))
+		return SIZE_MAX;
+
+	return bytes;
+}
+
+/**
+ * struct_size() - Calculate size of structure with trailing array.
+ * @p: Pointer to the structure.
+ * @member: Name of the array member.
+ * @n: Number of elements in the array.
+ *
+ * Calculates size of memory needed for structure @p followed by an
+ * array of @n @member elements.
+ *
+ * Return: number of bytes needed or SIZE_MAX on overflow.
+ */
+#define struct_size(p, member, n)					\
+	__ab_c_size(n,							\
+		    sizeof(*(p)->member) + __must_be_array((p)->member),\
+		    sizeof(*(p)))
+
+#endif /* __LINUX_OVERFLOW_H */
diff --git a/tools/include/tools/libc_compat.h b/tools/include/tools/libc_compat.h
new file mode 100644
index 000000000000..7bc720f54ef3
--- /dev/null
+++ b/tools/include/tools/libc_compat.h
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (C) 2018 Netronome Systems, Inc. */
+
+#ifndef __TOOLS_LIBC_COMPAT_H
+#define __TOOLS_LIBC_COMPAT_H
+
+#include <stdlib.h>
+#include <linux/overflow.h>
+
+/* !defined(_GNU_SOURCE) because e.g. libbpf can't use GNU strerror_r()
+ * for backward compatibility reasons.
+ */
+#if defined(COMPAT_NEED_REALLOCARRAY) || !defined(_GNU_SOURCE)
+static inline void *reallocarray(void *ptr, size_t nmemb, size_t size)
+{
+	size_t bytes;
+
+	if (unlikely(check_mul_overflow(nmemb, size, &bytes)))
+		return NULL;
+	return realloc(ptr, bytes);
+}
+#endif
+#endif
diff --git a/tools/lib/bpf/Makefile b/tools/lib/bpf/Makefile
index 5390e7725e43..7a8e4c98ef1a 100644
--- a/tools/lib/bpf/Makefile
+++ b/tools/lib/bpf/Makefile
@@ -66,7 +66,7 @@ ifndef VERBOSE
 endif
 
 FEATURE_USER = .libbpf
-FEATURE_TESTS = libelf libelf-getphdrnum libelf-mmap bpf
+FEATURE_TESTS = libelf libelf-getphdrnum libelf-mmap bpf reallocarray
 FEATURE_DISPLAY = libelf bpf
 
 INCLUDES = -I. -I$(srctree)/tools/include -I$(srctree)/tools/arch/$(ARCH)/include/uapi -I$(srctree)/tools/include/uapi -I$(srctree)/tools/perf
@@ -120,6 +120,10 @@ ifeq ($(feature-libelf-getphdrnum), 1)
   override CFLAGS += -DHAVE_ELF_GETPHDRNUM_SUPPORT
 endif
 
+ifeq ($(feature-reallocarray), 0)
+  override CFLAGS += -DCOMPAT_NEED_REALLOCARRAY
+endif
+
 # Append required CFLAGS
 override CFLAGS += $(EXTRA_WARNINGS)
 override CFLAGS += -Werror -Wall
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 5b0e84fbcf71..b653dbb266c7 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -41,6 +41,7 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/vfs.h>
+#include <tools/libc_compat.h>
 #include <libelf.h>
 #include <gelf.h>
 
@@ -369,7 +370,7 @@ bpf_object__add_program(struct bpf_object *obj, void *data, size_t size,
 	progs = obj->programs;
 	nr_progs = obj->nr_programs;
 
-	progs = realloc(progs, sizeof(progs[0]) * (nr_progs + 1));
+	progs = reallocarray(progs, nr_progs + 1, sizeof(progs[0]));
 	if (!progs) {
 		/*
 		 * In this case the original obj->programs
@@ -870,8 +871,8 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
 				continue;
 			}
 
-			reloc = realloc(reloc,
-					sizeof(*obj->efile.reloc) * nr_reloc);
+			reloc = reallocarray(reloc, nr_reloc,
+					     sizeof(*obj->efile.reloc));
 			if (!reloc) {
 				pr_warning("realloc failed\n");
 				err = -ENOMEM;
@@ -1163,7 +1164,7 @@ bpf_program__reloc_text(struct bpf_program *prog, struct bpf_object *obj,
 			return -LIBBPF_ERRNO__RELOC;
 		}
 		new_cnt = prog->insns_cnt + text->insns_cnt;
-		new_insn = realloc(prog->insns, new_cnt * sizeof(*insn));
+		new_insn = reallocarray(prog->insns, new_cnt, sizeof(*insn));
 		if (!new_insn) {
 			pr_warning("oom in prog realloc\n");
 			return -ENOMEM;
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH bpf-next v2 11/12] tools: libbpf: allow map reuse
  2018-07-09 17:59 [PATCH bpf-next v2 00/12] tools: bpf: extend bpftool prog load Jakub Kicinski
                   ` (9 preceding siblings ...)
  2018-07-09 17:59 ` [PATCH bpf-next v2 10/12] tools: bpf: make use of reallocarray Jakub Kicinski
@ 2018-07-09 17:59 ` Jakub Kicinski
  2018-07-09 20:22   ` Andrey Ignatov
  2018-07-09 17:59 ` [PATCH bpf-next v2 12/12] tools: bpftool: allow reuse of maps with bpftool prog load Jakub Kicinski
  11 siblings, 1 reply; 24+ messages in thread
From: Jakub Kicinski @ 2018-07-09 17:59 UTC (permalink / raw)
  To: alexei.starovoitov, daniel; +Cc: oss-drivers, netdev, Jakub Kicinski

More advanced applications may want to only replace programs without
destroying associated maps.  Allow libbpf users to achieve that.
Instead of always creating all of the maps at load time, expose to
users an API to reconstruct the map object from already existing
map.

The map parameters are read from the kernel and replace the parameters
of the ELF map.  libbpf does not restrict the map replacement, i.e.
the reused map does not have to be compatible with the ELF map
definition.  We relay on the verifier for checking the compatibility
between maps and programs.  The ELF map definition is completely
overwritten by the information read from the kernel, to make sure
libbpf's view of map object corresponds to the actual map.

Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com>
---
 tools/lib/bpf/libbpf.c | 35 +++++++++++++++++++++++++++++++++++
 tools/lib/bpf/libbpf.h |  1 +
 2 files changed, 36 insertions(+)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index b653dbb266c7..c80033fe66c3 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -215,6 +215,7 @@ struct bpf_map {
 	int fd;
 	char *name;
 	size_t offset;
+	bool fd_preset;
 	int map_ifindex;
 	struct bpf_map_def def;
 	uint32_t btf_key_type_id;
@@ -1082,6 +1083,34 @@ static int bpf_map_find_btf_info(struct bpf_map *map, const struct btf *btf)
 	return 0;
 }
 
+int bpf_map__reuse_fd(struct bpf_map *map, int fd)
+{
+	struct bpf_map_info info = {};
+	__u32 len = sizeof(info);
+	int err;
+
+	err = bpf_obj_get_info_by_fd(fd, &info, &len);
+	if (err)
+		return err;
+
+	map->fd = dup(fd);
+	if (map->fd < 0)
+		return map->fd;
+	map->fd_preset = true;
+
+	free(map->name);
+	map->name = strdup(info.name);
+	map->def.type = info.type;
+	map->def.key_size = info.key_size;
+	map->def.value_size = info.value_size;
+	map->def.max_entries = info.max_entries;
+	map->def.map_flags = info.map_flags;
+	map->btf_key_type_id = info.btf_key_type_id;
+	map->btf_value_type_id = info.btf_value_type_id;
+
+	return 0;
+}
+
 static int
 bpf_object__create_maps(struct bpf_object *obj)
 {
@@ -1094,6 +1123,12 @@ bpf_object__create_maps(struct bpf_object *obj)
 		struct bpf_map_def *def = &map->def;
 		int *pfd = &map->fd;
 
+		if (map->fd_preset) {
+			pr_debug("skip map create (preset) %s: fd=%d\n",
+				 map->name, map->fd);
+			continue;
+		}
+
 		create_attr.name = map->name;
 		create_attr.map_ifindex = map->map_ifindex;
 		create_attr.map_type = def->type;
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index 60593ac44700..8e709a74f47c 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -261,6 +261,7 @@ typedef void (*bpf_map_clear_priv_t)(struct bpf_map *, void *);
 int bpf_map__set_priv(struct bpf_map *map, void *priv,
 		      bpf_map_clear_priv_t clear_priv);
 void *bpf_map__priv(struct bpf_map *map);
+int bpf_map__reuse_fd(struct bpf_map *map, int fd);
 bool bpf_map__is_offload_neutral(struct bpf_map *map);
 void bpf_map__set_ifindex(struct bpf_map *map, __u32 ifindex);
 int bpf_map__pin(struct bpf_map *map, const char *path);
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH bpf-next v2 12/12] tools: bpftool: allow reuse of maps with bpftool prog load
  2018-07-09 17:59 [PATCH bpf-next v2 00/12] tools: bpf: extend bpftool prog load Jakub Kicinski
                   ` (10 preceding siblings ...)
  2018-07-09 17:59 ` [PATCH bpf-next v2 11/12] tools: libbpf: allow map reuse Jakub Kicinski
@ 2018-07-09 17:59 ` Jakub Kicinski
  2018-07-09 19:48   ` Alexei Starovoitov
  11 siblings, 1 reply; 24+ messages in thread
From: Jakub Kicinski @ 2018-07-09 17:59 UTC (permalink / raw)
  To: alexei.starovoitov, daniel; +Cc: oss-drivers, netdev, Jakub Kicinski

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.

Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com>
---
 .../bpftool/Documentation/bpftool-prog.rst    |  20 ++-
 tools/bpf/bpftool/bash-completion/bpftool     |  67 +++++++-
 tools/bpf/bpftool/main.h                      |   3 +
 tools/bpf/bpftool/map.c                       |   4 +-
 tools/bpf/bpftool/prog.c                      | 148 ++++++++++++++++--
 5 files changed, 219 insertions(+), 23 deletions(-)

diff --git a/tools/bpf/bpftool/Documentation/bpftool-prog.rst b/tools/bpf/bpftool/Documentation/bpftool-prog.rst
index e53e1ad2caf0..64156a16d530 100644
--- a/tools/bpf/bpftool/Documentation/bpftool-prog.rst
+++ b/tools/bpf/bpftool/Documentation/bpftool-prog.rst
@@ -24,9 +24,10 @@ MAP COMMANDS
 |	**bpftool** **prog dump xlated** *PROG* [{**file** *FILE* | **opcodes** | **visual**}]
 |	**bpftool** **prog dump jited**  *PROG* [{**file** *FILE* | **opcodes**}]
 |	**bpftool** **prog pin** *PROG* *FILE*
-|	**bpftool** **prog load** *OBJ* *FILE* [**type** *TYPE*] [**dev** *NAME*]
+|	**bpftool** **prog load** *OBJ* *FILE* [**type** *TYPE*] [**map** {**idx** *IDX* | **name** *NAME*} *MAP*] [**dev** *NAME*]
 |	**bpftool** **prog help**
 |
+|	*MAP* := { **id** *MAP_ID* | **pinned** *FILE* }
 |	*PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* }
 |	*TYPE* := {
 |		**socket** | **kprobe** | **kretprobe** | **classifier** | **action** |
@@ -73,10 +74,17 @@ DESCRIPTION
 
 		  Note: *FILE* must be located in *bpffs* mount.
 
-	**bpftool prog load** *OBJ* *FILE* [**type** *TYPE*] [**dev** *NAME*]
+	**bpftool prog load** *OBJ* *FILE* [**type** *TYPE*] [**map** {**idx** *IDX* | **name** *NAME*} *MAP*] [**dev** *NAME*]
 		  Load bpf program from binary *OBJ* and pin as *FILE*.
 		  **type** is optional, if not specified program type will be
 		  inferred from section names.
+		  By default bpftool will create new maps as declared in the ELF
+		  object being loaded.  **map** parameter allows for the reuse
+		  of existing maps.  It can be specified multiple times, each
+		  time for a different map.  *IDX* refers to index of the map
+		  to be replaced in the ELF file counting from 0, while *NAME*
+		  allows to replace a map by name.  *MAP* specifies the map to
+		  use, referring to it by **id** or through a **pinned** file.
 		  If **dev** *NAME* is specified program will be loaded onto
 		  given networking device (offload).
 
@@ -172,6 +180,14 @@ EXAMPLES
     mov    %rbx,0x0(%rbp)
     48 89 5d 00
 
+|
+| **# bpftool prog load xdp1_kern.o /sys/fs/bpf/xdp1 type xdp map name rxcnt id 7**
+| **# bpftool prog show pinned /sys/fs/bpf/xdp1**
+|   9: xdp  name xdp_prog1  tag 539ec6ce11b52f98  gpl
+|	loaded_at 2018-06-25T16:17:31-0700  uid 0
+|	xlated 488B  jited 336B  memlock 4096B  map_ids 7
+| **# rm /sys/fs/bpf/xdp1**
+|
 
 SEE ALSO
 ========
diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool
index caf8711993be..598066c40191 100644
--- a/tools/bpf/bpftool/bash-completion/bpftool
+++ b/tools/bpf/bpftool/bash-completion/bpftool
@@ -99,6 +99,29 @@ _bpftool_get_prog_tags()
         command sed -n 's/.*"tag": "\(.*\)",$/\1/p' )" -- "$cur" ) )
 }
 
+_bpftool_get_obj_map_names()
+{
+    local obj
+
+    obj=$1
+
+    maps=$(objdump -j maps -t $obj 2>/dev/null | \
+        command awk '/g     . maps/ {print $NF}')
+
+    COMPREPLY+=( $( compgen -W "$maps" -- "$cur" ) )
+}
+
+_bpftool_get_obj_map_idxs()
+{
+    local obj
+
+    obj=$1
+
+    nmaps=$(objdump -j maps -t $obj 2>/dev/null | grep -c 'g     . maps')
+
+    COMPREPLY+=( $( compgen -W "$(seq 0 $((nmaps - 1)))" -- "$cur" ) )
+}
+
 _sysfs_get_netdevs()
 {
     COMPREPLY+=( $( compgen -W "$( ls /sys/class/net 2>/dev/null )" -- \
@@ -220,12 +243,14 @@ _bpftool()
     # Completion depends on object and command in use
     case $object in
         prog)
-            case $prev in
-                id)
-                    _bpftool_get_prog_ids
-                    return 0
-                    ;;
-            esac
+            if [[ $command != "load" ]]; then
+                case $prev in
+                    id)
+                        _bpftool_get_prog_ids
+                        return 0
+                        ;;
+                esac
+            fi
 
             local PROG_TYPE='id pinned tag'
             case $command in
@@ -268,22 +293,52 @@ _bpftool()
                     return 0
                     ;;
                 load)
+                    local obj
+
                     if [[ ${#words[@]} -lt 6 ]]; then
                         _filedir
                         return 0
                     fi
 
+                    obj=${words[3]}
+
+                    if [[ ${words[-4]} == "map" ]]; then
+                        COMPREPLY=( $( compgen -W "id pinned" -- "$cur" ) )
+                        return 0
+                    fi
+                    if [[ ${words[-3]} == "map" ]]; then
+                        if [[ ${words[-2]} == "idx" ]]; then
+                            _bpftool_get_obj_map_idxs $obj
+                        elif [[ ${words[-2]} == "name" ]]; then
+                            _bpftool_get_obj_map_names $obj
+                        fi
+                        return 0
+                    fi
+                    if [[ ${words[-2]} == "map" ]]; then
+                        COMPREPLY=( $( compgen -W "idx name" -- "$cur" ) )
+                        return 0
+                    fi
+
                     case $prev in
                         type)
                             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" -- \
                                                    "$cur" ) )
                             return 0
                             ;;
+                        id)
+                            _bpftool_get_map_ids
+                            return 0
+                            ;;
+                        pinned)
+                            _filedir
+                            return 0
+                            ;;
                         dev)
                             _sysfs_get_netdevs
                             return 0
                             ;;
                         *)
+                            COMPREPLY=( $( compgen -W "map" -- "$cur" ) )
                             _bpftool_once_attr 'type'
                             _bpftool_once_attr 'dev'
                             return 0
diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h
index 1e02e4031693..41004bb2644a 100644
--- a/tools/bpf/bpftool/main.h
+++ b/tools/bpf/bpftool/main.h
@@ -75,6 +75,8 @@
 	"PROG := { id PROG_ID | pinned FILE | tag PROG_TAG }"
 #define HELP_SPEC_OPTIONS						\
 	"OPTIONS := { {-j|--json} [{-p|--pretty}] | {-f|--bpffs} }"
+#define HELP_SPEC_MAP							\
+	"MAP := { id MAP_ID | pinned FILE }"
 
 enum bpf_obj_type {
 	BPF_OBJ_UNKNOWN,
@@ -136,6 +138,7 @@ int do_cgroup(int argc, char **arg);
 int do_perf(int argc, char **arg);
 
 int prog_parse_fd(int *argc, char ***argv);
+int map_parse_fd(int *argc, char ***argv);
 int map_parse_fd_and_info(int *argc, char ***argv, void *info, __u32 *info_len);
 
 void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,
diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c
index 5989e1575ae4..e2baec1122fb 100644
--- a/tools/bpf/bpftool/map.c
+++ b/tools/bpf/bpftool/map.c
@@ -93,7 +93,7 @@ static void *alloc_value(struct bpf_map_info *info)
 		return malloc(info->value_size);
 }
 
-static int map_parse_fd(int *argc, char ***argv)
+int map_parse_fd(int *argc, char ***argv)
 {
 	int fd;
 
@@ -824,7 +824,7 @@ static int do_help(int argc, char **argv)
 		"       %s %s event_pipe MAP [cpu N index M]\n"
 		"       %s %s help\n"
 		"\n"
-		"       MAP := { id MAP_ID | pinned FILE }\n"
+		"       " HELP_SPEC_MAP "\n"
 		"       DATA := { [hex] BYTES }\n"
 		"       " HELP_SPEC_PROGRAM "\n"
 		"       VALUE := { DATA | MAP | PROG }\n"
diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c
index 267d653c93f5..0f8bdab62864 100644
--- a/tools/bpf/bpftool/prog.c
+++ b/tools/bpf/bpftool/prog.c
@@ -31,6 +31,7 @@
  * SOFTWARE.
  */
 
+#define _GNU_SOURCE
 #include <errno.h>
 #include <fcntl.h>
 #include <stdarg.h>
@@ -682,18 +683,34 @@ static int do_pin(int argc, char **argv)
 	return err;
 }
 
+struct map_replace {
+	int idx;
+	int fd;
+	char *name;
+};
+
+int map_replace_compar(const void *p1, const void *p2)
+{
+	const struct map_replace *a = p1, *b = p2;
+
+	return a->idx - b->idx;
+}
+
 static int do_load(int argc, char **argv)
 {
 	enum bpf_attach_type expected_attach_type;
 	struct bpf_object_open_attr attr = {
 		.prog_type	= BPF_PROG_TYPE_UNSPEC,
 	};
+	struct map_replace *map_replace = NULL;
 	const char *objfile, *pinfile;
+	unsigned int old_map_fds = 0;
 	struct bpf_program *prog;
 	struct bpf_object *obj;
 	struct bpf_map *map;
+	unsigned int i, j;
 	__u32 ifindex = 0;
-	int err;
+	int idx, err;
 
 	if (!REQ_ARGS(2))
 		return -1;
@@ -708,16 +725,16 @@ static int do_load(int argc, char **argv)
 
 			if (attr.prog_type != BPF_PROG_TYPE_UNSPEC) {
 				p_err("program type already specified");
-				return -1;
+				goto err_free_reuse_maps;
 			}
 			if (!REQ_ARGS(1))
-				return -1;
+				goto err_free_reuse_maps;
 
 			/* Put a '/' at the end of type to appease libbpf */
 			type = malloc(strlen(*argv) + 2);
 			if (!type) {
 				p_err("mem alloc failed");
-				return -1;
+				goto err_free_reuse_maps;
 			}
 			*type = 0;
 			strcat(type, *argv);
@@ -728,37 +745,81 @@ static int do_load(int argc, char **argv)
 			free(type);
 			if (err < 0) {
 				p_err("unknown program type '%s'", *argv);
-				return err;
+				goto err_free_reuse_maps;
 			}
 			NEXT_ARG();
+		} else if (is_prefix(*argv, "map")) {
+			char *endptr, *name;
+			int fd;
+
+			NEXT_ARG();
+
+			if (!REQ_ARGS(4))
+				goto err_free_reuse_maps;
+
+			if (is_prefix(*argv, "idx")) {
+				NEXT_ARG();
+
+				idx = strtoul(*argv, &endptr, 0);
+				if (*endptr) {
+					p_err("can't parse %s as IDX", *argv);
+					goto err_free_reuse_maps;
+				}
+				name = NULL;
+			} else if (is_prefix(*argv, "name")) {
+				NEXT_ARG();
+
+				name = *argv;
+				idx = -1;
+			} else {
+				p_err("expected 'idx' or 'name', got: '%s'?",
+				      *argv);
+				goto err_free_reuse_maps;
+			}
+			NEXT_ARG();
+
+			fd = map_parse_fd(&argc, &argv);
+			if (fd < 0)
+				goto err_free_reuse_maps;
+
+			map_replace = reallocarray(map_replace, old_map_fds + 1,
+						   sizeof(*map_replace));
+			if (!map_replace) {
+				p_err("mem alloc failed");
+				goto err_free_reuse_maps;
+			}
+			map_replace[old_map_fds].idx = idx;
+			map_replace[old_map_fds].name = name;
+			map_replace[old_map_fds].fd = fd;
+			old_map_fds++;
 		} else if (is_prefix(*argv, "dev")) {
 			NEXT_ARG();
 
 			if (ifindex) {
 				p_err("offload device already specified");
-				return -1;
+				goto err_free_reuse_maps;
 			}
 			if (!REQ_ARGS(1))
-				return -1;
+				goto err_free_reuse_maps;
 
 			ifindex = if_nametoindex(*argv);
 			if (!ifindex) {
 				p_err("unrecognized netdevice '%s': %s",
 				      *argv, strerror(errno));
-				return -1;
+				goto err_free_reuse_maps;
 			}
 			NEXT_ARG();
 		} else {
-			p_err("expected no more arguments, 'type' or 'dev', got: '%s'?",
+			p_err("expected no more arguments, 'type', 'map' or 'dev', got: '%s'?",
 			      *argv);
-			return -1;
+			goto err_free_reuse_maps;
 		}
 	}
 
 	obj = bpf_object__open_xattr(objfile, &attr);
 	if (IS_ERR_OR_NULL(obj)) {
 		p_err("failed to open object file");
-		return -1;
+		goto err_free_reuse_maps;
 	}
 
 	prog = bpf_program__next(NULL, obj);
@@ -782,10 +843,62 @@ static int do_load(int argc, char **argv)
 	bpf_program__set_type(prog, attr.prog_type);
 	bpf_program__set_expected_attach_type(prog, expected_attach_type);
 
-	bpf_map__for_each(map, obj)
+	qsort(map_replace, old_map_fds, sizeof(*map_replace),
+	      map_replace_compar);
+
+	/* After the sort maps by name will be first on the list, because they
+	 * have idx == -1.  Resolve them.
+	 */
+	j = 0;
+	while (j < old_map_fds && map_replace[j].name) {
+		i = 0;
+		bpf_map__for_each(map, obj) {
+			if (!strcmp(bpf_map__name(map), map_replace[j].name)) {
+				map_replace[j].idx = i;
+				break;
+			}
+			i++;
+		}
+		if (map_replace[j].idx == -1) {
+			p_err("unable to find map '%s'", map_replace[j].name);
+			goto err_close_obj;
+		}
+		j++;
+	}
+	/* Resort if any names were resolved */
+	if (j)
+		qsort(map_replace, old_map_fds, sizeof(*map_replace),
+		      map_replace_compar);
+
+	/* Set ifindex and name reuse */
+	j = 0;
+	idx = 0;
+	bpf_map__for_each(map, obj) {
 		if (!bpf_map__is_offload_neutral(map))
 			bpf_map__set_ifindex(map, ifindex);
 
+		if (j < old_map_fds && idx == map_replace[j].idx) {
+			err = bpf_map__reuse_fd(map, map_replace[j++].fd);
+			if (err) {
+				p_err("unable to set up map reuse: %d", err);
+				goto err_close_obj;
+			}
+
+			/* Next reuse wants to apply to the same map */
+			if (j < old_map_fds && map_replace[j].idx == idx) {
+				p_err("replacement for map idx %d specified more than once",
+				      idx);
+				goto err_close_obj;
+			}
+		}
+
+		idx++;
+	}
+	if (j < old_map_fds) {
+		p_err("map idx '%d' not used", map_replace[j].idx);
+		goto err_close_obj;
+	}
+
 	err = bpf_object__load(obj);
 	if (err) {
 		p_err("failed to load object file");
@@ -799,11 +912,18 @@ static int do_load(int argc, char **argv)
 		jsonw_null(json_wtr);
 
 	bpf_object__close(obj);
+	for (i = 0; i < old_map_fds; i++)
+		close(map_replace[i].fd);
+	free(map_replace);
 
 	return 0;
 
 err_close_obj:
 	bpf_object__close(obj);
+err_free_reuse_maps:
+	for (i = 0; i < old_map_fds; i++)
+		close(map_replace[i].fd);
+	free(map_replace);
 	return -1;
 }
 
@@ -819,9 +939,11 @@ static int do_help(int argc, char **argv)
 		"       %s %s dump xlated PROG [{ file FILE | opcodes | visual }]\n"
 		"       %s %s dump jited  PROG [{ file FILE | opcodes }]\n"
 		"       %s %s pin   PROG FILE\n"
-		"       %s %s load  OBJ  FILE [type TYPE] [dev NAME]\n"
+		"       %s %s load  OBJ  FILE [type TYPE] [dev NAME] \\\n"
+		"                         [map { idx IDX | name NAME } MAP]\n"
 		"       %s %s help\n"
 		"\n"
+		"       " HELP_SPEC_MAP "\n"
 		"       " HELP_SPEC_PROGRAM "\n"
 		"       TYPE := { socket | kprobe | kretprobe | classifier | action |\n"
 		"                 tracepoint | raw_tracepoint | xdp | perf_event | cgroup/skb |\n"
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* Re: [PATCH bpf-next v2 09/12] tools: bpftool: reimplement bpf_prog_load() for prog load
  2018-07-09 17:59 ` [PATCH bpf-next v2 09/12] tools: bpftool: reimplement bpf_prog_load() for prog load Jakub Kicinski
@ 2018-07-09 19:40   ` Alexei Starovoitov
  0 siblings, 0 replies; 24+ messages in thread
From: Alexei Starovoitov @ 2018-07-09 19:40 UTC (permalink / raw)
  To: Jakub Kicinski; +Cc: daniel, oss-drivers, netdev

On Mon, Jul 09, 2018 at 10:59:41AM -0700, Jakub Kicinski wrote:
> bpf_prog_load() is a very useful helper but it doesn't give us full
> flexibility of modifying the BPF objects before loading.  Open code
> bpf_prog_load() in bpftool so we can add extra logic in following
> commits.
> 
> Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
> Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com>

Acked-by: Alexei Starovoitov <ast@kernel.org>

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH bpf-next v2 12/12] tools: bpftool: allow reuse of maps with bpftool prog load
  2018-07-09 17:59 ` [PATCH bpf-next v2 12/12] tools: bpftool: allow reuse of maps with bpftool prog load Jakub Kicinski
@ 2018-07-09 19:48   ` Alexei Starovoitov
  2018-07-10  1:38     ` Jakub Kicinski
  0 siblings, 1 reply; 24+ messages in thread
From: Alexei Starovoitov @ 2018-07-09 19:48 UTC (permalink / raw)
  To: Jakub Kicinski; +Cc: daniel, oss-drivers, netdev

On Mon, Jul 09, 2018 at 10:59:44AM -0700, Jakub Kicinski wrote:
> 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.
> 
> Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
> Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com>

cmdline interface feels a bit awkward to use, but it's a nice improvement.
Acked-by: Alexei Starovoitov <ast@kernel.org>

any plans to extend bpf_map_def similar to iproute2 ?
so things like pinned file name and map reuse can be specified in .c file
instead of cmdline?

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH bpf-next v2 11/12] tools: libbpf: allow map reuse
  2018-07-09 17:59 ` [PATCH bpf-next v2 11/12] tools: libbpf: allow map reuse Jakub Kicinski
@ 2018-07-09 20:22   ` Andrey Ignatov
  2018-07-10  2:49     ` Jakub Kicinski
  0 siblings, 1 reply; 24+ messages in thread
From: Andrey Ignatov @ 2018-07-09 20:22 UTC (permalink / raw)
  To: Jakub Kicinski; +Cc: alexei.starovoitov, daniel, oss-drivers, netdev

Jakub Kicinski <jakub.kicinski@netronome.com> [Mon, 2018-07-09 11:01 -0700]:
> More advanced applications may want to only replace programs without
> destroying associated maps.  Allow libbpf users to achieve that.
> Instead of always creating all of the maps at load time, expose to
> users an API to reconstruct the map object from already existing
> map.
> 
> The map parameters are read from the kernel and replace the parameters
> of the ELF map.  libbpf does not restrict the map replacement, i.e.
> the reused map does not have to be compatible with the ELF map
> definition.  We relay on the verifier for checking the compatibility
> between maps and programs.  The ELF map definition is completely
> overwritten by the information read from the kernel, to make sure
> libbpf's view of map object corresponds to the actual map.

Thanks for working on this Jakub! I encountered this shortcoming of
libbpf as well and was planning to fix it, but you beat me to it :)


> Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
> Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com>
> ---
>  tools/lib/bpf/libbpf.c | 35 +++++++++++++++++++++++++++++++++++
>  tools/lib/bpf/libbpf.h |  1 +
>  2 files changed, 36 insertions(+)
> 
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index b653dbb266c7..c80033fe66c3 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -215,6 +215,7 @@ struct bpf_map {
>  	int fd;
>  	char *name;
>  	size_t offset;
> +	bool fd_preset;

Any reason not to use map->fd itself to identify if fd is present?

fd of every map is set to -1 in bpf_object__init_maps() that, in turn, is
called from __bpf_object__open():

	for (i = 0; i < nr_maps; i++)
		obj->maps[i].fd = -1;

Later it will either contain valid fd that is >= 0, or that same -1, what
should be enough to identify fd presence.


>  	int map_ifindex;
>  	struct bpf_map_def def;
>  	uint32_t btf_key_type_id;
> @@ -1082,6 +1083,34 @@ static int bpf_map_find_btf_info(struct bpf_map *map, const struct btf *btf)
>  	return 0;
>  }
>  
> +int bpf_map__reuse_fd(struct bpf_map *map, int fd)
> +{
> +	struct bpf_map_info info = {};
> +	__u32 len = sizeof(info);
> +	int err;
> +
> +	err = bpf_obj_get_info_by_fd(fd, &info, &len);
> +	if (err)
> +		return err;
> +

Should there be a check that map->fd doesn't contain any valid fd (>= 0)
before rewriting it so that if it does (e.g. because the function is
called after bpf_object__load() by mistake), current map->fd won't be
leaked?


> +	map->fd = dup(fd);

Unfortunately, new descriptor created by dup(2) will not have O_CLOEXEC set, in
contrast to original fd returned by kernel on map creation.

libbpf has other interface shortcomings where it comes up. E.g. struct
bpf_object owns all descriptors it contains (progs, maps) and closes them in
bpf_object__close(). if one wants to open/load ELF, then close it but
keep, say, prog fd to attach it to cgroup some time later, then fd
should be duplicated as well to get a new one not owned by bpf_object.

Currently I use this workaround to avoid time when new fd doesn't have
O_CLOEXEC:

	int new_prog_fd = open("/dev/null", O_RDONLY | O_CLOEXEC);
	if (new_prog_fd < 0 ||
	    dup3(bpf_program__fd(prog), new_prog_fd, O_CLOEXEC) == -1) {
		/* .. handle error .. */
		close(new_prog_fd);
	}
	/* .. use new_prog_fd with O_CLOEXEC set */

Not sure how to simplify it. dup2() has same problem with regard to
O_CLOEXEC.

Use-case: standalone server application that uses libbpf and does
fork()/execve() a lot.


> +	if (map->fd < 0)
> +		return map->fd;
> +	map->fd_preset = true;
> +
> +	free(map->name);
> +	map->name = strdup(info.name);
> +	map->def.type = info.type;
> +	map->def.key_size = info.key_size;
> +	map->def.value_size = info.value_size;
> +	map->def.max_entries = info.max_entries;
> +	map->def.map_flags = info.map_flags;
> +	map->btf_key_type_id = info.btf_key_type_id;
> +	map->btf_value_type_id = info.btf_value_type_id;
> +
> +	return 0;
> +}
> +
>  static int
>  bpf_object__create_maps(struct bpf_object *obj)
>  {
> @@ -1094,6 +1123,12 @@ bpf_object__create_maps(struct bpf_object *obj)
>  		struct bpf_map_def *def = &map->def;
>  		int *pfd = &map->fd;
>  
> +		if (map->fd_preset) {
> +			pr_debug("skip map create (preset) %s: fd=%d\n",
> +				 map->name, map->fd);
> +			continue;
> +		}
> +
>  		create_attr.name = map->name;
>  		create_attr.map_ifindex = map->map_ifindex;
>  		create_attr.map_type = def->type;
> diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
> index 60593ac44700..8e709a74f47c 100644
> --- a/tools/lib/bpf/libbpf.h
> +++ b/tools/lib/bpf/libbpf.h
> @@ -261,6 +261,7 @@ typedef void (*bpf_map_clear_priv_t)(struct bpf_map *, void *);
>  int bpf_map__set_priv(struct bpf_map *map, void *priv,
>  		      bpf_map_clear_priv_t clear_priv);
>  void *bpf_map__priv(struct bpf_map *map);
> +int bpf_map__reuse_fd(struct bpf_map *map, int fd);
>  bool bpf_map__is_offload_neutral(struct bpf_map *map);
>  void bpf_map__set_ifindex(struct bpf_map *map, __u32 ifindex);
>  int bpf_map__pin(struct bpf_map *map, const char *path);
> -- 
> 2.17.1
> 

-- 
Andrey Ignatov

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH bpf-next v2 12/12] tools: bpftool: allow reuse of maps with bpftool prog load
  2018-07-09 19:48   ` Alexei Starovoitov
@ 2018-07-10  1:38     ` Jakub Kicinski
  0 siblings, 0 replies; 24+ messages in thread
From: Jakub Kicinski @ 2018-07-10  1:38 UTC (permalink / raw)
  To: Alexei Starovoitov; +Cc: daniel, oss-drivers, netdev

On Mon, 9 Jul 2018 12:48:20 -0700, Alexei Starovoitov wrote:
> On Mon, Jul 09, 2018 at 10:59:44AM -0700, Jakub Kicinski wrote:
> > 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.
> > 
> > Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
> > Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com>  
> 
> cmdline interface feels a bit awkward to use, but it's a nice improvement.
> Acked-by: Alexei Starovoitov <ast@kernel.org>

Thanks, what about the cmdline feels awkward?  The syntax or having to
manipulate map reuse at cmdline level?

> any plans to extend bpf_map_def similar to iproute2 ?
> so things like pinned file name and map reuse can be specified in .c file
> instead of cmdline?

TBH for my purposes (testing, showcasing) being able to modify things
from command line is more convenient than baking such info in ELF files.
No plans to extend bpf_map_def at this point :(

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH bpf-next v2 11/12] tools: libbpf: allow map reuse
  2018-07-09 20:22   ` Andrey Ignatov
@ 2018-07-10  2:49     ` Jakub Kicinski
  2018-07-10  4:23       ` Andrey Ignatov
  0 siblings, 1 reply; 24+ messages in thread
From: Jakub Kicinski @ 2018-07-10  2:49 UTC (permalink / raw)
  To: Andrey Ignatov; +Cc: alexei.starovoitov, daniel, oss-drivers, netdev

On Mon, 9 Jul 2018 13:22:54 -0700, Andrey Ignatov wrote:
> Jakub Kicinski <jakub.kicinski@netronome.com> [Mon, 2018-07-09 11:01 -0700]:
> > More advanced applications may want to only replace programs without
> > destroying associated maps.  Allow libbpf users to achieve that.
> > Instead of always creating all of the maps at load time, expose to
> > users an API to reconstruct the map object from already existing
> > map.
> > 
> > The map parameters are read from the kernel and replace the parameters
> > of the ELF map.  libbpf does not restrict the map replacement, i.e.
> > the reused map does not have to be compatible with the ELF map
> > definition.  We relay on the verifier for checking the compatibility
> > between maps and programs.  The ELF map definition is completely
> > overwritten by the information read from the kernel, to make sure
> > libbpf's view of map object corresponds to the actual map.  
> 
> Thanks for working on this Jakub! I encountered this shortcoming of
> libbpf as well and was planning to fix it, but you beat me to it :)

Ah!  I wish I didn't! :)

> > Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
> > Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com>
> > ---
> >  tools/lib/bpf/libbpf.c | 35 +++++++++++++++++++++++++++++++++++
> >  tools/lib/bpf/libbpf.h |  1 +
> >  2 files changed, 36 insertions(+)
> > 
> > diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> > index b653dbb266c7..c80033fe66c3 100644
> > --- a/tools/lib/bpf/libbpf.c
> > +++ b/tools/lib/bpf/libbpf.c
> > @@ -215,6 +215,7 @@ struct bpf_map {
> >  	int fd;
> >  	char *name;
> >  	size_t offset;
> > +	bool fd_preset;  
> 
> Any reason not to use map->fd itself to identify if fd is present?

Note: pre-set, not present.

> fd of every map is set to -1 in bpf_object__init_maps() that, in turn, is
> called from __bpf_object__open():
> 
> 	for (i = 0; i < nr_maps; i++)
> 		obj->maps[i].fd = -1;
> 
> Later it will either contain valid fd that is >= 0, or that same -1, what
> should be enough to identify fd presence.

I thought it to be cleaner to indicate the fd has been pre-set, in case
things get more complicated in the future and fd >= 0 becomes ambiguous.

But no strong preference, should I change?

> >  	int map_ifindex;
> >  	struct bpf_map_def def;
> >  	uint32_t btf_key_type_id;
> > @@ -1082,6 +1083,34 @@ static int bpf_map_find_btf_info(struct bpf_map *map, const struct btf *btf)
> >  	return 0;
> >  }
> >  
> > +int bpf_map__reuse_fd(struct bpf_map *map, int fd)
> > +{
> > +	struct bpf_map_info info = {};
> > +	__u32 len = sizeof(info);
> > +	int err;
> > +
> > +	err = bpf_obj_get_info_by_fd(fd, &info, &len);
> > +	if (err)
> > +		return err;
> > +  
> 
> Should there be a check that map->fd doesn't contain any valid fd (>= 0)
> before rewriting it so that if it does (e.g. because the function is
> called after bpf_object__load() by mistake), current map->fd won't be
> leaked?

Hm.  In my first implementation libbpf just took the passed fd and
didn't do a dup(), the lifetime of the fd remained with the caller.
Having a check will prevent changing the descriptor unless we add some
from of "un-reuse" as well.  Perhaps I should just add a close() in
case fd >= 0?  Or do you prefer a hard error?

> > +	map->fd = dup(fd);  
> 
> Unfortunately, new descriptor created by dup(2) will not have O_CLOEXEC set, in
> contrast to original fd returned by kernel on map creation.
> 
> libbpf has other interface shortcomings where it comes up. E.g. struct
> bpf_object owns all descriptors it contains (progs, maps) and closes them in
> bpf_object__close(). if one wants to open/load ELF, then close it but
> keep, say, prog fd to attach it to cgroup some time later, then fd
> should be duplicated as well to get a new one not owned by bpf_object.
> 
> Currently I use this workaround to avoid time when new fd doesn't have
> O_CLOEXEC:
> 
> 	int new_prog_fd = open("/dev/null", O_RDONLY | O_CLOEXEC);
> 	if (new_prog_fd < 0 ||
> 	    dup3(bpf_program__fd(prog), new_prog_fd, O_CLOEXEC) == -1) {
> 		/* .. handle error .. */
> 		close(new_prog_fd);
> 	}
> 	/* .. use new_prog_fd with O_CLOEXEC set */
> 
> Not sure how to simplify it. dup2() has same problem with regard to
> O_CLOEXEC.
> 
> Use-case: standalone server application that uses libbpf and does
> fork()/execve() a lot.

Good point!  I have no better ideas.  Although being slightly paranoid
I would perhaps use "/" instead of "/dev/null"?  Shouldn't matter?

> > +	if (map->fd < 0)
> > +		return map->fd;
> > +	map->fd_preset = true;
> > +
> > +	free(map->name);
> > +	map->name = strdup(info.name);
> > +	map->def.type = info.type;
> > +	map->def.key_size = info.key_size;
> > +	map->def.value_size = info.value_size;
> > +	map->def.max_entries = info.max_entries;
> > +	map->def.map_flags = info.map_flags;
> > +	map->btf_key_type_id = info.btf_key_type_id;
> > +	map->btf_value_type_id = info.btf_value_type_id;
> > +
> > +	return 0;
> > +}
> > +
> >  static int
> >  bpf_object__create_maps(struct bpf_object *obj)
> >  {
> > @@ -1094,6 +1123,12 @@ bpf_object__create_maps(struct bpf_object *obj)
> >  		struct bpf_map_def *def = &map->def;
> >  		int *pfd = &map->fd;
> >  
> > +		if (map->fd_preset) {
> > +			pr_debug("skip map create (preset) %s: fd=%d\n",
> > +				 map->name, map->fd);
> > +			continue;
> > +		}
> > +
> >  		create_attr.name = map->name;
> >  		create_attr.map_ifindex = map->map_ifindex;
> >  		create_attr.map_type = def->type;
> > diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
> > index 60593ac44700..8e709a74f47c 100644
> > --- a/tools/lib/bpf/libbpf.h
> > +++ b/tools/lib/bpf/libbpf.h
> > @@ -261,6 +261,7 @@ typedef void (*bpf_map_clear_priv_t)(struct bpf_map *, void *);
> >  int bpf_map__set_priv(struct bpf_map *map, void *priv,
> >  		      bpf_map_clear_priv_t clear_priv);
> >  void *bpf_map__priv(struct bpf_map *map);
> > +int bpf_map__reuse_fd(struct bpf_map *map, int fd);
> >  bool bpf_map__is_offload_neutral(struct bpf_map *map);
> >  void bpf_map__set_ifindex(struct bpf_map *map, __u32 ifindex);
> >  int bpf_map__pin(struct bpf_map *map, const char *path);
> > -- 
> > 2.17.1
> >   
> 

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH bpf-next v2 11/12] tools: libbpf: allow map reuse
  2018-07-10  2:49     ` Jakub Kicinski
@ 2018-07-10  4:23       ` Andrey Ignatov
  2018-07-10 18:09         ` Jakub Kicinski
  0 siblings, 1 reply; 24+ messages in thread
From: Andrey Ignatov @ 2018-07-10  4:23 UTC (permalink / raw)
  To: Jakub Kicinski; +Cc: alexei.starovoitov, daniel, oss-drivers, netdev

Jakub Kicinski <jakub.kicinski@netronome.com> [Mon, 2018-07-09 19:49 -0700]:
> On Mon, 9 Jul 2018 13:22:54 -0700, Andrey Ignatov wrote:
> > Jakub Kicinski <jakub.kicinski@netronome.com> [Mon, 2018-07-09 11:01 -0700]:
> > > More advanced applications may want to only replace programs without
> > > destroying associated maps.  Allow libbpf users to achieve that.
> > > Instead of always creating all of the maps at load time, expose to
> > > users an API to reconstruct the map object from already existing
> > > map.
> > > 
> > > The map parameters are read from the kernel and replace the parameters
> > > of the ELF map.  libbpf does not restrict the map replacement, i.e.
> > > the reused map does not have to be compatible with the ELF map
> > > definition.  We relay on the verifier for checking the compatibility
> > > between maps and programs.  The ELF map definition is completely
> > > overwritten by the information read from the kernel, to make sure
> > > libbpf's view of map object corresponds to the actual map.  
> > 
> > Thanks for working on this Jakub! I encountered this shortcoming of
> > libbpf as well and was planning to fix it, but you beat me to it :)
> 
> Ah!  I wish I didn't! :)
> 
> > > Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
> > > Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com>
> > > ---
> > >  tools/lib/bpf/libbpf.c | 35 +++++++++++++++++++++++++++++++++++
> > >  tools/lib/bpf/libbpf.h |  1 +
> > >  2 files changed, 36 insertions(+)
> > > 
> > > diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> > > index b653dbb266c7..c80033fe66c3 100644
> > > --- a/tools/lib/bpf/libbpf.c
> > > +++ b/tools/lib/bpf/libbpf.c
> > > @@ -215,6 +215,7 @@ struct bpf_map {
> > >  	int fd;
> > >  	char *name;
> > >  	size_t offset;
> > > +	bool fd_preset;  
> > 
> > Any reason not to use map->fd itself to identify if fd is present?
> 
> Note: pre-set, not present.

Oh, sorry, I'm blind :)


> > fd of every map is set to -1 in bpf_object__init_maps() that, in turn, is
> > called from __bpf_object__open():
> > 
> > 	for (i = 0; i < nr_maps; i++)
> > 		obj->maps[i].fd = -1;
> > 
> > Later it will either contain valid fd that is >= 0, or that same -1, what
> > should be enough to identify fd presence.
> 
> I thought it to be cleaner to indicate the fd has been pre-set, in case
> things get more complicated in the future and fd >= 0 becomes ambiguous.
> 
> But no strong preference, should I change?

My preference (not strong either) is to avoid a new field whenever it's
possible. Though if you have a use-case that can't be covered by
(fd >= 0) keeping the field is fine as well.


> > >  	int map_ifindex;
> > >  	struct bpf_map_def def;
> > >  	uint32_t btf_key_type_id;
> > > @@ -1082,6 +1083,34 @@ static int bpf_map_find_btf_info(struct bpf_map *map, const struct btf *btf)
> > >  	return 0;
> > >  }
> > >  
> > > +int bpf_map__reuse_fd(struct bpf_map *map, int fd)
> > > +{
> > > +	struct bpf_map_info info = {};
> > > +	__u32 len = sizeof(info);
> > > +	int err;
> > > +
> > > +	err = bpf_obj_get_info_by_fd(fd, &info, &len);
> > > +	if (err)
> > > +		return err;
> > > +  
> > 
> > Should there be a check that map->fd doesn't contain any valid fd (>= 0)
> > before rewriting it so that if it does (e.g. because the function is
> > called after bpf_object__load() by mistake), current map->fd won't be
> > leaked?
> 
> Hm.  In my first implementation libbpf just took the passed fd and
> didn't do a dup(), the lifetime of the fd remained with the caller.
> Having a check will prevent changing the descriptor unless we add some
> from of "un-reuse" as well.  Perhaps I should just add a close() in
> case fd >= 0?  Or do you prefer a hard error?

Agree, close() in case fd >= 0 should be fine since caller already made it
explicit that they don't care about current fd and there should not be a
reason to hard-fail.


> > > +	map->fd = dup(fd);  
> > 
> > Unfortunately, new descriptor created by dup(2) will not have O_CLOEXEC set, in
> > contrast to original fd returned by kernel on map creation.
> > 
> > libbpf has other interface shortcomings where it comes up. E.g. struct
> > bpf_object owns all descriptors it contains (progs, maps) and closes them in
> > bpf_object__close(). if one wants to open/load ELF, then close it but
> > keep, say, prog fd to attach it to cgroup some time later, then fd
> > should be duplicated as well to get a new one not owned by bpf_object.
> > 
> > Currently I use this workaround to avoid time when new fd doesn't have
> > O_CLOEXEC:
> > 
> > 	int new_prog_fd = open("/dev/null", O_RDONLY | O_CLOEXEC);
> > 	if (new_prog_fd < 0 ||
> > 	    dup3(bpf_program__fd(prog), new_prog_fd, O_CLOEXEC) == -1) {
> > 		/* .. handle error .. */
> > 		close(new_prog_fd);
> > 	}
> > 	/* .. use new_prog_fd with O_CLOEXEC set */
> > 
> > Not sure how to simplify it. dup2() has same problem with regard to
> > O_CLOEXEC.
> > 
> > Use-case: standalone server application that uses libbpf and does
> > fork()/execve() a lot.
> 
> Good point!  I have no better ideas.  Although being slightly paranoid
> I would perhaps use "/" instead of "/dev/null"?  Shouldn't matter?

No strong preferences, important thing is to create fd with O_CLOEXEC
set somehow.

Is it safer to use "/" than "/dev/null"? (trying to understand if I
should change my code as well)


> > > +	if (map->fd < 0)
> > > +		return map->fd;
> > > +	map->fd_preset = true;
> > > +
> > > +	free(map->name);
> > > +	map->name = strdup(info.name);
> > > +	map->def.type = info.type;
> > > +	map->def.key_size = info.key_size;
> > > +	map->def.value_size = info.value_size;
> > > +	map->def.max_entries = info.max_entries;
> > > +	map->def.map_flags = info.map_flags;
> > > +	map->btf_key_type_id = info.btf_key_type_id;
> > > +	map->btf_value_type_id = info.btf_value_type_id;
> > > +
> > > +	return 0;
> > > +}
> > > +
> > >  static int
> > >  bpf_object__create_maps(struct bpf_object *obj)
> > >  {
> > > @@ -1094,6 +1123,12 @@ bpf_object__create_maps(struct bpf_object *obj)
> > >  		struct bpf_map_def *def = &map->def;
> > >  		int *pfd = &map->fd;
> > >  
> > > +		if (map->fd_preset) {
> > > +			pr_debug("skip map create (preset) %s: fd=%d\n",
> > > +				 map->name, map->fd);
> > > +			continue;
> > > +		}
> > > +
> > >  		create_attr.name = map->name;
> > >  		create_attr.map_ifindex = map->map_ifindex;
> > >  		create_attr.map_type = def->type;
> > > diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
> > > index 60593ac44700..8e709a74f47c 100644
> > > --- a/tools/lib/bpf/libbpf.h
> > > +++ b/tools/lib/bpf/libbpf.h
> > > @@ -261,6 +261,7 @@ typedef void (*bpf_map_clear_priv_t)(struct bpf_map *, void *);
> > >  int bpf_map__set_priv(struct bpf_map *map, void *priv,
> > >  		      bpf_map_clear_priv_t clear_priv);
> > >  void *bpf_map__priv(struct bpf_map *map);
> > > +int bpf_map__reuse_fd(struct bpf_map *map, int fd);
> > >  bool bpf_map__is_offload_neutral(struct bpf_map *map);
> > >  void bpf_map__set_ifindex(struct bpf_map *map, __u32 ifindex);
> > >  int bpf_map__pin(struct bpf_map *map, const char *path);
> > > -- 
> > > 2.17.1
> > >   
> > 
> 

-- 
Andrey Ignatov

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH bpf-next v2 05/12] tools: libbpf: expose the prog type guessing from section name logic
  2018-07-09 17:59 ` [PATCH bpf-next v2 05/12] tools: libbpf: expose the prog type guessing from section name logic Jakub Kicinski
@ 2018-07-10  5:10   ` Andrey Ignatov
  0 siblings, 0 replies; 24+ messages in thread
From: Andrey Ignatov @ 2018-07-10  5:10 UTC (permalink / raw)
  To: Jakub Kicinski; +Cc: alexei.starovoitov, daniel, oss-drivers, netdev

Jakub Kicinski <jakub.kicinski@netronome.com> [Mon, 2018-07-09 11:01 -0700]:
> libbpf can guess program type based on ELF section names.  As libbpf
> becomes more popular its association between section name strings and
> types becomes more of a standard.  Allow libbpf users to use the same
> logic for matching strings to types, e.g. when the string originates
> from command line.
> 
> Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
> Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com>
> ---
>  tools/lib/bpf/libbpf.c | 43 ++++++++++++++++++++++++------------------
>  tools/lib/bpf/libbpf.h |  3 +++
>  2 files changed, 28 insertions(+), 18 deletions(-)
> 
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index 38ed3e92e393..30f3e58bd563 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -2081,25 +2081,33 @@ static const struct {
>  #undef BPF_S_PROG_SEC
>  #undef BPF_SA_PROG_SEC
>  
> -static int bpf_program__identify_section(struct bpf_program *prog)
> +int libbpf_prog_type_by_string(const char *name, enum bpf_prog_type *prog_type,
> +			       enum bpf_attach_type *expected_attach_type)
>  {
>  	int i;
>  
> -	if (!prog->section_name)
> -		goto err;
> -
> -	for (i = 0; i < ARRAY_SIZE(section_names); i++)
> -		if (strncmp(prog->section_name, section_names[i].sec,
> -			    section_names[i].len) == 0)
> -			return i;
> -
> -err:
> -	pr_warning("failed to guess program type based on section name %s\n",
> -		   prog->section_name);
> +	if (!name)
> +		return -1;

Should it return -EINVAL? It can help in bpf_prog_load_xattr below:

			err = bpf_program__identify_section(prog, &prog_type,
							    &expected_attach_type);
			if (err < 0) {
				...
				return err;
			}


>  
> +	for (i = 0; i < ARRAY_SIZE(section_names); i++) {
> +		if (strncmp(name, section_names[i].sec, section_names[i].len))
> +			continue;
> +		*prog_type = section_names[i].prog_type;
> +		*expected_attach_type = section_names[i].expected_attach_type;
> +		return 0;
> +	}
>  	return -1;

Same here.

>  }
>  
> +static int
> +bpf_program__identify_section(struct bpf_program *prog,
> +			      enum bpf_prog_type *prog_type,
> +			      enum bpf_attach_type *expected_attach_type)
> +{
> +	return libbpf_prog_type_by_string(prog->section_name, prog_type,
> +					  expected_attach_type);
> +}
> +
>  int bpf_map__fd(struct bpf_map *map)
>  {
>  	return map ? map->fd : -EINVAL;
> @@ -2230,7 +2238,6 @@ int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr,
>  	enum bpf_prog_type prog_type;
>  	struct bpf_object *obj;
>  	struct bpf_map *map;
> -	int section_idx;
>  	int err;
>  
>  	if (!attr)
> @@ -2252,14 +2259,14 @@ int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr,
>  		prog->prog_ifindex = attr->ifindex;
>  		expected_attach_type = attr->expected_attach_type;
>  		if (prog_type == BPF_PROG_TYPE_UNSPEC) {
> -			section_idx = bpf_program__identify_section(prog);
> -			if (section_idx < 0) {
> +			err = bpf_program__identify_section(prog, &prog_type,
> +							    &expected_attach_type);
> +			if (err < 0) {
> +				pr_warning("failed to guess program type based on section name %s\n",
> +					   prog->section_name);
>  				bpf_object__close(obj);
>  				return -EINVAL;
>  			}
> -			prog_type = section_names[section_idx].prog_type;
> -			expected_attach_type =
> -				section_names[section_idx].expected_attach_type;
>  		}
>  
>  		bpf_program__set_type(prog, prog_type);
> diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
> index 564f4be9bae0..617dacfc6704 100644
> --- a/tools/lib/bpf/libbpf.h
> +++ b/tools/lib/bpf/libbpf.h
> @@ -92,6 +92,9 @@ int bpf_object__set_priv(struct bpf_object *obj, void *priv,
>  			 bpf_object_clear_priv_t clear_priv);
>  void *bpf_object__priv(struct bpf_object *prog);
>  
> +int libbpf_prog_type_by_string(const char *name, enum bpf_prog_type *prog_type,

Nit:

I think it should be either:
  int libbpf_prog_type_by_title(const char *title, enum bpf_prog_type *prog_type,

(to be consistent with bpf_program__title())),

or:
  int libbpf_prog_type_by_name(const char *name, enum bpf_prog_type *prog_type,

(to have function name consistent with argument name and with
bpf_program->name).

IMO "name" is better since it's used across API many times and will be
more consistent, when "title" is used just once (IMO
bpf_program__title() should have been called bpf_program__name() to be
consistent with bpf_map__name() and others, not sure if it's fine to
change now).

> +			       enum bpf_attach_type *expected_attach_type);
> +
>  /* Accessors of bpf_program */
>  struct bpf_program;
>  struct bpf_program *bpf_program__next(struct bpf_program *prog,
> -- 
> 2.17.1
> 

-- 
Andrey Ignatov

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH bpf-next v2 08/12] tools: libbpf: add extended attributes version of bpf_object__open()
  2018-07-09 17:59 ` [PATCH bpf-next v2 08/12] tools: libbpf: add extended attributes version of bpf_object__open() Jakub Kicinski
@ 2018-07-10  5:39   ` Andrey Ignatov
  0 siblings, 0 replies; 24+ messages in thread
From: Andrey Ignatov @ 2018-07-10  5:39 UTC (permalink / raw)
  To: Jakub Kicinski; +Cc: alexei.starovoitov, daniel, oss-drivers, netdev

Jakub Kicinski <jakub.kicinski@netronome.com> [Mon, 2018-07-09 11:01 -0700]:
> Similarly to bpf_prog_load() users of bpf_object__open() may need
> to specify the expected program type.  Program type is needed at
> open to avoid the kernel version check for program types which don't
> require it.
> 
> Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
> Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com>
> ---
>  tools/lib/bpf/libbpf.c | 21 +++++++++++++++++----
>  tools/lib/bpf/libbpf.h |  6 ++++++
>  2 files changed, 23 insertions(+), 4 deletions(-)
> 
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index edc3b0b3737d..5b0e84fbcf71 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -1520,7 +1520,8 @@ __bpf_object__open(const char *path, void *obj_buf, size_t obj_buf_sz,
>  	return ERR_PTR(err);
>  }
>  
> -struct bpf_object *bpf_object__open(const char *path)
> +struct bpf_object *bpf_object__open_xattr(const char *path,
> +					  struct bpf_object_open_attr *attr)
>  {
>  	/* param validation */
>  	if (!path)
> @@ -1528,7 +1529,17 @@ struct bpf_object *bpf_object__open(const char *path)
>  
>  	pr_debug("loading %s\n", path);
>  
> -	return __bpf_object__open(path, NULL, 0, true);
> +	return __bpf_object__open(path, NULL, 0,
> +				  bpf_prog_type__needs_kver(attr->prog_type));
> +}
> +
> +struct bpf_object *bpf_object__open(const char *path)
> +{
> +	struct bpf_object_open_attr attr = {
> +		.prog_type	= BPF_PROG_TYPE_UNSPEC,
> +	};
> +
> +	return bpf_object__open_xattr(path, &attr);
>  }
>  
>  struct bpf_object *bpf_object__open_buffer(void *obj_buf,
> @@ -2238,6 +2249,9 @@ int bpf_prog_load(const char *file, enum bpf_prog_type type,
>  int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr,
>  			struct bpf_object **pobj, int *prog_fd)
>  {
> +	struct bpf_object_open_attr open_attr = {
> +		.prog_type	= attr->prog_type,
> +	};
>  	struct bpf_program *prog, *first_prog = NULL;
>  	enum bpf_attach_type expected_attach_type;
>  	enum bpf_prog_type prog_type;
> @@ -2250,8 +2264,7 @@ int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr,
>  	if (!attr->file)
>  		return -EINVAL;
>  
> -	obj = __bpf_object__open(attr->file, NULL, 0,
> -				 bpf_prog_type__needs_kver(attr->prog_type));
> +	obj = bpf_object__open_xattr(attr->file, &open_attr);
>  	if (IS_ERR_OR_NULL(obj))
>  		return -ENOENT;
>  
> diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
> index 3122d74f2643..60593ac44700 100644
> --- a/tools/lib/bpf/libbpf.h
> +++ b/tools/lib/bpf/libbpf.h
> @@ -66,7 +66,13 @@ void libbpf_set_print(libbpf_print_fn_t warn,
>  /* Hide internal to user */
>  struct bpf_object;
>  
> +struct bpf_object_open_attr {
> +	enum bpf_prog_type prog_type;
> +};
> +
>  struct bpf_object *bpf_object__open(const char *path);
> +struct bpf_object *bpf_object__open_xattr(const char *path,
> +					  struct bpf_object_open_attr *attr);

Should the new bpf_object__open_xattr() API have _only_ attr argument?
Path, in turn, can become a member of attr.

That way it can be reused e.g. to load object from buffer (like
bpf_object__open_buffer() below), where path is not needed.

Otherwise, if bpf_object__open_buffer() has to be extended in the
future, another _xattr function will be needed (or caller would need to
pass NULL to path, what would make API less convenient).


>  struct bpf_object *bpf_object__open_buffer(void *obj_buf,
>  					   size_t obj_buf_sz,
>  					   const char *name);
> -- 
> 2.17.1
> 

-- 
Andrey Ignatov

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH bpf-next v2 11/12] tools: libbpf: allow map reuse
  2018-07-10  4:23       ` Andrey Ignatov
@ 2018-07-10 18:09         ` Jakub Kicinski
  2018-07-10 18:16           ` Daniel Borkmann
  0 siblings, 1 reply; 24+ messages in thread
From: Jakub Kicinski @ 2018-07-10 18:09 UTC (permalink / raw)
  To: Andrey Ignatov; +Cc: alexei.starovoitov, daniel, oss-drivers, netdev

On Mon, 9 Jul 2018 21:23:20 -0700, Andrey Ignatov wrote:
> Jakub Kicinski <jakub.kicinski@netronome.com> [Mon, 2018-07-09 19:49 -0700]:
> > On Mon, 9 Jul 2018 13:22:54 -0700, Andrey Ignatov wrote:  
> > > Jakub Kicinski <jakub.kicinski@netronome.com> [Mon, 2018-07-09 11:01 -0700]:  
> > > fd of every map is set to -1 in bpf_object__init_maps() that, in turn, is
> > > called from __bpf_object__open():
> > > 
> > > 	for (i = 0; i < nr_maps; i++)
> > > 		obj->maps[i].fd = -1;
> > > 
> > > Later it will either contain valid fd that is >= 0, or that same -1, what
> > > should be enough to identify fd presence.  
> > 
> > I thought it to be cleaner to indicate the fd has been pre-set, in case
> > things get more complicated in the future and fd >= 0 becomes ambiguous.
> > 
> > But no strong preference, should I change?  
> 
> My preference (not strong either) is to avoid a new field whenever it's
> possible. Though if you have a use-case that can't be covered by
> (fd >= 0) keeping the field is fine as well.

Okay, I can change.  I think it may be worthwhile keeping the
information that the map definition was replaced by something that did
not come from the ELF file, but I don't actually have a use for it
right now, so we can just add it back later :)

> > > > +	map->fd = dup(fd);    
> > > 
> > > Unfortunately, new descriptor created by dup(2) will not have O_CLOEXEC set, in
> > > contrast to original fd returned by kernel on map creation.
> > > 
> > > libbpf has other interface shortcomings where it comes up. E.g. struct
> > > bpf_object owns all descriptors it contains (progs, maps) and closes them in
> > > bpf_object__close(). if one wants to open/load ELF, then close it but
> > > keep, say, prog fd to attach it to cgroup some time later, then fd
> > > should be duplicated as well to get a new one not owned by bpf_object.
> > > 
> > > Currently I use this workaround to avoid time when new fd doesn't have
> > > O_CLOEXEC:
> > > 
> > > 	int new_prog_fd = open("/dev/null", O_RDONLY | O_CLOEXEC);
> > > 	if (new_prog_fd < 0 ||
> > > 	    dup3(bpf_program__fd(prog), new_prog_fd, O_CLOEXEC) == -1) {
> > > 		/* .. handle error .. */
> > > 		close(new_prog_fd);
> > > 	}
> > > 	/* .. use new_prog_fd with O_CLOEXEC set */
> > > 
> > > Not sure how to simplify it. dup2() has same problem with regard to
> > > O_CLOEXEC.
> > > 
> > > Use-case: standalone server application that uses libbpf and does
> > > fork()/execve() a lot.  
> > 
> > Good point!  I have no better ideas.  Although being slightly paranoid
> > I would perhaps use "/" instead of "/dev/null"?  Shouldn't matter?  
> 
> No strong preferences, important thing is to create fd with O_CLOEXEC
> set somehow.
> 
> Is it safer to use "/" than "/dev/null"? (trying to understand if I
> should change my code as well)

IDK :)  Could there be a crazy scenario when someone runs chroot or a
very broken system without /dev/null?  / should always be there?

Thanks for all the other reviews, I will update the code accordingly!

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH bpf-next v2 11/12] tools: libbpf: allow map reuse
  2018-07-10 18:09         ` Jakub Kicinski
@ 2018-07-10 18:16           ` Daniel Borkmann
  2018-07-10 20:58             ` Jakub Kicinski
  0 siblings, 1 reply; 24+ messages in thread
From: Daniel Borkmann @ 2018-07-10 18:16 UTC (permalink / raw)
  To: Jakub Kicinski, Andrey Ignatov; +Cc: alexei.starovoitov, oss-drivers, netdev

On 07/10/2018 08:09 PM, Jakub Kicinski wrote:
> On Mon, 9 Jul 2018 21:23:20 -0700, Andrey Ignatov wrote:
>> Jakub Kicinski <jakub.kicinski@netronome.com> [Mon, 2018-07-09 19:49 -0700]:
>>> On Mon, 9 Jul 2018 13:22:54 -0700, Andrey Ignatov wrote:  
>>>> Jakub Kicinski <jakub.kicinski@netronome.com> [Mon, 2018-07-09 11:01 -0700]:  
>>>> fd of every map is set to -1 in bpf_object__init_maps() that, in turn, is
>>>> called from __bpf_object__open():
>>>>
>>>> 	for (i = 0; i < nr_maps; i++)
>>>> 		obj->maps[i].fd = -1;
>>>>
>>>> Later it will either contain valid fd that is >= 0, or that same -1, what
>>>> should be enough to identify fd presence.  
>>>
>>> I thought it to be cleaner to indicate the fd has been pre-set, in case
>>> things get more complicated in the future and fd >= 0 becomes ambiguous.
>>>
>>> But no strong preference, should I change?  
>>
>> My preference (not strong either) is to avoid a new field whenever it's
>> possible. Though if you have a use-case that can't be covered by
>> (fd >= 0) keeping the field is fine as well.
> 
> Okay, I can change.  I think it may be worthwhile keeping the
> information that the map definition was replaced by something that did
> not come from the ELF file, but I don't actually have a use for it
> right now, so we can just add it back later :)
> 
>>>>> +	map->fd = dup(fd);    
>>>>
>>>> Unfortunately, new descriptor created by dup(2) will not have O_CLOEXEC set, in
>>>> contrast to original fd returned by kernel on map creation.
>>>>
>>>> libbpf has other interface shortcomings where it comes up. E.g. struct
>>>> bpf_object owns all descriptors it contains (progs, maps) and closes them in
>>>> bpf_object__close(). if one wants to open/load ELF, then close it but
>>>> keep, say, prog fd to attach it to cgroup some time later, then fd
>>>> should be duplicated as well to get a new one not owned by bpf_object.
>>>>
>>>> Currently I use this workaround to avoid time when new fd doesn't have
>>>> O_CLOEXEC:
>>>>
>>>> 	int new_prog_fd = open("/dev/null", O_RDONLY | O_CLOEXEC);
>>>> 	if (new_prog_fd < 0 ||
>>>> 	    dup3(bpf_program__fd(prog), new_prog_fd, O_CLOEXEC) == -1) {
>>>> 		/* .. handle error .. */
>>>> 		close(new_prog_fd);
>>>> 	}
>>>> 	/* .. use new_prog_fd with O_CLOEXEC set */
>>>>
>>>> Not sure how to simplify it. dup2() has same problem with regard to
>>>> O_CLOEXEC.
>>>>
>>>> Use-case: standalone server application that uses libbpf and does
>>>> fork()/execve() a lot.  
>>>
>>> Good point!  I have no better ideas.  Although being slightly paranoid
>>> I would perhaps use "/" instead of "/dev/null"?  Shouldn't matter?  
>>
>> No strong preferences, important thing is to create fd with O_CLOEXEC
>> set somehow.
>>
>> Is it safer to use "/" than "/dev/null"? (trying to understand if I
>> should change my code as well)
> 
> IDK :)  Could there be a crazy scenario when someone runs chroot or a
> very broken system without /dev/null?  / should always be there?
> 
> Thanks for all the other reviews, I will update the code accordingly!

Ok, I've tossed the v2 series from patchwork and waiting for your respin
in that case.

Thanks,
Daniel

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH bpf-next v2 11/12] tools: libbpf: allow map reuse
  2018-07-10 18:16           ` Daniel Borkmann
@ 2018-07-10 20:58             ` Jakub Kicinski
  0 siblings, 0 replies; 24+ messages in thread
From: Jakub Kicinski @ 2018-07-10 20:58 UTC (permalink / raw)
  To: Daniel Borkmann; +Cc: Andrey Ignatov, alexei.starovoitov, oss-drivers, netdev

On Tue, 10 Jul 2018 20:16:46 +0200, Daniel Borkmann wrote:
> On 07/10/2018 08:09 PM, Jakub Kicinski wrote:
> > On Mon, 9 Jul 2018 21:23:20 -0700, Andrey Ignatov wrote:  
> >> Jakub Kicinski <jakub.kicinski@netronome.com> [Mon, 2018-07-09 19:49 -0700]:  
> >>> On Mon, 9 Jul 2018 13:22:54 -0700, Andrey Ignatov wrote:    
> >>>> Jakub Kicinski <jakub.kicinski@netronome.com> [Mon, 2018-07-09 11:01 -0700]:  
> >>>>> +	map->fd = dup(fd);      
> >>>>
> >>>> Unfortunately, new descriptor created by dup(2) will not have O_CLOEXEC set, in
> >>>> contrast to original fd returned by kernel on map creation.
> >>>>
> >>>> libbpf has other interface shortcomings where it comes up. E.g. struct
> >>>> bpf_object owns all descriptors it contains (progs, maps) and closes them in
> >>>> bpf_object__close(). if one wants to open/load ELF, then close it but
> >>>> keep, say, prog fd to attach it to cgroup some time later, then fd
> >>>> should be duplicated as well to get a new one not owned by bpf_object.
> >>>>
> >>>> Currently I use this workaround to avoid time when new fd doesn't have
> >>>> O_CLOEXEC:
> >>>>
> >>>> 	int new_prog_fd = open("/dev/null", O_RDONLY | O_CLOEXEC);
> >>>> 	if (new_prog_fd < 0 ||
> >>>> 	    dup3(bpf_program__fd(prog), new_prog_fd, O_CLOEXEC) == -1) {
> >>>> 		/* .. handle error .. */
> >>>> 		close(new_prog_fd);
> >>>> 	}
> >>>> 	/* .. use new_prog_fd with O_CLOEXEC set */
> >>>>
> >>>> Not sure how to simplify it. dup2() has same problem with regard to
> >>>> O_CLOEXEC.
> >>>>
> >>>> Use-case: standalone server application that uses libbpf and does
> >>>> fork()/execve() a lot.    
> >>>
> >>> Good point!  I have no better ideas.  Although being slightly paranoid
> >>> I would perhaps use "/" instead of "/dev/null"?  Shouldn't matter?    
> >>
> >> No strong preferences, important thing is to create fd with O_CLOEXEC
> >> set somehow.
> >>
> >> Is it safer to use "/" than "/dev/null"? (trying to understand if I
> >> should change my code as well)  
> > 
> > IDK :)  Could there be a crazy scenario when someone runs chroot or a
> > very broken system without /dev/null?  / should always be there?
> > 
> > Thanks for all the other reviews, I will update the code accordingly!  

Oh no, dup3() is under _GNU_SOURCE as well.  libbpf kind of depends
on XSI-compliant strerror_r() semantics, I think I'll have to move the
strerror_r()-dependent code out of the libbpf.c file :(

> Ok, I've tossed the v2 series from patchwork and waiting for your respin
> in that case.

Yes, thank you!

^ permalink raw reply	[flat|nested] 24+ messages in thread

end of thread, other threads:[~2018-07-10 21:30 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-07-09 17:59 [PATCH bpf-next v2 00/12] tools: bpf: extend bpftool prog load Jakub Kicinski
2018-07-09 17:59 ` [PATCH bpf-next v2 01/12] selftests/bpf: remove duplicated word from test offloads Jakub Kicinski
2018-07-09 17:59 ` [PATCH bpf-next v2 02/12] selftests/bpf: add Error: prefix in check_extack helper Jakub Kicinski
2018-07-09 17:59 ` [PATCH bpf-next v2 03/12] tools: bpftool: refactor argument parsing for prog load Jakub Kicinski
2018-07-09 17:59 ` [PATCH bpf-next v2 04/12] tools: bpftool: add support for loading programs for offload Jakub Kicinski
2018-07-09 17:59 ` [PATCH bpf-next v2 05/12] tools: libbpf: expose the prog type guessing from section name logic Jakub Kicinski
2018-07-10  5:10   ` Andrey Ignatov
2018-07-09 17:59 ` [PATCH bpf-next v2 06/12] tools: bpftool: allow users to specify program type for prog load Jakub Kicinski
2018-07-09 17:59 ` [PATCH bpf-next v2 07/12] tools: libbpf: recognize offload neutral maps Jakub Kicinski
2018-07-09 17:59 ` [PATCH bpf-next v2 08/12] tools: libbpf: add extended attributes version of bpf_object__open() Jakub Kicinski
2018-07-10  5:39   ` Andrey Ignatov
2018-07-09 17:59 ` [PATCH bpf-next v2 09/12] tools: bpftool: reimplement bpf_prog_load() for prog load Jakub Kicinski
2018-07-09 19:40   ` Alexei Starovoitov
2018-07-09 17:59 ` [PATCH bpf-next v2 10/12] tools: bpf: make use of reallocarray Jakub Kicinski
2018-07-09 17:59 ` [PATCH bpf-next v2 11/12] tools: libbpf: allow map reuse Jakub Kicinski
2018-07-09 20:22   ` Andrey Ignatov
2018-07-10  2:49     ` Jakub Kicinski
2018-07-10  4:23       ` Andrey Ignatov
2018-07-10 18:09         ` Jakub Kicinski
2018-07-10 18:16           ` Daniel Borkmann
2018-07-10 20:58             ` Jakub Kicinski
2018-07-09 17:59 ` [PATCH bpf-next v2 12/12] tools: bpftool: allow reuse of maps with bpftool prog load Jakub Kicinski
2018-07-09 19:48   ` Alexei Starovoitov
2018-07-10  1:38     ` Jakub Kicinski

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).