From: Andrew <and...@daynix.com> Added function that loads RSS eBPF program. Added stub functions for RSS eBPF. Added meson and configuration options.
Signed-off-by: Sameeh Jubran <sam...@daynix.com> Signed-off-by: Yuri Benditovich <yuri.benditov...@daynix.com> Signed-off-by: Andrew Melnychenko <and...@daynix.com> --- configure | 36 ++++++++++ ebpf/ebpf-stub.c | 28 ++++++++ ebpf/ebpf_rss.c | 178 +++++++++++++++++++++++++++++++++++++++++++++++ ebpf/ebpf_rss.h | 30 ++++++++ ebpf/meson.build | 1 + meson.build | 3 + 6 files changed, 276 insertions(+) create mode 100644 ebpf/ebpf-stub.c create mode 100644 ebpf/ebpf_rss.c create mode 100644 ebpf/ebpf_rss.h create mode 100644 ebpf/meson.build diff --git a/configure b/configure index 6df4306c88..bae4ea54f8 100755 --- a/configure +++ b/configure @@ -330,6 +330,7 @@ vhost_scsi="" vhost_vsock="" vhost_user="" vhost_user_fs="" +bpf="" kvm="auto" hax="auto" hvf="auto" @@ -1210,6 +1211,10 @@ for opt do ;; --enable-membarrier) membarrier="yes" ;; + --disable-bpf) bpf="no" + ;; + --enable-bpf) bpf="yes" + ;; --disable-blobs) blobs="false" ;; --with-pkgversion=*) pkgversion="$optarg" @@ -1792,6 +1797,7 @@ disabled with --disable-FEATURE, default is enabled if available: vhost-kernel vhost kernel backend support vhost-user vhost-user backend support vhost-vdpa vhost-vdpa kernel backend support + bpf BPF kernel support spice spice rbd rados block device (rbd) libiscsi iscsi support @@ -5347,6 +5353,33 @@ else membarrier=no fi +########################################## +# check for usable bpf system call +if test "$bpf" = ""; then + have_bpf=no + if test "$linux" = "yes" ; then + cat > $TMPC << EOF + #include <sys/syscall.h> + #include <linux/bpf.h> + #include <unistd.h> + #include <stdlib.h> + #include <string.h> + int main(void) { + union bpf_attr * attr = NULL; + syscall(__NR_bpf, BPF_PROG_LOAD, attr, sizeof(attr)); + exit(0); + } +EOF + if compile_prog "" "" ; then + have_bpf=yes + bpf=yes + fi + fi + if test "$have_bpf" = "no"; then + feature_not_found "bpf" "the bpf system call is not available" + fi +fi + ########################################## # check if rtnetlink.h exists and is useful have_rtnetlink=no @@ -6279,6 +6312,9 @@ fi if test "$membarrier" = "yes" ; then echo "CONFIG_MEMBARRIER=y" >> $config_host_mak fi +if test "$bpf" = "yes" -a "$bigendian" != "yes"; then + echo "CONFIG_EBPF=y" >> $config_host_mak +fi if test "$signalfd" = "yes" ; then echo "CONFIG_SIGNALFD=y" >> $config_host_mak fi diff --git a/ebpf/ebpf-stub.c b/ebpf/ebpf-stub.c new file mode 100644 index 0000000000..281dc039d3 --- /dev/null +++ b/ebpf/ebpf-stub.c @@ -0,0 +1,28 @@ +#include "qemu/osdep.h" +#include "ebpf/ebpf_rss.h" + +void ebpf_rss_init(struct EBPFRSSContext *ctx) +{ + +} + +bool ebpf_rss_is_loaded(struct EBPFRSSContext *ctx) +{ + return false; +} + +bool ebpf_rss_load(struct EBPFRSSContext *ctx) +{ + return false; +} + +bool ebpf_rss_set_all(struct EBPFRSSContext *ctx, struct EBPFRSSConfig *config, + uint16_t *indirections_table, uint8_t *toeplitz_key) +{ + return false; +} + +void ebpf_rss_unload(struct EBPFRSSContext *ctx) +{ + +} diff --git a/ebpf/ebpf_rss.c b/ebpf/ebpf_rss.c new file mode 100644 index 0000000000..f3c948a7a0 --- /dev/null +++ b/ebpf/ebpf_rss.c @@ -0,0 +1,178 @@ +#include "qemu/osdep.h" +#include "qemu/error-report.h" + +#include "hw/virtio/virtio-net.h" /* VIRTIO_NET_RSS_MAX_TABLE_LEN */ + +#include "ebpf/ebpf_rss.h" +#include "ebpf/ebpf.h" +#include "ebpf/tun_rss_steering.h" +#include "trace.h" + +void ebpf_rss_init(struct EBPFRSSContext *ctx) +{ + if (ctx != NULL) { + ctx->program_fd = -1; + } +} + +bool ebpf_rss_is_loaded(struct EBPFRSSContext *ctx) +{ + return ctx != NULL && ctx->program_fd >= 0; +} + +bool ebpf_rss_load(struct EBPFRSSContext *ctx) +{ + if (ctx == NULL) { + return false; + } + + ctx->map_configuration = + bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(uint32_t), + sizeof(struct EBPFRSSConfig), 1); + if (ctx->map_configuration < 0) { + trace_ebpf_error("eBPF RSS", "can not create MAP for configurations"); + goto l_conf_create; + } + ctx->map_toeplitz_key = + bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(uint32_t), + VIRTIO_NET_RSS_MAX_KEY_SIZE, 1); + if (ctx->map_toeplitz_key < 0) { + trace_ebpf_error("eBPF RSS", "can not create MAP for toeplitz key"); + goto l_toe_create; + } + + ctx->map_indirections_table = + bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(uint32_t), + sizeof(uint16_t), VIRTIO_NET_RSS_MAX_TABLE_LEN); + if (ctx->map_indirections_table < 0) { + trace_ebpf_error("eBPF RSS", "can not create MAP for indirections table"); + goto l_table_create; + } + + bpf_fixup_mapfd(reltun_rss_steering, + sizeof(reltun_rss_steering) / sizeof(struct fixup_mapfd_t), + instun_rss_steering, + sizeof(instun_rss_steering) / sizeof(struct bpf_insn), + "tap_rss_map_configurations", ctx->map_configuration); + + bpf_fixup_mapfd(reltun_rss_steering, + sizeof(reltun_rss_steering) / sizeof(struct fixup_mapfd_t), + instun_rss_steering, + sizeof(instun_rss_steering) / sizeof(struct bpf_insn), + "tap_rss_map_toeplitz_key", ctx->map_toeplitz_key); + + bpf_fixup_mapfd(reltun_rss_steering, + sizeof(reltun_rss_steering) / sizeof(struct fixup_mapfd_t), + instun_rss_steering, + sizeof(instun_rss_steering) / sizeof(struct bpf_insn), + "tap_rss_map_indirection_table", ctx->map_indirections_table); + + ctx->program_fd = + bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, instun_rss_steering, + sizeof(instun_rss_steering) / sizeof(struct bpf_insn), + "GPL"); + if (ctx->program_fd < 0) { + trace_ebpf_error("eBPF RSS", "can not load eBPF program"); + goto l_prog_load; + } + + return true; +l_prog_load: + close(ctx->map_indirections_table); +l_table_create: + close(ctx->map_toeplitz_key); +l_toe_create: + close(ctx->map_configuration); +l_conf_create: + return false; +} + +static bool ebpf_rss_set_config(struct EBPFRSSContext *ctx, + struct EBPFRSSConfig *config) +{ + if (!ebpf_rss_is_loaded(ctx)) { + return false; + } + uint32_t map_key = 0; + if (bpf_update_elem(ctx->map_configuration, + &map_key, config, BPF_ANY) < 0) { + return false; + } + return true; +} + +static bool ebpf_rss_set_indirections_table(struct EBPFRSSContext *ctx, + uint16_t *indirections_table, + size_t len) +{ + if (!ebpf_rss_is_loaded(ctx) || indirections_table == NULL || + len > VIRTIO_NET_RSS_MAX_TABLE_LEN) { + return false; + } + uint32_t i = 0; + + for (; i < len; ++i) { + if (bpf_update_elem(ctx->map_indirections_table, &i, + indirections_table + i, BPF_ANY) < 0) { + return false; + } + } + return true; +} + +static bool ebpf_rss_set_toepliz_key(struct EBPFRSSContext *ctx, + uint8_t *toeplitz_key) +{ + if (!ebpf_rss_is_loaded(ctx) || toeplitz_key == NULL) { + return false; + } + uint32_t map_key = 0; + + /* prepare toeplitz key */ + uint8_t toe[VIRTIO_NET_RSS_MAX_KEY_SIZE] = {}; + memcpy(toe, toeplitz_key, VIRTIO_NET_RSS_MAX_KEY_SIZE); + *(uint32_t *)toe = ntohl(*(uint32_t *)toe); + + if (bpf_update_elem(ctx->map_toeplitz_key, &map_key, toe, + BPF_ANY) < 0) { + return false; + } + return true; +} + +bool ebpf_rss_set_all(struct EBPFRSSContext *ctx, struct EBPFRSSConfig *config, + uint16_t *indirections_table, uint8_t *toeplitz_key) +{ + if (!ebpf_rss_is_loaded(ctx) || config == NULL || + indirections_table == NULL || toeplitz_key == NULL) { + return false; + } + + if (!ebpf_rss_set_config(ctx, config)) { + return false; + } + + if (!ebpf_rss_set_indirections_table(ctx, indirections_table, + config->indirections_len)) { + return false; + } + + if (!ebpf_rss_set_toepliz_key(ctx, toeplitz_key)) { + return false; + } + + return true; +} + +void ebpf_rss_unload(struct EBPFRSSContext *ctx) +{ + if (!ebpf_rss_is_loaded(ctx)) { + return; + } + + close(ctx->program_fd); + close(ctx->map_configuration); + close(ctx->map_toeplitz_key); + close(ctx->map_indirections_table); + ctx->program_fd = -1; +} diff --git a/ebpf/ebpf_rss.h b/ebpf/ebpf_rss.h new file mode 100644 index 0000000000..ffed7b571a --- /dev/null +++ b/ebpf/ebpf_rss.h @@ -0,0 +1,30 @@ +#ifndef QEMU_EBPF_RSS_H +#define QEMU_EBPF_RSS_H + +struct EBPFRSSContext { + int program_fd; + int map_configuration; + int map_toeplitz_key; + int map_indirections_table; +}; + +struct EBPFRSSConfig { + uint8_t redirect; + uint8_t populate_hash; + uint32_t hash_types; + uint16_t indirections_len; + uint16_t default_queue; +}; + +void ebpf_rss_init(struct EBPFRSSContext *ctx); + +bool ebpf_rss_is_loaded(struct EBPFRSSContext *ctx); + +bool ebpf_rss_load(struct EBPFRSSContext *ctx); + +bool ebpf_rss_set_all(struct EBPFRSSContext *ctx, struct EBPFRSSConfig *config, + uint16_t *indirections_table, uint8_t *toeplitz_key); + +void ebpf_rss_unload(struct EBPFRSSContext *ctx); + +#endif /* QEMU_EBPF_RSS_H */ diff --git a/ebpf/meson.build b/ebpf/meson.build new file mode 100644 index 0000000000..10f4bc9ca8 --- /dev/null +++ b/ebpf/meson.build @@ -0,0 +1 @@ +specific_ss.add(when: 'CONFIG_EBPF', if_true: files('ebpf_rss.c', 'ebpf.c'), if_false: files('ebpf-stub.c')) diff --git a/meson.build b/meson.build index 47e32e1fcb..d0ea1a0e9d 100644 --- a/meson.build +++ b/meson.build @@ -1368,6 +1368,7 @@ if have_system 'backends', 'backends/tpm', 'chardev', + 'ebpf', 'hw/9pfs', 'hw/acpi', 'hw/alpha', @@ -1530,6 +1531,7 @@ subdir('accel') subdir('plugins') subdir('bsd-user') subdir('linux-user') +subdir('ebpf') bsd_user_ss.add(files('gdbstub.c')) specific_ss.add_all(when: 'CONFIG_BSD_USER', if_true: bsd_user_ss) @@ -2093,6 +2095,7 @@ summary_info += {'vhost-vsock support': config_host.has_key('CONFIG_VHOST_VSOCK' summary_info += {'vhost-user support': config_host.has_key('CONFIG_VHOST_KERNEL')} summary_info += {'vhost-user-fs support': config_host.has_key('CONFIG_VHOST_USER_FS')} summary_info += {'vhost-vdpa support': config_host.has_key('CONFIG_VHOST_VDPA')} +summary_info += {'bpf support': config_host.has_key('CONFIG_EBPF')} summary_info += {'Trace backends': config_host['TRACE_BACKENDS']} if config_host['TRACE_BACKENDS'].split().contains('simple') summary_info += {'Trace output file': config_host['CONFIG_TRACE_FILE'] + '-<pid>'} -- 2.28.0