diff mbox series

[RFC,v2,3/5] ebpf: Added eBPF RSS loader.

Message ID 20201119111305.485202-4-andrew@daynix.com
State New
Headers show
Series eBPF RSS support for virtio-net | expand

Commit Message

Andrew Melnichenko Nov. 19, 2020, 11:13 a.m. UTC
From: Andrew <andrew@daynix.com>

Added function that loads RSS eBPF program.
Added stub functions for RSS eBPF loader.
Added meson and configuration options.

By default, eBPF feature enabled if libbpf is present in the build system.
libbpf checked in configuration shell script and meson script.

Signed-off-by: Yuri Benditovich <yuri.benditovich@daynix.com>
Signed-off-by: Andrew Melnychenko <andrew@daynix.com>
---
 configure            |  33 ++++++++
 ebpf/ebpf_rss-stub.c |  40 +++++++++
 ebpf/ebpf_rss.c      | 187 +++++++++++++++++++++++++++++++++++++++++++
 ebpf/ebpf_rss.h      |  44 ++++++++++
 ebpf/meson.build     |   1 +
 ebpf/trace-events    |   4 +
 ebpf/trace.h         |   2 +
 meson.build          |  11 +++
 8 files changed, 322 insertions(+)
 create mode 100644 ebpf/ebpf_rss-stub.c
 create mode 100644 ebpf/ebpf_rss.c
 create mode 100644 ebpf/ebpf_rss.h
 create mode 100644 ebpf/meson.build
 create mode 100644 ebpf/trace-events
 create mode 100644 ebpf/trace.h

Comments

