qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Jason Wang <jasowang@redhat.com>
To: Andrew Melnychenko <andrew@daynix.com>, mst@redhat.com
Cc: yan@daynix.com, yuri.benditovich@daynix.com, qemu-devel@nongnu.org
Subject: Re: [RFC PATCH v2 3/5] ebpf: Added eBPF RSS loader.
Date: Tue, 24 Nov 2020 16:33:00 +0800	[thread overview]
Message-ID: <cb43f9bf-13e4-9ff2-2cf0-b1bd2848edf3@redhat.com> (raw)
In-Reply-To: <20201119111305.485202-4-andrew@daynix.com>


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>'}



  reply	other threads:[~2020-11-24  8:34 UTC|newest]

Thread overview: 29+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-11-19 11:13 [RFC PATCH v2 0/5] eBPF RSS support for virtio-net Andrew Melnychenko
2020-11-19 11:13 ` [RFC PATCH v2 1/5] net: Added SetSteeringEBPF method for NetClientState Andrew Melnychenko
2020-11-23  6:10   ` Jason Wang
2020-11-19 11:13 ` [RFC PATCH v2 2/5] ebpf: Added eBPF RSS program Andrew Melnychenko
2020-11-24  8:14   ` Jason Wang
2020-11-19 11:13 ` [RFC PATCH v2 3/5] ebpf: Added eBPF RSS loader Andrew Melnychenko
2020-11-24  8:33   ` Jason Wang [this message]
2020-11-19 11:13 ` [RFC PATCH v2 4/5] virtio-net: Added eBPF RSS to virtio-net Andrew Melnychenko
2020-11-24  8:48   ` Jason Wang
2020-12-01  7:40     ` Yuri Benditovich
2020-12-02  4:05       ` Jason Wang
2020-12-02  7:16         ` Yuri Benditovich
2020-12-02  8:06           ` Jason Wang
2020-11-19 11:13 ` [RFC PATCH v2 5/5] docs: Added eBPF documentation Andrew Melnychenko
2020-11-24  8:54   ` Jason Wang
2020-11-26 13:00     ` Yuri Benditovich
2020-11-27  4:36       ` Jason Wang
2020-11-23  6:08 ` [RFC PATCH v2 0/5] eBPF RSS support for virtio-net Jason Wang
2020-11-26 12:52   ` Yuri Benditovich
2020-11-27  4:35     ` Jason Wang
2020-11-27  6:06       ` Yuri Benditovich
2020-11-30  2:54         ` Jason Wang
2020-12-02 13:55 ` Jason Wang
2020-12-02 14:18   ` Toke Høiland-Jørgensen
2020-12-04  7:42     ` Yuri Benditovich
2020-12-04 10:09       ` Toke Høiland-Jørgensen
2020-12-04 12:31         ` Yuri Benditovich
2020-12-04 13:57           ` Toke Høiland-Jørgensen
2020-12-06 18:44             ` Yuri Benditovich

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=cb43f9bf-13e4-9ff2-2cf0-b1bd2848edf3@redhat.com \
    --to=jasowang@redhat.com \
    --cc=andrew@daynix.com \
    --cc=mst@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=yan@daynix.com \
    --cc=yuri.benditovich@daynix.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).