diff --git a/headers/xdp/libxdp.h b/headers/xdp/libxdp.h index 801e826d..5283074a 100644 --- a/headers/xdp/libxdp.h +++ b/headers/xdp/libxdp.h @@ -44,6 +44,9 @@ typedef int (*libxdp_print_fn_t)(enum libxdp_print_level level, libxdp_print_fn_t libxdp_set_print(libxdp_print_fn_t fn); +/* Probe and cache kernel feature support during single-threaded startup. */ +void libxdp_cache_kernel_features(void); + struct xdp_program; struct xdp_multiprog; diff --git a/lib/libxdp/README.org b/lib/libxdp/README.org index 269650ef..a712dfdf 100644 --- a/lib/libxdp/README.org +++ b/lib/libxdp/README.org @@ -27,6 +27,13 @@ well as reading and writing packets from these sockets. Some of the functionality provided by libxdp depends on particular kernel features; see the "Kernel feature compatibility" section below for details. +Applications that want to control when libbpf reports unsupported XDP feature +probes can call =libxdp_cache_kernel_features()= during single-threaded startup. +This probes and caches XDP frags and device-bound support, so later dispatcher +attachments reuse the results instead of repeating the probes. Call it with the +same privileges that will be used for later attachments, and before starting +threads that use libxdp. + ** Using libxdp from an application Basic usage of libxdp from an application is quite straight forward. The diff --git a/lib/libxdp/libxdp.3 b/lib/libxdp/libxdp.3 index 5dd7c3fc..d36a97a6 100644 --- a/lib/libxdp/libxdp.3 +++ b/lib/libxdp/libxdp.3 @@ -19,6 +19,13 @@ well as reading and writing packets from these sockets. .PP Some of the functionality provided by libxdp depends on particular kernel features; see the "Kernel feature compatibility" section below for details. +.PP +Applications that want to control when libbpf reports unsupported XDP feature +probes can call \fIlibxdp_cache_kernel_features()\fP during single-threaded startup. +This probes and caches XDP frags and device-bound support, so later dispatcher +attachments reuse the results instead of repeating the probes. Call it with the +same privileges that will be used for later attachments, and before starting +threads that use libxdp. .SS "Using libxdp from an application" .PP Basic usage of libxdp from an application is quite straight forward. The diff --git a/lib/libxdp/libxdp.c b/lib/libxdp/libxdp.c index fa57b003..c0e57eca 100644 --- a/lib/libxdp/libxdp.c +++ b/lib/libxdp/libxdp.c @@ -232,6 +232,14 @@ static enum { COMPAT_UNSUPPORTED } kernel_compat = COMPAT_UNKNOWN; +/* Set by libxdp_cache_kernel_features() during single-threaded startup. */ +static bool kernel_features_cached; +static bool cached_kernel_frags_support; +static bool cached_kernel_dev_bound; + +static bool kernel_has_frags_support(void); +static bool kernel_has_dev_bound(void); + static int xdp_multiprog__attach(struct xdp_multiprog *old_mp, struct xdp_multiprog *mp, enum xdp_attach_mode mode); @@ -1664,13 +1672,13 @@ struct xdp_program *xdp_program__clone(struct xdp_program *prog, unsigned int fl } #ifndef HAVE_LIBBPF_BPF_PROGRAM__FLAGS -static bool kernel_has_frags_support(void) +static bool probe_kernel_frags_support(void) { pr_debug("Can't support frags with old version of libbpf that doesn't support setting program flags.\n"); return false; } -static bool kernel_has_dev_bound(void) +static bool probe_kernel_dev_bound(void) { pr_debug("Can't bind to device with old version of libbpf that doesn't support setting program flags.\n"); return false; @@ -1723,12 +1731,12 @@ static bool kernel_has_bpf_flag(__u32 flag) return ret; } -static bool kernel_has_frags_support(void) +static bool probe_kernel_frags_support(void) { return kernel_has_bpf_flag(BPF_F_XDP_HAS_FRAGS); } -static bool kernel_has_dev_bound(void) +static bool probe_kernel_dev_bound(void) { return kernel_has_bpf_flag(BPF_F_XDP_DEV_BOUND_ONLY); } @@ -1763,6 +1771,32 @@ static int xdp_program__set_xdp_dev_bound(struct xdp_program *prog, } #endif // HAVE_LIBBPF_BPF_PROGRAM__FLAGS +static bool kernel_has_frags_support(void) +{ + return kernel_features_cached ? cached_kernel_frags_support : + probe_kernel_frags_support(); +} + +static bool kernel_has_dev_bound(void) +{ + return kernel_features_cached ? cached_kernel_dev_bound : + probe_kernel_dev_bound(); +} + +/* + * Probe kernel features ahead of time so applications can choose when the + * libbpf diagnostics from unsupported features are emitted. + */ +void libxdp_cache_kernel_features(void) +{ + if (kernel_features_cached) + return; + + cached_kernel_frags_support = probe_kernel_frags_support(); + cached_kernel_dev_bound = probe_kernel_dev_bound(); + kernel_features_cached = true; +} + static int xdp_program__attach_single(struct xdp_program *prog, int ifindex, enum xdp_attach_mode mode, unsigned int flags) diff --git a/lib/libxdp/libxdp.map b/lib/libxdp/libxdp.map index 26d3ac5d..61079d0a 100644 --- a/lib/libxdp/libxdp.map +++ b/lib/libxdp/libxdp.map @@ -88,3 +88,7 @@ LIBXDP_1.5.0 { LIBXDP_1.6.0 { xdp_multiprog__xdp_dev_bound; } LIBXDP_1.5.0; + +LIBXDP_1.7.0 { + libxdp_cache_kernel_features; +} LIBXDP_1.6.0; diff --git a/lib/libxdp/tests/Makefile b/lib/libxdp/tests/Makefile index 67af3e6c..1cc8f1d9 100644 --- a/lib/libxdp/tests/Makefile +++ b/lib/libxdp/tests/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) -USER_TARGETS := test_xsk_refcnt test_xdp_frags test_link_detach test_xsk_umem_flags test_xdp_devbound test_xsk_map_leak +USER_TARGETS := test_xsk_refcnt test_xdp_frags test_link_detach test_xsk_umem_flags test_xdp_devbound test_xsk_map_leak test_kernel_feature_cache STATIC_LINK_TARGETS := check_kern_compat test_dispatcher_versions BPF_TARGETS := xdp_dispatcher_v1 xdp_dispatcher_v2 xdp_pass USER_LIBS := -lpthread diff --git a/lib/libxdp/tests/test-libxdp.sh b/lib/libxdp/tests/test-libxdp.sh index af0cb63f..5ce5e2c8 100644 --- a/lib/libxdp/tests/test-libxdp.sh +++ b/lib/libxdp/tests/test-libxdp.sh @@ -1,6 +1,6 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) -ALL_TESTS="test_link_so test_link_a test_old_dispatcher test_xdp_devbound test_xdp_frags test_xsk_prog_refcnt_bpffs test_xsk_prog_refcnt_legacy test_xsk_non_privileged test_link_detach test_xsk_umem_flags test_xsk_map_leak" +ALL_TESTS="test_link_so test_link_a test_kernel_feature_cache test_old_dispatcher test_xdp_devbound test_xdp_frags test_xsk_prog_refcnt_bpffs test_xsk_prog_refcnt_legacy test_xsk_non_privileged test_link_detach test_xsk_umem_flags test_xsk_map_leak" TESTS_DIR=$(dirname "${BASH_SOURCE[0]}") @@ -21,6 +21,7 @@ test_link_so() int main(int argc, char **argv) { (void) argc; (void) argv; (void) xdp_program__open_file("filename", "section_name", NULL); + libxdp_cache_kernel_features(); return 0; } EOF @@ -40,6 +41,7 @@ test_link_a() int main(int argc, char **argv) { (void) argc; (void) argv; (void) xdp_program__open_file("filename", "section_name", NULL); + libxdp_cache_kernel_features(); return 0; } EOF @@ -49,6 +51,12 @@ EOF return $retval } +test_kernel_feature_cache() +{ + export LIBXDP_OBJECT_PATH="$TESTS_DIR/.." + check_run "$TESTS_DIR/test_kernel_feature_cache" +} + test_refcnt_once() { # We need multiple queues for this test diff --git a/lib/libxdp/tests/test_kernel_feature_cache.c b/lib/libxdp/tests/test_kernel_feature_cache.c new file mode 100644 index 00000000..b4ac4bc1 --- /dev/null +++ b/lib/libxdp/tests/test_kernel_feature_cache.c @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) +#include + +static int debug_messages; + +static int count_debug(enum libxdp_print_level level, const char *format, + va_list args) +{ + (void)format; + (void)args; + + if (level == LIBXDP_DEBUG) + debug_messages++; + return 0; +} + +int main(void) +{ + libxdp_print_fn_t old_print; + int first_probe_messages; + + old_print = libxdp_set_print(count_debug); + libxdp_cache_kernel_features(); + first_probe_messages = debug_messages; + if (!first_probe_messages) + return 1; + + libxdp_cache_kernel_features(); + if (debug_messages != first_probe_messages) + return 2; + + libxdp_set_print(old_print); + return 0; +}