Jason Wang Nov. 24, 2020, 8:33 a.m. UTC | #1
On 2020/11/19 下午7:13, Andrew Melnychenko wrote:
> From: Andrew <andrew@daynix.com>
>
> Added function that loads RSS eBPF program.
> Added stub functions for RSS eBPF loader.
> Added meson and configuration options.
>
> By default, eBPF feature enabled if libbpf is present in the build system.
> libbpf checked in configuration shell script and meson script.
>
> Signed-off-by: Yuri Benditovich <yuri.benditovich@daynix.com>
> Signed-off-by: Andrew Melnychenko <andrew@daynix.com>
> ---
>   configure            |  33 ++++++++
>   ebpf/ebpf_rss-stub.c |  40 +++++++++
>   ebpf/ebpf_rss.c      | 187 +++++++++++++++++++++++++++++++++++++++++++
>   ebpf/ebpf_rss.h      |  44 ++++++++++
>   ebpf/meson.build     |   1 +
>   ebpf/trace-events    |   4 +
>   ebpf/trace.h         |   2 +
>   meson.build          |  11 +++
>   8 files changed, 322 insertions(+)
>   create mode 100644 ebpf/ebpf_rss-stub.c
>   create mode 100644 ebpf/ebpf_rss.c
>   create mode 100644 ebpf/ebpf_rss.h
>   create mode 100644 ebpf/meson.build
>   create mode 100644 ebpf/trace-events
>   create mode 100644 ebpf/trace.h
>
> diff --git a/configure b/configure
> index 6df4306c88..97455950f6 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,30 @@ else
>       membarrier=no
>   fi
>   
> +##########################################
> +# check for usable bpf system call
> +if test "$bpf" = ""; then
> +    have_bpf=no
> +    if test "$linux" = "yes" -a "$bigendian" != "yes"; then
> +        cat > $TMPC << EOF
> +    #include <stdlib.h>
> +    #include <bpf/libbpf.h>
> +    int main(void) {
> +        struct bpf_object *obj = NULL;
> +        bpf_object__load(obj);


Do we need a test of btf here?


> +        exit(0);
> +    }
> +EOF
> +        if compile_prog "" "-lbpf" ; then
> +            have_bpf=yes
> +            bpf=yes
> +        fi
> +    fi
> +    if test "$have_bpf" = "no"; then
> +      feature_not_found "bpf" "the libbpf is not available"
> +    fi
> +fi
> +
>   ##########################################
>   # check if rtnetlink.h exists and is useful
>   have_rtnetlink=no
> @@ -6279,6 +6309,9 @@ fi
>   if test "$membarrier" = "yes" ; then
>     echo "CONFIG_MEMBARRIER=y" >> $config_host_mak
>   fi
> +if test "$bpf" = "yes" -a "$bigendian" != "yes" -a "$linux" = "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_rss-stub.c b/ebpf/ebpf_rss-stub.c
> new file mode 100644
> index 0000000000..e71e229190
> --- /dev/null
> +++ b/ebpf/ebpf_rss-stub.c
> @@ -0,0 +1,40 @@
> +/*
> + * eBPF RSS stub file
> + *
> + * Developed by Daynix Computing LTD (http://www.daynix.com)
> + *
> + * Authors:
> + *  Yuri Benditovich <yuri.benditovich@daynix.com>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2.  See
> + * the COPYING file in the top-level directory.
> + */
> +
> +#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..d07fac6904
> --- /dev/null
> +++ b/ebpf/ebpf_rss.c
> @@ -0,0 +1,187 @@
> +/*
> + * eBPF RSS loader
> + *
> + * Developed by Daynix Computing LTD (http://www.daynix.com)
> + *
> + * Authors:
> + *  Andrew Melnychenko <andrew@daynix.com>
> + *  Yuri Benditovich <yuri.benditovich@daynix.com>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2.  See
> + * the COPYING file in the top-level directory.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/error-report.h"
> +
> +#include <bpf/libbpf.h>
> +#include <bpf/bpf.h>
> +
> +#include "hw/virtio/virtio-net.h" /* VIRTIO_NET_RSS_MAX_TABLE_LEN */
> +
> +#include "ebpf/ebpf_rss.h"
> +#include "ebpf/tun_rss_steering.h"
> +#include "trace.h"
> +
> +void ebpf_rss_init(struct EBPFRSSContext *ctx)
> +{
> +    if (ctx != NULL) {
> +        ctx->obj = NULL;
> +    }
> +}
> +
> +bool ebpf_rss_is_loaded(struct EBPFRSSContext *ctx)
> +{
> +    return ctx != NULL && ctx->obj != NULL;
> +}
> +
> +bool ebpf_rss_load(struct EBPFRSSContext *ctx)
> +{
> +    struct bpf_object *object = NULL;
> +    struct bpf_program *prog  = NULL;


Unnecessary initialization.


> +
> +    if (ctx == NULL) {
> +        return false;
> +    }
> +
> +    object = bpf_object__open_mem(data_tun_rss_steering,
> +                                  sizeof(data_tun_rss_steering), NULL);
> +    if (object == NULL) {
> +        trace_ebpf_error("eBPF RSS", "can not open eBPF object");
> +        return false;
> +    }
> +
> +    prog = bpf_object__find_program_by_title(object, "tun_rss_steering");
> +    if (prog == NULL) {
> +        trace_ebpf_error("eBPF RSS", "can not find RSS program");
> +        goto l_issue;
> +    }
> +
> +    bpf_program__set_socket_filter(prog);
> +
> +    if (bpf_object__load(object)) {
> +        trace_ebpf_error("eBPF RSS", "can not load RSS program");


It would be useful for getting the verbose log via extack but I'm not 
sure if libbpf support this. If yes, it's better to add the output to 
the trace.


> +        goto l_issue;
> +    }
> +
> +    ctx->obj = object;
> +    ctx->program_fd = bpf_program__fd(prog);
> +
> +    ctx->map_configuration =
> +            bpf_object__find_map_fd_by_name(object,
> +                                            "tap_rss_map_configurations");
> +    if (ctx->map_configuration < 0) {
> +        trace_ebpf_error("eBPF RSS", "can not find MAP for configurations");
> +        goto l_issue;
> +    }
> +
> +    ctx->map_toeplitz_key =
> +            bpf_object__find_map_fd_by_name(object,
> +                                            "tap_rss_map_toeplitz_key");
> +    if (ctx->map_toeplitz_key < 0) {
> +        trace_ebpf_error("eBPF RSS", "can not find MAP for toeplitz key");
> +        goto l_issue;
> +    }
> +
> +    ctx->map_indirections_table =
> +            bpf_object__find_map_fd_by_name(object,
> +                                            "tap_rss_map_indirection_table");
> +    if (ctx->map_indirections_table < 0) {
> +        trace_ebpf_error("eBPF RSS", "can not find MAP for indirections table");
> +        goto l_issue;
> +    }
> +
> +    return true;
> +
> +l_issue:
> +    bpf_object__close(object);
> +    ctx->obj = NULL;


Do we need to clean other member of ctx?


> +    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_map_update_elem(ctx->map_configuration,
> +                            &map_key, config, 0) < 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_map_update_elem(ctx->map_indirections_table, &i,
> +                                indirections_table + i, 0) < 0) {
> +            return false;
> +        }
> +    }


Do we need to delete the elem in the range of [i, len)?


> +    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] = {};


Compilers may warn about such declaration after statement.


> +    memcpy(toe, toeplitz_key, VIRTIO_NET_RSS_MAX_KEY_SIZE);
> +    *(uint32_t *)toe = ntohl(*(uint32_t *)toe);
> +
> +    if (bpf_map_update_elem(ctx->map_toeplitz_key, &map_key, toe,
> +                            0) < 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;
> +    }
> +
> +    bpf_object__close(ctx->obj);
> +    ctx->obj = NULL;
> +}
> diff --git a/ebpf/ebpf_rss.h b/ebpf/ebpf_rss.h
> new file mode 100644
> index 0000000000..0fcb4e010f
> --- /dev/null
> +++ b/ebpf/ebpf_rss.h
> @@ -0,0 +1,44 @@
> +/*
> + * eBPF RSS header
> + *
> + * Developed by Daynix Computing LTD (http://www.daynix.com)
> + *
> + * Authors:
> + *  Andrew Melnychenko <andrew@daynix.com>
> + *  Yuri Benditovich <yuri.benditovich@daynix.com>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2.  See
> + * the COPYING file in the top-level directory.
> + */
> +
> +#ifndef QEMU_EBPF_RSS_H
> +#define QEMU_EBPF_RSS_H
> +
> +struct EBPFRSSContext {
> +    void *obj;
> +    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..f5bd5a0f01
> --- /dev/null
> +++ b/ebpf/meson.build
> @@ -0,0 +1 @@
> +common_ss.add(when: 'CONFIG_EBPF', if_true: files('ebpf_rss.c'), if_false: files('ebpf_rss-stub.c'))
> diff --git a/ebpf/trace-events b/ebpf/trace-events
> new file mode 100644
> index 0000000000..411b1e2be3
> --- /dev/null
> +++ b/ebpf/trace-events
> @@ -0,0 +1,4 @@
> +# See docs/devel/tracing.txt for syntax documentation.
> +
> +# ebpf-rss.c
> +ebpf_error(const char *s1, const char *s2) "error in %s: %s"
> diff --git a/ebpf/trace.h b/ebpf/trace.h
> new file mode 100644
> index 0000000000..ad570e6691
> --- /dev/null
> +++ b/ebpf/trace.h
> @@ -0,0 +1,2 @@
> +#include "trace/trace-ebpf.h"
> +
> diff --git a/meson.build b/meson.build
> index 47e32e1fcb..374ec88c2e 100644
> --- a/meson.build
> +++ b/meson.build
> @@ -736,6 +736,12 @@ if not has_malloc_trim and get_option('malloc_trim').enabled()
>     endif
>   endif
>   
> +# libbpf
> +libbpf = not_found
> +if 'CONFIG_EBPF' in config_host
> +  libbpf = cc.find_library('bpf', required: true)
> +endif
> +
>   #################
>   # config-host.h #
>   #################
> @@ -1368,6 +1374,7 @@ if have_system
>       'backends',
>       'backends/tpm',
>       'chardev',
> +    'ebpf',
>       'hw/9pfs',
>       'hw/acpi',
>       'hw/alpha',
> @@ -1530,6 +1537,9 @@ subdir('accel')
>   subdir('plugins')
>   subdir('bsd-user')
>   subdir('linux-user')
> +subdir('ebpf')
> +
> +common_ss.add(libbpf)
>   
>   bsd_user_ss.add(files('gdbstub.c'))
>   specific_ss.add_all(when: 'CONFIG_BSD_USER', if_true: bsd_user_ss)
> @@ -2093,6 +2103,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>'}
diff mbox series

Patch

diff --git a/configure b/configure
index 6df4306c88..97455950f6 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,30 @@  else
     membarrier=no
 fi
 
+##########################################
+# check for usable bpf system call
+if test "$bpf" = ""; then
+    have_bpf=no
+    if test "$linux" = "yes" -a "$bigendian" != "yes"; then
+        cat > $TMPC << EOF
+    #include <stdlib.h>
+    #include <bpf/libbpf.h>
+    int main(void) {
+        struct bpf_object *obj = NULL;
+        bpf_object__load(obj);
+        exit(0);
+    }
+EOF
+        if compile_prog "" "-lbpf" ; then
+            have_bpf=yes
+            bpf=yes
+        fi
+    fi
+    if test "$have_bpf" = "no"; then
+      feature_not_found "bpf" "the libbpf is not available"
+    fi
+fi
+
 ##########################################
 # check if rtnetlink.h exists and is useful
 have_rtnetlink=no
@@ -6279,6 +6309,9 @@  fi
 if test "$membarrier" = "yes" ; then
   echo "CONFIG_MEMBARRIER=y" >> $config_host_mak
 fi
+if test "$bpf" = "yes" -a "$bigendian" != "yes" -a "$linux" = "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_rss-stub.c b/ebpf/ebpf_rss-stub.c
new file mode 100644
index 0000000000..e71e229190
--- /dev/null
+++ b/ebpf/ebpf_rss-stub.c
@@ -0,0 +1,40 @@ 
+/*
+ * eBPF RSS stub file
+ *
+ * Developed by Daynix Computing LTD (http://www.daynix.com)
+ *
+ * Authors:
+ *  Yuri Benditovich <yuri.benditovich@daynix.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ */
+
+#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..d07fac6904
--- /dev/null
+++ b/ebpf/ebpf_rss.c
@@ -0,0 +1,187 @@ 
+/*
+ * eBPF RSS loader
+ *
+ * Developed by Daynix Computing LTD (http://www.daynix.com)
+ *
+ * Authors:
+ *  Andrew Melnychenko <andrew@daynix.com>
+ *  Yuri Benditovich <yuri.benditovich@daynix.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/error-report.h"
+
+#include <bpf/libbpf.h>
+#include <bpf/bpf.h>
+
+#include "hw/virtio/virtio-net.h" /* VIRTIO_NET_RSS_MAX_TABLE_LEN */
+
+#include "ebpf/ebpf_rss.h"
+#include "ebpf/tun_rss_steering.h"
+#include "trace.h"
+
+void ebpf_rss_init(struct EBPFRSSContext *ctx)
+{
+    if (ctx != NULL) {
+        ctx->obj = NULL;
+    }
+}
+
+bool ebpf_rss_is_loaded(struct EBPFRSSContext *ctx)
+{
+    return ctx != NULL && ctx->obj != NULL;
+}
+
+bool ebpf_rss_load(struct EBPFRSSContext *ctx)
+{
+    struct bpf_object *object = NULL;
+    struct bpf_program *prog  = NULL;
+
+    if (ctx == NULL) {
+        return false;
+    }
+
+    object = bpf_object__open_mem(data_tun_rss_steering,
+                                  sizeof(data_tun_rss_steering), NULL);
+    if (object == NULL) {
+        trace_ebpf_error("eBPF RSS", "can not open eBPF object");
+        return false;
+    }
+
+    prog = bpf_object__find_program_by_title(object, "tun_rss_steering");
+    if (prog == NULL) {
+        trace_ebpf_error("eBPF RSS", "can not find RSS program");
+        goto l_issue;
+    }
+
+    bpf_program__set_socket_filter(prog);
+
+    if (bpf_object__load(object)) {
+        trace_ebpf_error("eBPF RSS", "can not load RSS program");
+        goto l_issue;
+    }
+
+    ctx->obj = object;
+    ctx->program_fd = bpf_program__fd(prog);
+
+    ctx->map_configuration =
+            bpf_object__find_map_fd_by_name(object,
+                                            "tap_rss_map_configurations");
+    if (ctx->map_configuration < 0) {
+        trace_ebpf_error("eBPF RSS", "can not find MAP for configurations");
+        goto l_issue;
+    }
+
+    ctx->map_toeplitz_key =
+            bpf_object__find_map_fd_by_name(object,
+                                            "tap_rss_map_toeplitz_key");
+    if (ctx->map_toeplitz_key < 0) {
+        trace_ebpf_error("eBPF RSS", "can not find MAP for toeplitz key");
+        goto l_issue;
+    }
+
+    ctx->map_indirections_table =
+            bpf_object__find_map_fd_by_name(object,
+                                            "tap_rss_map_indirection_table");
+    if (ctx->map_indirections_table < 0) {
+        trace_ebpf_error("eBPF RSS", "can not find MAP for indirections table");
+        goto l_issue;
+    }
+
+    return true;
+
+l_issue:
+    bpf_object__close(object);
+    ctx->obj = NULL;
+    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_map_update_elem(ctx->map_configuration,
+                            &map_key, config, 0) < 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_map_update_elem(ctx->map_indirections_table, &i,
+                                indirections_table + i, 0) < 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_map_update_elem(ctx->map_toeplitz_key, &map_key, toe,
+                            0) < 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;
+    }
+
+    bpf_object__close(ctx->obj);
+    ctx->obj = NULL;
+}
diff --git a/ebpf/ebpf_rss.h b/ebpf/ebpf_rss.h
new file mode 100644
index 0000000000..0fcb4e010f
--- /dev/null
+++ b/ebpf/ebpf_rss.h
@@ -0,0 +1,44 @@ 
+/*
+ * eBPF RSS header
+ *
+ * Developed by Daynix Computing LTD (http://www.daynix.com)
+ *
+ * Authors:
+ *  Andrew Melnychenko <andrew@daynix.com>
+ *  Yuri Benditovich <yuri.benditovich@daynix.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ */
+
+#ifndef QEMU_EBPF_RSS_H
+#define QEMU_EBPF_RSS_H
+
+struct EBPFRSSContext {
+    void *obj;
+    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..f5bd5a0f01
--- /dev/null
+++ b/ebpf/meson.build
@@ -0,0 +1 @@ 
+common_ss.add(when: 'CONFIG_EBPF', if_true: files('ebpf_rss.c'), if_false: files('ebpf_rss-stub.c'))
diff --git a/ebpf/trace-events b/ebpf/trace-events
new file mode 100644
index 0000000000..411b1e2be3
--- /dev/null
+++ b/ebpf/trace-events
@@ -0,0 +1,4 @@ 
+# See docs/devel/tracing.txt for syntax documentation.
+
+# ebpf-rss.c
+ebpf_error(const char *s1, const char *s2) "error in %s: %s"
diff --git a/ebpf/trace.h b/ebpf/trace.h
new file mode 100644
index 0000000000..ad570e6691
--- /dev/null
+++ b/ebpf/trace.h
@@ -0,0 +1,2 @@ 
+#include "trace/trace-ebpf.h"
+
diff --git a/meson.build b/meson.build
index 47e32e1fcb..374ec88c2e 100644
--- a/meson.build
+++ b/meson.build
@@ -736,6 +736,12 @@  if not has_malloc_trim and get_option('malloc_trim').enabled()
   endif
 endif
 
+# libbpf
+libbpf = not_found
+if 'CONFIG_EBPF' in config_host
+  libbpf = cc.find_library('bpf', required: true)
+endif
+
 #################
 # config-host.h #
 #################
@@ -1368,6 +1374,7 @@  if have_system
     'backends',
     'backends/tpm',
     'chardev',
+    'ebpf',
     'hw/9pfs',
     'hw/acpi',
     'hw/alpha',
@@ -1530,6 +1537,9 @@  subdir('accel')
 subdir('plugins')
 subdir('bsd-user')
 subdir('linux-user')
+subdir('ebpf')
+
+common_ss.add(libbpf)
 
 bsd_user_ss.add(files('gdbstub.c'))
 specific_ss.add_all(when: 'CONFIG_BSD_USER', if_true: bsd_user_ss)
@@ -2093,6 +2103,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>'}