1
The following changes since commit 7d0e02405fc02a181319b1ab8681d2f72246b7c6:
1
The following changes since commit 92f8c6fef13b31ba222c4d20ad8afd2b79c4c28e:
2
2
3
Merge remote-tracking branch 'remotes/vivier2/tags/trivial-patches-pull-request' into staging (2019-07-01 17:40:32 +0100)
3
Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20210525' into staging (2021-05-25 16:17:06 +0100)
4
4
5
are available in the git repository at:
5
are available in the git repository at:
6
6
7
https://github.com/jasowang/qemu.git tags/net-pull-request
7
https://github.com/jasowang/qemu.git tags/net-pull-request
8
8
9
for you to fetch changes up to 0e8818f023616677416840d6ddc880db8de3c967:
9
for you to fetch changes up to 90322e646e87c1440661cb3ddbc0cc94309d8a4f:
10
10
11
migration/colo.c: Add missed filter notify for Xen COLO. (2019-07-02 10:21:07 +0800)
11
MAINTAINERS: Added eBPF maintainers information. (2021-06-04 15:25:46 +0800)
12
12
13
----------------------------------------------------------------
13
----------------------------------------------------------------
14
14
15
----------------------------------------------------------------
15
----------------------------------------------------------------
16
Cédric Le Goater (1):
16
Andrew Melnychenko (7):
17
ftgmac100: do not link to netdev
17
net/tap: Added TUNSETSTEERINGEBPF code.
18
net: Added SetSteeringEBPF method for NetClientState.
19
ebpf: Added eBPF RSS program.
20
ebpf: Added eBPF RSS loader.
21
virtio-net: Added eBPF RSS to virtio-net.
22
docs: Added eBPF documentation.
23
MAINTAINERS: Added eBPF maintainers information.
18
24
19
Dr. David Alan Gilbert (5):
25
MAINTAINERS | 8 +
20
net/announce: Allow optional list of interfaces
26
configure | 8 +-
21
net/announce: Add HMP optional interface list
27
docs/devel/ebpf_rss.rst | 125 +++++++++
22
net/announce: Add optional ID
28
docs/devel/index.rst | 1 +
23
net/announce: Add HMP optional ID
29
ebpf/ebpf_rss-stub.c | 40 +++
24
net/announce: Expand test for stopping self announce
30
ebpf/ebpf_rss.c | 165 ++++++++++++
25
31
ebpf/ebpf_rss.h | 44 ++++
26
Markus Armbruster (2):
32
ebpf/meson.build | 1 +
27
MAINTAINERS: Add qemu-bridge-helper.c to "Network device backends"
33
ebpf/rss.bpf.skeleton.h | 431 +++++++++++++++++++++++++++++++
28
qemu-bridge-helper: Document known shortcomings
34
ebpf/trace-events | 4 +
29
35
ebpf/trace.h | 1 +
30
Stefano Garzarella (4):
36
hw/net/vhost_net.c | 3 +
31
net: fix assertion failure when ipv6-prefixlen is not a number
37
hw/net/virtio-net.c | 116 ++++++++-
32
net: avoid using variable length array in net_client_init()
38
include/hw/virtio/virtio-net.h | 4 +
33
net: use g_strsplit() for parsing host address and port
39
include/net/net.h | 2 +
34
net: remove unused get_str_sep() function
40
meson.build | 23 ++
35
41
meson_options.txt | 2 +
36
Zhang Chen (5):
42
net/tap-bsd.c | 5 +
37
COLO-compare: Add new parameter to communicate with remote colo-frame
43
net/tap-linux.c | 13 +
38
COLO-compare: Add remote notification chardev handler frame
44
net/tap-linux.h | 1 +
39
COLO-compare: Make the compare_chr_send() can send notification message.
45
net/tap-solaris.c | 5 +
40
COLO-compare: Add colo-compare remote notify support
46
net/tap-stub.c | 5 +
41
migration/colo.c: Add missed filter notify for Xen COLO.
47
net/tap.c | 9 +
42
48
net/tap_int.h | 1 +
43
MAINTAINERS | 1 +
49
net/vhost-vdpa.c | 2 +
44
hmp-commands.hx | 7 ++-
50
tools/ebpf/Makefile.ebpf | 21 ++
45
hw/net/ftgmac100.c | 2 -
51
tools/ebpf/rss.bpf.c | 571 +++++++++++++++++++++++++++++++++++++++++
46
hw/net/virtio-net.c | 4 +-
52
27 files changed, 1607 insertions(+), 4 deletions(-)
47
include/net/announce.h | 8 ++-
53
create mode 100644 docs/devel/ebpf_rss.rst
48
migration/colo.c | 2 +
54
create mode 100644 ebpf/ebpf_rss-stub.c
49
monitor/hmp-cmds.c | 41 ++++++++++++-
55
create mode 100644 ebpf/ebpf_rss.c
50
net/announce.c | 89 +++++++++++++++++++++++----
56
create mode 100644 ebpf/ebpf_rss.h
51
net/colo-compare.c | 155 ++++++++++++++++++++++++++++++++++++++++++------
57
create mode 100644 ebpf/meson.build
52
net/net.c | 99 +++++++++++++++----------------
58
create mode 100644 ebpf/rss.bpf.skeleton.h
53
net/trace-events | 3 +-
59
create mode 100644 ebpf/trace-events
54
qapi/net.json | 16 ++++-
60
create mode 100644 ebpf/trace.h
55
qemu-bridge-helper.c | 12 +++-
61
create mode 100755 tools/ebpf/Makefile.ebpf
56
qemu-options.hx | 33 ++++++++++-
62
create mode 100644 tools/ebpf/rss.bpf.c
57
tests/virtio-net-test.c | 57 +++++++++++++++++-
58
15 files changed, 430 insertions(+), 99 deletions(-)
59
63
60
64
diff view generated by jsdifflib
1
From: Zhang Chen <chen.zhang@intel.com>
1
From: Andrew Melnychenko <andrew@daynix.com>
2
2
3
We need to notify net filter to do checkpoint for Xen COLO, like KVM side.
3
Additional code that will be used for eBPF setting steering routine.
4
4
5
Signed-off-by: Zhang Chen <chen.zhang@intel.com>
5
Signed-off-by: Andrew Melnychenko <andrew@daynix.com>
6
Signed-off-by: Jason Wang <jasowang@redhat.com>
6
Signed-off-by: Jason Wang <jasowang@redhat.com>
7
---
7
---
8
migration/colo.c | 2 ++
8
net/tap-linux.h | 1 +
9
1 file changed, 2 insertions(+)
9
1 file changed, 1 insertion(+)
10
10
11
diff --git a/migration/colo.c b/migration/colo.c
11
diff --git a/net/tap-linux.h b/net/tap-linux.h
12
index XXXXXXX..XXXXXXX 100644
12
index XXXXXXX..XXXXXXX 100644
13
--- a/migration/colo.c
13
--- a/net/tap-linux.h
14
+++ b/migration/colo.c
14
+++ b/net/tap-linux.h
15
@@ -XXX,XX +XXX,XX @@ ReplicationStatus *qmp_query_xen_replication_status(Error **errp)
15
@@ -XXX,XX +XXX,XX @@
16
void qmp_xen_colo_do_checkpoint(Error **errp)
16
#define TUNSETQUEUE _IOW('T', 217, int)
17
{
17
#define TUNSETVNETLE _IOW('T', 220, int)
18
replication_do_checkpoint_all(errp);
18
#define TUNSETVNETBE _IOW('T', 222, int)
19
+ /* Notify all filters of all NIC to do checkpoint */
19
+#define TUNSETSTEERINGEBPF _IOR('T', 224, int)
20
+ colo_notify_filters_event(COLO_EVENT_CHECKPOINT, errp);
20
21
}
22
#endif
21
#endif
23
22
24
--
23
--
25
2.5.0
24
2.7.4
26
25
27
26
diff view generated by jsdifflib
1
From: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
1
From: Andrew Melnychenko <andrew@daynix.com>
2
2
3
Add the optional interface list to the HMP command.
3
For now, that method supported only by Linux TAP.
4
Linux TAP uses TUNSETSTEERINGEBPF ioctl.
4
5
5
i.e.
6
Signed-off-by: Andrew Melnychenko <andrew@daynix.com>
6
7
All interfaces
8
announce_self
9
10
Just the named interfaces:
11
announce_self vn1,vn2
12
13
Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
14
Signed-off-by: Jason Wang <jasowang@redhat.com>
7
Signed-off-by: Jason Wang <jasowang@redhat.com>
15
---
8
---
16
hmp-commands.hx | 6 ++++--
9
include/net/net.h | 2 ++
17
monitor/hmp-cmds.c | 38 +++++++++++++++++++++++++++++++++++++-
10
net/tap-bsd.c | 5 +++++
18
2 files changed, 41 insertions(+), 3 deletions(-)
11
net/tap-linux.c | 13 +++++++++++++
12
net/tap-solaris.c | 5 +++++
13
net/tap-stub.c | 5 +++++
14
net/tap.c | 9 +++++++++
15
net/tap_int.h | 1 +
16
7 files changed, 40 insertions(+)
19
17
20
diff --git a/hmp-commands.hx b/hmp-commands.hx
18
diff --git a/include/net/net.h b/include/net/net.h
21
index XXXXXXX..XXXXXXX 100644
19
index XXXXXXX..XXXXXXX 100644
22
--- a/hmp-commands.hx
20
--- a/include/net/net.h
23
+++ b/hmp-commands.hx
21
+++ b/include/net/net.h
24
@@ -XXX,XX +XXX,XX @@ ETEXI
22
@@ -XXX,XX +XXX,XX @@ typedef int (SetVnetBE)(NetClientState *, bool);
25
23
typedef struct SocketReadState SocketReadState;
26
{
24
typedef void (SocketReadStateFinalize)(SocketReadState *rs);
27
.name = "announce_self",
25
typedef void (NetAnnounce)(NetClientState *);
28
- .args_type = "",
26
+typedef bool (SetSteeringEBPF)(NetClientState *, int);
29
- .params = "",
27
30
+ .args_type = "interfaces:s?",
28
typedef struct NetClientInfo {
31
+ .params = "[interfaces]",
29
NetClientDriver type;
32
.help = "Trigger GARP/RARP announcements",
30
@@ -XXX,XX +XXX,XX @@ typedef struct NetClientInfo {
33
.cmd = hmp_announce_self,
31
SetVnetLE *set_vnet_le;
34
},
32
SetVnetBE *set_vnet_be;
35
@@ -XXX,XX +XXX,XX @@ STEXI
33
NetAnnounce *announce;
36
Trigger a round of GARP/RARP broadcasts; this is useful for explicitly updating the
34
+ SetSteeringEBPF *set_steering_ebpf;
37
network infrastructure after a reconfiguration or some forms of migration.
35
} NetClientInfo;
38
The timings of the round are set by the migration announce parameters.
36
39
+An optional comma separated @var{interfaces} list restricts the announce to the
37
struct NetClientState {
40
+named set of interfaces.
38
diff --git a/net/tap-bsd.c b/net/tap-bsd.c
41
ETEXI
42
43
{
44
diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c
45
index XXXXXXX..XXXXXXX 100644
39
index XXXXXXX..XXXXXXX 100644
46
--- a/monitor/hmp-cmds.c
40
--- a/net/tap-bsd.c
47
+++ b/monitor/hmp-cmds.c
41
+++ b/net/tap-bsd.c
48
@@ -XXX,XX +XXX,XX @@
42
@@ -XXX,XX +XXX,XX @@ int tap_fd_get_ifname(int fd, char *ifname)
49
#include "monitor/monitor-internal.h"
43
{
50
#include "monitor/qdev.h"
44
return -1;
51
#include "qapi/error.h"
52
+#include "qapi/clone-visitor.h"
53
#include "qapi/opts-visitor.h"
54
#include "qapi/qapi-builtin-visit.h"
55
#include "qapi/qapi-commands-block.h"
56
@@ -XXX,XX +XXX,XX @@
57
#include "qapi/qapi-commands-run-state.h"
58
#include "qapi/qapi-commands-tpm.h"
59
#include "qapi/qapi-commands-ui.h"
60
+#include "qapi/qapi-visit-net.h"
61
#include "qapi/qmp/qdict.h"
62
#include "qapi/qmp/qerror.h"
63
#include "qapi/string-input-visitor.h"
64
@@ -XXX,XX +XXX,XX @@ static void hmp_handle_error(Monitor *mon, Error **errp)
65
}
66
}
45
}
67
46
+
68
+/*
47
+int tap_fd_set_steering_ebpf(int fd, int prog_fd)
69
+ * Produce a strList from a comma separated list.
70
+ * A NULL or empty input string return NULL.
71
+ */
72
+static strList *strList_from_comma_list(const char *in)
73
+{
48
+{
74
+ strList *res = NULL;
49
+ return -1;
75
+ strList **hook = &res;
50
+}
51
diff --git a/net/tap-linux.c b/net/tap-linux.c
52
index XXXXXXX..XXXXXXX 100644
53
--- a/net/tap-linux.c
54
+++ b/net/tap-linux.c
55
@@ -XXX,XX +XXX,XX @@ int tap_fd_get_ifname(int fd, char *ifname)
56
pstrcpy(ifname, sizeof(ifr.ifr_name), ifr.ifr_name);
57
return 0;
58
}
76
+
59
+
77
+ while (in && in[0]) {
60
+int tap_fd_set_steering_ebpf(int fd, int prog_fd)
78
+ char *comma = strchr(in, ',');
61
+{
79
+ *hook = g_new0(strList, 1);
62
+ if (ioctl(fd, TUNSETSTEERINGEBPF, (void *) &prog_fd) != 0) {
63
+ error_report("Issue while setting TUNSETSTEERINGEBPF:"
64
+ " %s with fd: %d, prog_fd: %d",
65
+ strerror(errno), fd, prog_fd);
80
+
66
+
81
+ if (comma) {
67
+ return -1;
82
+ (*hook)->value = g_strndup(in, comma - in);
83
+ in = comma + 1; /* skip the , */
84
+ } else {
85
+ (*hook)->value = g_strdup(in);
86
+ in = NULL;
87
+ }
88
+ hook = &(*hook)->next;
89
+ }
68
+ }
90
+
69
+
91
+ return res;
70
+ return 0;
71
+}
72
diff --git a/net/tap-solaris.c b/net/tap-solaris.c
73
index XXXXXXX..XXXXXXX 100644
74
--- a/net/tap-solaris.c
75
+++ b/net/tap-solaris.c
76
@@ -XXX,XX +XXX,XX @@ int tap_fd_get_ifname(int fd, char *ifname)
77
{
78
return -1;
79
}
80
+
81
+int tap_fd_set_steering_ebpf(int fd, int prog_fd)
82
+{
83
+ return -1;
84
+}
85
diff --git a/net/tap-stub.c b/net/tap-stub.c
86
index XXXXXXX..XXXXXXX 100644
87
--- a/net/tap-stub.c
88
+++ b/net/tap-stub.c
89
@@ -XXX,XX +XXX,XX @@ int tap_fd_get_ifname(int fd, char *ifname)
90
{
91
return -1;
92
}
93
+
94
+int tap_fd_set_steering_ebpf(int fd, int prog_fd)
95
+{
96
+ return -1;
97
+}
98
diff --git a/net/tap.c b/net/tap.c
99
index XXXXXXX..XXXXXXX 100644
100
--- a/net/tap.c
101
+++ b/net/tap.c
102
@@ -XXX,XX +XXX,XX @@ static void tap_poll(NetClientState *nc, bool enable)
103
tap_write_poll(s, enable);
104
}
105
106
+static bool tap_set_steering_ebpf(NetClientState *nc, int prog_fd)
107
+{
108
+ TAPState *s = DO_UPCAST(TAPState, nc, nc);
109
+ assert(nc->info->type == NET_CLIENT_DRIVER_TAP);
110
+
111
+ return tap_fd_set_steering_ebpf(s->fd, prog_fd) == 0;
92
+}
112
+}
93
+
113
+
94
void hmp_info_name(Monitor *mon, const QDict *qdict)
114
int tap_get_fd(NetClientState *nc)
95
{
115
{
96
NameInfo *info;
116
TAPState *s = DO_UPCAST(TAPState, nc, nc);
97
@@ -XXX,XX +XXX,XX @@ void hmp_info_snapshots(Monitor *mon, const QDict *qdict)
117
@@ -XXX,XX +XXX,XX @@ static NetClientInfo net_tap_info = {
98
118
.set_vnet_hdr_len = tap_set_vnet_hdr_len,
99
void hmp_announce_self(Monitor *mon, const QDict *qdict)
119
.set_vnet_le = tap_set_vnet_le,
100
{
120
.set_vnet_be = tap_set_vnet_be,
101
- qmp_announce_self(migrate_announce_params(), NULL);
121
+ .set_steering_ebpf = tap_set_steering_ebpf,
102
+ const char *interfaces_str = qdict_get_try_str(qdict, "interfaces");
122
};
103
+ AnnounceParameters *params = QAPI_CLONE(AnnounceParameters,
123
104
+ migrate_announce_params());
124
static TAPState *net_tap_fd_init(NetClientState *peer,
105
+
125
diff --git a/net/tap_int.h b/net/tap_int.h
106
+ qapi_free_strList(params->interfaces);
126
index XXXXXXX..XXXXXXX 100644
107
+ params->interfaces = strList_from_comma_list(interfaces_str);
127
--- a/net/tap_int.h
108
+ params->has_interfaces = params->interfaces != NULL;
128
+++ b/net/tap_int.h
109
+ qmp_announce_self(params, NULL);
129
@@ -XXX,XX +XXX,XX @@ int tap_fd_set_vnet_be(int fd, int vnet_is_be);
110
+ qapi_free_AnnounceParameters(params);
130
int tap_fd_enable(int fd);
111
}
131
int tap_fd_disable(int fd);
112
132
int tap_fd_get_ifname(int fd, char *ifname);
113
void hmp_migrate_cancel(Monitor *mon, const QDict *qdict)
133
+int tap_fd_set_steering_ebpf(int fd, int prog_fd);
134
135
#endif /* NET_TAP_INT_H */
114
--
136
--
115
2.5.0
137
2.7.4
116
138
117
139
diff view generated by jsdifflib
1
From: Markus Armbruster <armbru@redhat.com>
1
From: Andrew Melnychenko <andrew@daynix.com>
2
2
3
Signed-off-by: Markus Armbruster <armbru@redhat.com>
3
RSS program and Makefile to build it.
4
The bpftool used to generate '.h' file.
5
The data in that file may be loaded by libbpf.
6
EBPF compilation is not required for building qemu.
7
You can use Makefile if you need to regenerate rss.bpf.skeleton.h.
8
9
Signed-off-by: Yuri Benditovich <yuri.benditovich@daynix.com>
10
Signed-off-by: Andrew Melnychenko <andrew@daynix.com>
4
Signed-off-by: Jason Wang <jasowang@redhat.com>
11
Signed-off-by: Jason Wang <jasowang@redhat.com>
5
---
12
---
6
qemu-bridge-helper.c | 12 +++++++++++-
13
tools/ebpf/Makefile.ebpf | 21 ++
7
1 file changed, 11 insertions(+), 1 deletion(-)
14
tools/ebpf/rss.bpf.c | 571 +++++++++++++++++++++++++++++++++++++++++++++++
15
2 files changed, 592 insertions(+)
16
create mode 100755 tools/ebpf/Makefile.ebpf
17
create mode 100644 tools/ebpf/rss.bpf.c
8
18
9
diff --git a/qemu-bridge-helper.c b/qemu-bridge-helper.c
19
diff --git a/tools/ebpf/Makefile.ebpf b/tools/ebpf/Makefile.ebpf
10
index XXXXXXX..XXXXXXX 100644
20
new file mode 100755
11
--- a/qemu-bridge-helper.c
21
index XXXXXXX..XXXXXXX
12
+++ b/qemu-bridge-helper.c
22
--- /dev/null
23
+++ b/tools/ebpf/Makefile.ebpf
13
@@ -XXX,XX +XXX,XX @@
24
@@ -XXX,XX +XXX,XX @@
14
*
25
+OBJS = rss.bpf.o
15
* This work is licensed under the terms of the GNU GPL, version 2. See
26
+
16
* the COPYING file in the top-level directory.
27
+LLC ?= llc
17
- *
28
+CLANG ?= clang
29
+INC_FLAGS = `$(CLANG) -print-file-name=include`
30
+EXTRA_CFLAGS ?= -O2 -emit-llvm -fno-stack-protector
31
+
32
+all: $(OBJS)
33
+
34
+.PHONY: clean
35
+
36
+clean:
37
+    rm -f $(OBJS)
38
+
39
+$(OBJS): %.o:%.c
40
+    $(CLANG) $(INC_FLAGS) \
41
+ -D__KERNEL__ -D__ASM_SYSREG_H \
42
+ -I../include $(LINUXINCLUDE) \
43
+ $(EXTRA_CFLAGS) -c $< -o -| $(LLC) -march=bpf -filetype=obj -o $@
44
+    bpftool gen skeleton rss.bpf.o > rss.bpf.skeleton.h
45
+    cp rss.bpf.skeleton.h ../../ebpf/
46
diff --git a/tools/ebpf/rss.bpf.c b/tools/ebpf/rss.bpf.c
47
new file mode 100644
48
index XXXXXXX..XXXXXXX
49
--- /dev/null
50
+++ b/tools/ebpf/rss.bpf.c
51
@@ -XXX,XX +XXX,XX @@
52
+/*
53
+ * eBPF RSS program
54
+ *
55
+ * Developed by Daynix Computing LTD (http://www.daynix.com)
56
+ *
57
+ * Authors:
58
+ * Andrew Melnychenko <andrew@daynix.com>
59
+ * Yuri Benditovich <yuri.benditovich@daynix.com>
60
+ *
61
+ * This work is licensed under the terms of the GNU GPL, version 2. See
62
+ * the COPYING file in the top-level directory.
63
+ *
64
+ * Prepare:
65
+ * Requires llvm, clang, bpftool, linux kernel tree
66
+ *
67
+ * Build rss.bpf.skeleton.h:
68
+ * make -f Makefile.ebpf clean all
18
+ */
69
+ */
19
+
70
+
71
+#include <stddef.h>
72
+#include <stdbool.h>
73
+#include <linux/bpf.h>
74
+
75
+#include <linux/in.h>
76
+#include <linux/if_ether.h>
77
+#include <linux/ip.h>
78
+#include <linux/ipv6.h>
79
+
80
+#include <linux/udp.h>
81
+#include <linux/tcp.h>
82
+
83
+#include <bpf/bpf_helpers.h>
84
+#include <bpf/bpf_endian.h>
85
+#include <linux/virtio_net.h>
86
+
87
+#define INDIRECTION_TABLE_SIZE 128
88
+#define HASH_CALCULATION_BUFFER_SIZE 36
89
+
90
+struct rss_config_t {
91
+ __u8 redirect;
92
+ __u8 populate_hash;
93
+ __u32 hash_types;
94
+ __u16 indirections_len;
95
+ __u16 default_queue;
96
+} __attribute__((packed));
97
+
98
+struct toeplitz_key_data_t {
99
+ __u32 leftmost_32_bits;
100
+ __u8 next_byte[HASH_CALCULATION_BUFFER_SIZE];
101
+};
102
+
103
+struct packet_hash_info_t {
104
+ __u8 is_ipv4;
105
+ __u8 is_ipv6;
106
+ __u8 is_udp;
107
+ __u8 is_tcp;
108
+ __u8 is_ipv6_ext_src;
109
+ __u8 is_ipv6_ext_dst;
110
+ __u8 is_fragmented;
111
+
112
+ __u16 src_port;
113
+ __u16 dst_port;
114
+
115
+ union {
116
+ struct {
117
+ __be32 in_src;
118
+ __be32 in_dst;
119
+ };
120
+
121
+ struct {
122
+ struct in6_addr in6_src;
123
+ struct in6_addr in6_dst;
124
+ struct in6_addr in6_ext_src;
125
+ struct in6_addr in6_ext_dst;
126
+ };
127
+ };
128
+};
129
+
130
+struct bpf_map_def SEC("maps")
131
+tap_rss_map_configurations = {
132
+ .type = BPF_MAP_TYPE_ARRAY,
133
+ .key_size = sizeof(__u32),
134
+ .value_size = sizeof(struct rss_config_t),
135
+ .max_entries = 1,
136
+};
137
+
138
+struct bpf_map_def SEC("maps")
139
+tap_rss_map_toeplitz_key = {
140
+ .type = BPF_MAP_TYPE_ARRAY,
141
+ .key_size = sizeof(__u32),
142
+ .value_size = sizeof(struct toeplitz_key_data_t),
143
+ .max_entries = 1,
144
+};
145
+
146
+struct bpf_map_def SEC("maps")
147
+tap_rss_map_indirection_table = {
148
+ .type = BPF_MAP_TYPE_ARRAY,
149
+ .key_size = sizeof(__u32),
150
+ .value_size = sizeof(__u16),
151
+ .max_entries = INDIRECTION_TABLE_SIZE,
152
+};
153
+
154
+static inline void net_rx_rss_add_chunk(__u8 *rss_input, size_t *bytes_written,
155
+ const void *ptr, size_t size) {
156
+ __builtin_memcpy(&rss_input[*bytes_written], ptr, size);
157
+ *bytes_written += size;
158
+}
159
+
160
+static inline
161
+void net_toeplitz_add(__u32 *result,
162
+ __u8 *input,
163
+ __u32 len
164
+ , struct toeplitz_key_data_t *key) {
165
+
166
+ __u32 accumulator = *result;
167
+ __u32 leftmost_32_bits = key->leftmost_32_bits;
168
+ __u32 byte;
169
+
170
+ for (byte = 0; byte < HASH_CALCULATION_BUFFER_SIZE; byte++) {
171
+ __u8 input_byte = input[byte];
172
+ __u8 key_byte = key->next_byte[byte];
173
+ __u8 bit;
174
+
175
+ for (bit = 0; bit < 8; bit++) {
176
+ if (input_byte & (1 << 7)) {
177
+ accumulator ^= leftmost_32_bits;
178
+ }
179
+
180
+ leftmost_32_bits =
181
+ (leftmost_32_bits << 1) | ((key_byte & (1 << 7)) >> 7);
182
+
183
+ input_byte <<= 1;
184
+ key_byte <<= 1;
185
+ }
186
+ }
187
+
188
+ *result = accumulator;
189
+}
190
+
191
+
192
+static inline int ip6_extension_header_type(__u8 hdr_type)
193
+{
194
+ switch (hdr_type) {
195
+ case IPPROTO_HOPOPTS:
196
+ case IPPROTO_ROUTING:
197
+ case IPPROTO_FRAGMENT:
198
+ case IPPROTO_ICMPV6:
199
+ case IPPROTO_NONE:
200
+ case IPPROTO_DSTOPTS:
201
+ case IPPROTO_MH:
202
+ return 1;
203
+ default:
204
+ return 0;
205
+ }
206
+}
20
+/*
207
+/*
21
+ * Known shortcomings:
208
+ * According to
22
+ * - There is no manual page
209
+ * https://www.iana.org/assignments/ipv6-parameters/ipv6-parameters.xhtml
23
+ * - The syntax of the ACL file is not documented anywhere
210
+ * we expect that there are would be no more than 11 extensions in IPv6 header,
24
+ * - parse_acl_file() doesn't report fopen() failure properly, fails
211
+ * also there is 27 TLV options for Destination and Hop-by-hop extensions.
25
+ * to check ferror() after fgets() failure, arbitrarily truncates
212
+ * Need to choose reasonable amount of maximum extensions/options we may
26
+ * long lines, handles whitespace inconsistently, error messages
213
+ * check to find ext src/dst.
27
+ * don't point to the offending file and line, errors in included
214
+ */
28
+ * files are reported, but otherwise ignored, ...
215
+#define IP6_EXTENSIONS_COUNT 11
29
*/
216
+#define IP6_OPTIONS_COUNT 30
30
217
+
31
#include "qemu/osdep.h"
218
+static inline int parse_ipv6_ext(struct __sk_buff *skb,
219
+ struct packet_hash_info_t *info,
220
+ __u8 *l4_protocol, size_t *l4_offset)
221
+{
222
+ int err = 0;
223
+
224
+ if (!ip6_extension_header_type(*l4_protocol)) {
225
+ return 0;
226
+ }
227
+
228
+ struct ipv6_opt_hdr ext_hdr = {};
229
+
230
+ for (unsigned int i = 0; i < IP6_EXTENSIONS_COUNT; ++i) {
231
+
232
+ err = bpf_skb_load_bytes_relative(skb, *l4_offset, &ext_hdr,
233
+ sizeof(ext_hdr), BPF_HDR_START_NET);
234
+ if (err) {
235
+ goto error;
236
+ }
237
+
238
+ if (*l4_protocol == IPPROTO_ROUTING) {
239
+ struct ipv6_rt_hdr ext_rt = {};
240
+
241
+ err = bpf_skb_load_bytes_relative(skb, *l4_offset, &ext_rt,
242
+ sizeof(ext_rt), BPF_HDR_START_NET);
243
+ if (err) {
244
+ goto error;
245
+ }
246
+
247
+ if ((ext_rt.type == IPV6_SRCRT_TYPE_2) &&
248
+ (ext_rt.hdrlen == sizeof(struct in6_addr) / 8) &&
249
+ (ext_rt.segments_left == 1)) {
250
+
251
+ err = bpf_skb_load_bytes_relative(skb,
252
+ *l4_offset + offsetof(struct rt2_hdr, addr),
253
+ &info->in6_ext_dst, sizeof(info->in6_ext_dst),
254
+ BPF_HDR_START_NET);
255
+ if (err) {
256
+ goto error;
257
+ }
258
+
259
+ info->is_ipv6_ext_dst = 1;
260
+ }
261
+
262
+ } else if (*l4_protocol == IPPROTO_DSTOPTS) {
263
+ struct ipv6_opt_t {
264
+ __u8 type;
265
+ __u8 length;
266
+ } __attribute__((packed)) opt = {};
267
+
268
+ size_t opt_offset = sizeof(ext_hdr);
269
+
270
+ for (unsigned int j = 0; j < IP6_OPTIONS_COUNT; ++j) {
271
+ err = bpf_skb_load_bytes_relative(skb, *l4_offset + opt_offset,
272
+ &opt, sizeof(opt), BPF_HDR_START_NET);
273
+ if (err) {
274
+ goto error;
275
+ }
276
+
277
+ if (opt.type == IPV6_TLV_HAO) {
278
+ err = bpf_skb_load_bytes_relative(skb,
279
+ *l4_offset + opt_offset
280
+ + offsetof(struct ipv6_destopt_hao, addr),
281
+ &info->in6_ext_src, sizeof(info->in6_ext_src),
282
+ BPF_HDR_START_NET);
283
+ if (err) {
284
+ goto error;
285
+ }
286
+
287
+ info->is_ipv6_ext_src = 1;
288
+ break;
289
+ }
290
+
291
+ opt_offset += (opt.type == IPV6_TLV_PAD1) ?
292
+ 1 : opt.length + sizeof(opt);
293
+
294
+ if (opt_offset + 1 >= ext_hdr.hdrlen * 8) {
295
+ break;
296
+ }
297
+ }
298
+ } else if (*l4_protocol == IPPROTO_FRAGMENT) {
299
+ info->is_fragmented = true;
300
+ }
301
+
302
+ *l4_protocol = ext_hdr.nexthdr;
303
+ *l4_offset += (ext_hdr.hdrlen + 1) * 8;
304
+
305
+ if (!ip6_extension_header_type(ext_hdr.nexthdr)) {
306
+ return 0;
307
+ }
308
+ }
309
+
310
+ return 0;
311
+error:
312
+ return err;
313
+}
314
+
315
+static __be16 parse_eth_type(struct __sk_buff *skb)
316
+{
317
+ unsigned int offset = 12;
318
+ __be16 ret = 0;
319
+ int err = 0;
320
+
321
+ err = bpf_skb_load_bytes_relative(skb, offset, &ret, sizeof(ret),
322
+ BPF_HDR_START_MAC);
323
+ if (err) {
324
+ return 0;
325
+ }
326
+
327
+ switch (bpf_ntohs(ret)) {
328
+ case ETH_P_8021AD:
329
+ offset += 4;
330
+ case ETH_P_8021Q:
331
+ offset += 4;
332
+ err = bpf_skb_load_bytes_relative(skb, offset, &ret, sizeof(ret),
333
+ BPF_HDR_START_MAC);
334
+ default:
335
+ break;
336
+ }
337
+
338
+ if (err) {
339
+ return 0;
340
+ }
341
+
342
+ return ret;
343
+}
344
+
345
+static inline int parse_packet(struct __sk_buff *skb,
346
+ struct packet_hash_info_t *info)
347
+{
348
+ int err = 0;
349
+
350
+ if (!info || !skb) {
351
+ return -1;
352
+ }
353
+
354
+ size_t l4_offset = 0;
355
+ __u8 l4_protocol = 0;
356
+ __u16 l3_protocol = bpf_ntohs(parse_eth_type(skb));
357
+ if (l3_protocol == 0) {
358
+ err = -1;
359
+ goto error;
360
+ }
361
+
362
+ if (l3_protocol == ETH_P_IP) {
363
+ info->is_ipv4 = 1;
364
+
365
+ struct iphdr ip = {};
366
+ err = bpf_skb_load_bytes_relative(skb, 0, &ip, sizeof(ip),
367
+ BPF_HDR_START_NET);
368
+ if (err) {
369
+ goto error;
370
+ }
371
+
372
+ info->in_src = ip.saddr;
373
+ info->in_dst = ip.daddr;
374
+ info->is_fragmented = !!ip.frag_off;
375
+
376
+ l4_protocol = ip.protocol;
377
+ l4_offset = ip.ihl * 4;
378
+ } else if (l3_protocol == ETH_P_IPV6) {
379
+ info->is_ipv6 = 1;
380
+
381
+ struct ipv6hdr ip6 = {};
382
+ err = bpf_skb_load_bytes_relative(skb, 0, &ip6, sizeof(ip6),
383
+ BPF_HDR_START_NET);
384
+ if (err) {
385
+ goto error;
386
+ }
387
+
388
+ info->in6_src = ip6.saddr;
389
+ info->in6_dst = ip6.daddr;
390
+
391
+ l4_protocol = ip6.nexthdr;
392
+ l4_offset = sizeof(ip6);
393
+
394
+ err = parse_ipv6_ext(skb, info, &l4_protocol, &l4_offset);
395
+ if (err) {
396
+ goto error;
397
+ }
398
+ }
399
+
400
+ if (l4_protocol != 0 && !info->is_fragmented) {
401
+ if (l4_protocol == IPPROTO_TCP) {
402
+ info->is_tcp = 1;
403
+
404
+ struct tcphdr tcp = {};
405
+ err = bpf_skb_load_bytes_relative(skb, l4_offset, &tcp, sizeof(tcp),
406
+ BPF_HDR_START_NET);
407
+ if (err) {
408
+ goto error;
409
+ }
410
+
411
+ info->src_port = tcp.source;
412
+ info->dst_port = tcp.dest;
413
+ } else if (l4_protocol == IPPROTO_UDP) { /* TODO: add udplite? */
414
+ info->is_udp = 1;
415
+
416
+ struct udphdr udp = {};
417
+ err = bpf_skb_load_bytes_relative(skb, l4_offset, &udp, sizeof(udp),
418
+ BPF_HDR_START_NET);
419
+ if (err) {
420
+ goto error;
421
+ }
422
+
423
+ info->src_port = udp.source;
424
+ info->dst_port = udp.dest;
425
+ }
426
+ }
427
+
428
+ return 0;
429
+
430
+error:
431
+ return err;
432
+}
433
+
434
+static inline __u32 calculate_rss_hash(struct __sk_buff *skb,
435
+ struct rss_config_t *config, struct toeplitz_key_data_t *toe)
436
+{
437
+ __u8 rss_input[HASH_CALCULATION_BUFFER_SIZE] = {};
438
+ size_t bytes_written = 0;
439
+ __u32 result = 0;
440
+ int err = 0;
441
+ struct packet_hash_info_t packet_info = {};
442
+
443
+ err = parse_packet(skb, &packet_info);
444
+ if (err) {
445
+ return 0;
446
+ }
447
+
448
+ if (packet_info.is_ipv4) {
449
+ if (packet_info.is_tcp &&
450
+ config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_TCPv4) {
451
+
452
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
453
+ &packet_info.in_src,
454
+ sizeof(packet_info.in_src));
455
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
456
+ &packet_info.in_dst,
457
+ sizeof(packet_info.in_dst));
458
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
459
+ &packet_info.src_port,
460
+ sizeof(packet_info.src_port));
461
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
462
+ &packet_info.dst_port,
463
+ sizeof(packet_info.dst_port));
464
+ } else if (packet_info.is_udp &&
465
+ config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_UDPv4) {
466
+
467
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
468
+ &packet_info.in_src,
469
+ sizeof(packet_info.in_src));
470
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
471
+ &packet_info.in_dst,
472
+ sizeof(packet_info.in_dst));
473
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
474
+ &packet_info.src_port,
475
+ sizeof(packet_info.src_port));
476
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
477
+ &packet_info.dst_port,
478
+ sizeof(packet_info.dst_port));
479
+ } else if (config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_IPv4) {
480
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
481
+ &packet_info.in_src,
482
+ sizeof(packet_info.in_src));
483
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
484
+ &packet_info.in_dst,
485
+ sizeof(packet_info.in_dst));
486
+ }
487
+ } else if (packet_info.is_ipv6) {
488
+ if (packet_info.is_tcp &&
489
+ config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_TCPv6) {
490
+
491
+ if (packet_info.is_ipv6_ext_src &&
492
+ config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_TCP_EX) {
493
+
494
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
495
+ &packet_info.in6_ext_src,
496
+ sizeof(packet_info.in6_ext_src));
497
+ } else {
498
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
499
+ &packet_info.in6_src,
500
+ sizeof(packet_info.in6_src));
501
+ }
502
+ if (packet_info.is_ipv6_ext_dst &&
503
+ config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_TCP_EX) {
504
+
505
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
506
+ &packet_info.in6_ext_dst,
507
+ sizeof(packet_info.in6_ext_dst));
508
+ } else {
509
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
510
+ &packet_info.in6_dst,
511
+ sizeof(packet_info.in6_dst));
512
+ }
513
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
514
+ &packet_info.src_port,
515
+ sizeof(packet_info.src_port));
516
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
517
+ &packet_info.dst_port,
518
+ sizeof(packet_info.dst_port));
519
+ } else if (packet_info.is_udp &&
520
+ config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_UDPv6) {
521
+
522
+ if (packet_info.is_ipv6_ext_src &&
523
+ config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_UDP_EX) {
524
+
525
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
526
+ &packet_info.in6_ext_src,
527
+ sizeof(packet_info.in6_ext_src));
528
+ } else {
529
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
530
+ &packet_info.in6_src,
531
+ sizeof(packet_info.in6_src));
532
+ }
533
+ if (packet_info.is_ipv6_ext_dst &&
534
+ config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_UDP_EX) {
535
+
536
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
537
+ &packet_info.in6_ext_dst,
538
+ sizeof(packet_info.in6_ext_dst));
539
+ } else {
540
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
541
+ &packet_info.in6_dst,
542
+ sizeof(packet_info.in6_dst));
543
+ }
544
+
545
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
546
+ &packet_info.src_port,
547
+ sizeof(packet_info.src_port));
548
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
549
+ &packet_info.dst_port,
550
+ sizeof(packet_info.dst_port));
551
+
552
+ } else if (config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_IPv6) {
553
+ if (packet_info.is_ipv6_ext_src &&
554
+ config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_IP_EX) {
555
+
556
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
557
+ &packet_info.in6_ext_src,
558
+ sizeof(packet_info.in6_ext_src));
559
+ } else {
560
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
561
+ &packet_info.in6_src,
562
+ sizeof(packet_info.in6_src));
563
+ }
564
+ if (packet_info.is_ipv6_ext_dst &&
565
+ config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_IP_EX) {
566
+
567
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
568
+ &packet_info.in6_ext_dst,
569
+ sizeof(packet_info.in6_ext_dst));
570
+ } else {
571
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
572
+ &packet_info.in6_dst,
573
+ sizeof(packet_info.in6_dst));
574
+ }
575
+ }
576
+ }
577
+
578
+ if (bytes_written) {
579
+ net_toeplitz_add(&result, rss_input, bytes_written, toe);
580
+ }
581
+
582
+ return result;
583
+}
584
+
585
+SEC("tun_rss_steering")
586
+int tun_rss_steering_prog(struct __sk_buff *skb)
587
+{
588
+
589
+ struct rss_config_t *config;
590
+ struct toeplitz_key_data_t *toe;
591
+
592
+ __u32 key = 0;
593
+ __u32 hash = 0;
594
+
595
+ config = bpf_map_lookup_elem(&tap_rss_map_configurations, &key);
596
+ toe = bpf_map_lookup_elem(&tap_rss_map_toeplitz_key, &key);
597
+
598
+ if (config && toe) {
599
+ if (!config->redirect) {
600
+ return config->default_queue;
601
+ }
602
+
603
+ hash = calculate_rss_hash(skb, config, toe);
604
+ if (hash) {
605
+ __u32 table_idx = hash % config->indirections_len;
606
+ __u16 *queue = 0;
607
+
608
+ queue = bpf_map_lookup_elem(&tap_rss_map_indirection_table,
609
+ &table_idx);
610
+
611
+ if (queue) {
612
+ return *queue;
613
+ }
614
+ }
615
+
616
+ return config->default_queue;
617
+ }
618
+
619
+ return -1;
620
+}
621
+
622
+char _license[] SEC("license") = "GPL v2";
32
--
623
--
33
2.5.0
624
2.7.4
34
625
35
626
diff view generated by jsdifflib
1
From: Zhang Chen <chen.zhang@intel.com>
1
From: Andrew Melnychenko <andrew@daynix.com>
2
2
3
This patch make colo-compare can send message to remote COLO frame(Xen) when occur checkpoint.
3
Added function that loads RSS eBPF program.
4
Added stub functions for RSS eBPF loader.
5
Added meson and configuration options.
4
6
5
Signed-off-by: Zhang Chen <chen.zhang@intel.com>
7
By default, eBPF feature enabled if libbpf is present in the build system.
8
libbpf checked in configuration shell script and meson script.
9
10
Signed-off-by: Yuri Benditovich <yuri.benditovich@daynix.com>
11
Signed-off-by: Andrew Melnychenko <andrew@daynix.com>
6
Signed-off-by: Jason Wang <jasowang@redhat.com>
12
Signed-off-by: Jason Wang <jasowang@redhat.com>
7
---
13
---
8
net/colo-compare.c | 54 ++++++++++++++++++++++++++++++++++++++++++++----------
14
configure | 8 +-
9
1 file changed, 44 insertions(+), 10 deletions(-)
15
ebpf/ebpf_rss-stub.c | 40 +++++
16
ebpf/ebpf_rss.c | 165 ++++++++++++++++++
17
ebpf/ebpf_rss.h | 44 +++++
18
ebpf/meson.build | 1 +
19
ebpf/rss.bpf.skeleton.h | 431 ++++++++++++++++++++++++++++++++++++++++++++++++
20
ebpf/trace-events | 4 +
21
ebpf/trace.h | 1 +
22
meson.build | 23 +++
23
meson_options.txt | 2 +
24
10 files changed, 718 insertions(+), 1 deletion(-)
25
create mode 100644 ebpf/ebpf_rss-stub.c
26
create mode 100644 ebpf/ebpf_rss.c
27
create mode 100644 ebpf/ebpf_rss.h
28
create mode 100644 ebpf/meson.build
29
create mode 100644 ebpf/rss.bpf.skeleton.h
30
create mode 100644 ebpf/trace-events
31
create mode 100644 ebpf/trace.h
10
32
11
diff --git a/net/colo-compare.c b/net/colo-compare.c
33
diff --git a/configure b/configure
34
index XXXXXXX..XXXXXXX 100755
35
--- a/configure
36
+++ b/configure
37
@@ -XXX,XX +XXX,XX @@ vhost_vsock="$default_feature"
38
vhost_user="no"
39
vhost_user_blk_server="auto"
40
vhost_user_fs="$default_feature"
41
+bpf="auto"
42
kvm="auto"
43
hax="auto"
44
hvf="auto"
45
@@ -XXX,XX +XXX,XX @@ for opt do
46
;;
47
--enable-membarrier) membarrier="yes"
48
;;
49
+ --disable-bpf) bpf="disabled"
50
+ ;;
51
+ --enable-bpf) bpf="enabled"
52
+ ;;
53
--disable-blobs) blobs="false"
54
;;
55
--with-pkgversion=*) pkgversion="$optarg"
56
@@ -XXX,XX +XXX,XX @@ disabled with --disable-FEATURE, default is enabled if available
57
vhost-user vhost-user backend support
58
vhost-user-blk-server vhost-user-blk server support
59
vhost-vdpa vhost-vdpa kernel backend support
60
+ bpf BPF kernel support
61
spice spice
62
spice-protocol spice-protocol
63
rbd rados block device (rbd)
64
@@ -XXX,XX +XXX,XX @@ if test "$skip_meson" = no; then
65
-Dattr=$attr -Ddefault_devices=$default_devices \
66
-Ddocs=$docs -Dsphinx_build=$sphinx_build -Dinstall_blobs=$blobs \
67
-Dvhost_user_blk_server=$vhost_user_blk_server -Dmultiprocess=$multiprocess \
68
- -Dfuse=$fuse -Dfuse_lseek=$fuse_lseek -Dguest_agent_msi=$guest_agent_msi \
69
+ -Dfuse=$fuse -Dfuse_lseek=$fuse_lseek -Dguest_agent_msi=$guest_agent_msi -Dbpf=$bpf\
70
$(if test "$default_features" = no; then echo "-Dauto_features=disabled"; fi) \
71
    -Dtcg_interpreter=$tcg_interpreter \
72
$cross_arg \
73
diff --git a/ebpf/ebpf_rss-stub.c b/ebpf/ebpf_rss-stub.c
74
new file mode 100644
75
index XXXXXXX..XXXXXXX
76
--- /dev/null
77
+++ b/ebpf/ebpf_rss-stub.c
78
@@ -XXX,XX +XXX,XX @@
79
+/*
80
+ * eBPF RSS stub file
81
+ *
82
+ * Developed by Daynix Computing LTD (http://www.daynix.com)
83
+ *
84
+ * Authors:
85
+ * Yuri Benditovich <yuri.benditovich@daynix.com>
86
+ *
87
+ * This work is licensed under the terms of the GNU GPL, version 2. See
88
+ * the COPYING file in the top-level directory.
89
+ */
90
+
91
+#include "qemu/osdep.h"
92
+#include "ebpf/ebpf_rss.h"
93
+
94
+void ebpf_rss_init(struct EBPFRSSContext *ctx)
95
+{
96
+
97
+}
98
+
99
+bool ebpf_rss_is_loaded(struct EBPFRSSContext *ctx)
100
+{
101
+ return false;
102
+}
103
+
104
+bool ebpf_rss_load(struct EBPFRSSContext *ctx)
105
+{
106
+ return false;
107
+}
108
+
109
+bool ebpf_rss_set_all(struct EBPFRSSContext *ctx, struct EBPFRSSConfig *config,
110
+ uint16_t *indirections_table, uint8_t *toeplitz_key)
111
+{
112
+ return false;
113
+}
114
+
115
+void ebpf_rss_unload(struct EBPFRSSContext *ctx)
116
+{
117
+
118
+}
119
diff --git a/ebpf/ebpf_rss.c b/ebpf/ebpf_rss.c
120
new file mode 100644
121
index XXXXXXX..XXXXXXX
122
--- /dev/null
123
+++ b/ebpf/ebpf_rss.c
124
@@ -XXX,XX +XXX,XX @@
125
+/*
126
+ * eBPF RSS loader
127
+ *
128
+ * Developed by Daynix Computing LTD (http://www.daynix.com)
129
+ *
130
+ * Authors:
131
+ * Andrew Melnychenko <andrew@daynix.com>
132
+ * Yuri Benditovich <yuri.benditovich@daynix.com>
133
+ *
134
+ * This work is licensed under the terms of the GNU GPL, version 2. See
135
+ * the COPYING file in the top-level directory.
136
+ */
137
+
138
+#include "qemu/osdep.h"
139
+#include "qemu/error-report.h"
140
+
141
+#include <bpf/libbpf.h>
142
+#include <bpf/bpf.h>
143
+
144
+#include "hw/virtio/virtio-net.h" /* VIRTIO_NET_RSS_MAX_TABLE_LEN */
145
+
146
+#include "ebpf/ebpf_rss.h"
147
+#include "ebpf/rss.bpf.skeleton.h"
148
+#include "trace.h"
149
+
150
+void ebpf_rss_init(struct EBPFRSSContext *ctx)
151
+{
152
+ if (ctx != NULL) {
153
+ ctx->obj = NULL;
154
+ }
155
+}
156
+
157
+bool ebpf_rss_is_loaded(struct EBPFRSSContext *ctx)
158
+{
159
+ return ctx != NULL && ctx->obj != NULL;
160
+}
161
+
162
+bool ebpf_rss_load(struct EBPFRSSContext *ctx)
163
+{
164
+ struct rss_bpf *rss_bpf_ctx;
165
+
166
+ if (ctx == NULL) {
167
+ return false;
168
+ }
169
+
170
+ rss_bpf_ctx = rss_bpf__open();
171
+ if (rss_bpf_ctx == NULL) {
172
+ trace_ebpf_error("eBPF RSS", "can not open eBPF RSS object");
173
+ goto error;
174
+ }
175
+
176
+ bpf_program__set_socket_filter(rss_bpf_ctx->progs.tun_rss_steering_prog);
177
+
178
+ if (rss_bpf__load(rss_bpf_ctx)) {
179
+ trace_ebpf_error("eBPF RSS", "can not load RSS program");
180
+ goto error;
181
+ }
182
+
183
+ ctx->obj = rss_bpf_ctx;
184
+ ctx->program_fd = bpf_program__fd(
185
+ rss_bpf_ctx->progs.tun_rss_steering_prog);
186
+ ctx->map_configuration = bpf_map__fd(
187
+ rss_bpf_ctx->maps.tap_rss_map_configurations);
188
+ ctx->map_indirections_table = bpf_map__fd(
189
+ rss_bpf_ctx->maps.tap_rss_map_indirection_table);
190
+ ctx->map_toeplitz_key = bpf_map__fd(
191
+ rss_bpf_ctx->maps.tap_rss_map_toeplitz_key);
192
+
193
+ return true;
194
+error:
195
+ rss_bpf__destroy(rss_bpf_ctx);
196
+ ctx->obj = NULL;
197
+
198
+ return false;
199
+}
200
+
201
+static bool ebpf_rss_set_config(struct EBPFRSSContext *ctx,
202
+ struct EBPFRSSConfig *config)
203
+{
204
+ uint32_t map_key = 0;
205
+
206
+ if (!ebpf_rss_is_loaded(ctx)) {
207
+ return false;
208
+ }
209
+ if (bpf_map_update_elem(ctx->map_configuration,
210
+ &map_key, config, 0) < 0) {
211
+ return false;
212
+ }
213
+ return true;
214
+}
215
+
216
+static bool ebpf_rss_set_indirections_table(struct EBPFRSSContext *ctx,
217
+ uint16_t *indirections_table,
218
+ size_t len)
219
+{
220
+ uint32_t i = 0;
221
+
222
+ if (!ebpf_rss_is_loaded(ctx) || indirections_table == NULL ||
223
+ len > VIRTIO_NET_RSS_MAX_TABLE_LEN) {
224
+ return false;
225
+ }
226
+
227
+ for (; i < len; ++i) {
228
+ if (bpf_map_update_elem(ctx->map_indirections_table, &i,
229
+ indirections_table + i, 0) < 0) {
230
+ return false;
231
+ }
232
+ }
233
+ return true;
234
+}
235
+
236
+static bool ebpf_rss_set_toepliz_key(struct EBPFRSSContext *ctx,
237
+ uint8_t *toeplitz_key)
238
+{
239
+ uint32_t map_key = 0;
240
+
241
+ /* prepare toeplitz key */
242
+ uint8_t toe[VIRTIO_NET_RSS_MAX_KEY_SIZE] = {};
243
+
244
+ if (!ebpf_rss_is_loaded(ctx) || toeplitz_key == NULL) {
245
+ return false;
246
+ }
247
+ memcpy(toe, toeplitz_key, VIRTIO_NET_RSS_MAX_KEY_SIZE);
248
+ *(uint32_t *)toe = ntohl(*(uint32_t *)toe);
249
+
250
+ if (bpf_map_update_elem(ctx->map_toeplitz_key, &map_key, toe,
251
+ 0) < 0) {
252
+ return false;
253
+ }
254
+ return true;
255
+}
256
+
257
+bool ebpf_rss_set_all(struct EBPFRSSContext *ctx, struct EBPFRSSConfig *config,
258
+ uint16_t *indirections_table, uint8_t *toeplitz_key)
259
+{
260
+ if (!ebpf_rss_is_loaded(ctx) || config == NULL ||
261
+ indirections_table == NULL || toeplitz_key == NULL) {
262
+ return false;
263
+ }
264
+
265
+ if (!ebpf_rss_set_config(ctx, config)) {
266
+ return false;
267
+ }
268
+
269
+ if (!ebpf_rss_set_indirections_table(ctx, indirections_table,
270
+ config->indirections_len)) {
271
+ return false;
272
+ }
273
+
274
+ if (!ebpf_rss_set_toepliz_key(ctx, toeplitz_key)) {
275
+ return false;
276
+ }
277
+
278
+ return true;
279
+}
280
+
281
+void ebpf_rss_unload(struct EBPFRSSContext *ctx)
282
+{
283
+ if (!ebpf_rss_is_loaded(ctx)) {
284
+ return;
285
+ }
286
+
287
+ rss_bpf__destroy(ctx->obj);
288
+ ctx->obj = NULL;
289
+}
290
diff --git a/ebpf/ebpf_rss.h b/ebpf/ebpf_rss.h
291
new file mode 100644
292
index XXXXXXX..XXXXXXX
293
--- /dev/null
294
+++ b/ebpf/ebpf_rss.h
295
@@ -XXX,XX +XXX,XX @@
296
+/*
297
+ * eBPF RSS header
298
+ *
299
+ * Developed by Daynix Computing LTD (http://www.daynix.com)
300
+ *
301
+ * Authors:
302
+ * Andrew Melnychenko <andrew@daynix.com>
303
+ * Yuri Benditovich <yuri.benditovich@daynix.com>
304
+ *
305
+ * This work is licensed under the terms of the GNU GPL, version 2. See
306
+ * the COPYING file in the top-level directory.
307
+ */
308
+
309
+#ifndef QEMU_EBPF_RSS_H
310
+#define QEMU_EBPF_RSS_H
311
+
312
+struct EBPFRSSContext {
313
+ void *obj;
314
+ int program_fd;
315
+ int map_configuration;
316
+ int map_toeplitz_key;
317
+ int map_indirections_table;
318
+};
319
+
320
+struct EBPFRSSConfig {
321
+ uint8_t redirect;
322
+ uint8_t populate_hash;
323
+ uint32_t hash_types;
324
+ uint16_t indirections_len;
325
+ uint16_t default_queue;
326
+} __attribute__((packed));
327
+
328
+void ebpf_rss_init(struct EBPFRSSContext *ctx);
329
+
330
+bool ebpf_rss_is_loaded(struct EBPFRSSContext *ctx);
331
+
332
+bool ebpf_rss_load(struct EBPFRSSContext *ctx);
333
+
334
+bool ebpf_rss_set_all(struct EBPFRSSContext *ctx, struct EBPFRSSConfig *config,
335
+ uint16_t *indirections_table, uint8_t *toeplitz_key);
336
+
337
+void ebpf_rss_unload(struct EBPFRSSContext *ctx);
338
+
339
+#endif /* QEMU_EBPF_RSS_H */
340
diff --git a/ebpf/meson.build b/ebpf/meson.build
341
new file mode 100644
342
index XXXXXXX..XXXXXXX
343
--- /dev/null
344
+++ b/ebpf/meson.build
345
@@ -0,0 +1 @@
346
+common_ss.add(when: libbpf, if_true: files('ebpf_rss.c'), if_false: files('ebpf_rss-stub.c'))
347
diff --git a/ebpf/rss.bpf.skeleton.h b/ebpf/rss.bpf.skeleton.h
348
new file mode 100644
349
index XXXXXXX..XXXXXXX
350
--- /dev/null
351
+++ b/ebpf/rss.bpf.skeleton.h
352
@@ -XXX,XX +XXX,XX @@
353
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
354
+
355
+/* THIS FILE IS AUTOGENERATED! */
356
+#ifndef __RSS_BPF_SKEL_H__
357
+#define __RSS_BPF_SKEL_H__
358
+
359
+#include <stdlib.h>
360
+#include <bpf/libbpf.h>
361
+
362
+struct rss_bpf {
363
+    struct bpf_object_skeleton *skeleton;
364
+    struct bpf_object *obj;
365
+    struct {
366
+        struct bpf_map *tap_rss_map_configurations;
367
+        struct bpf_map *tap_rss_map_indirection_table;
368
+        struct bpf_map *tap_rss_map_toeplitz_key;
369
+    } maps;
370
+    struct {
371
+        struct bpf_program *tun_rss_steering_prog;
372
+    } progs;
373
+    struct {
374
+        struct bpf_link *tun_rss_steering_prog;
375
+    } links;
376
+};
377
+
378
+static void
379
+rss_bpf__destroy(struct rss_bpf *obj)
380
+{
381
+    if (!obj)
382
+        return;
383
+    if (obj->skeleton)
384
+        bpf_object__destroy_skeleton(obj->skeleton);
385
+    free(obj);
386
+}
387
+
388
+static inline int
389
+rss_bpf__create_skeleton(struct rss_bpf *obj);
390
+
391
+static inline struct rss_bpf *
392
+rss_bpf__open_opts(const struct bpf_object_open_opts *opts)
393
+{
394
+    struct rss_bpf *obj;
395
+
396
+    obj = (struct rss_bpf *)calloc(1, sizeof(*obj));
397
+    if (!obj)
398
+        return NULL;
399
+    if (rss_bpf__create_skeleton(obj))
400
+        goto err;
401
+    if (bpf_object__open_skeleton(obj->skeleton, opts))
402
+        goto err;
403
+
404
+    return obj;
405
+err:
406
+    rss_bpf__destroy(obj);
407
+    return NULL;
408
+}
409
+
410
+static inline struct rss_bpf *
411
+rss_bpf__open(void)
412
+{
413
+    return rss_bpf__open_opts(NULL);
414
+}
415
+
416
+static inline int
417
+rss_bpf__load(struct rss_bpf *obj)
418
+{
419
+    return bpf_object__load_skeleton(obj->skeleton);
420
+}
421
+
422
+static inline struct rss_bpf *
423
+rss_bpf__open_and_load(void)
424
+{
425
+    struct rss_bpf *obj;
426
+
427
+    obj = rss_bpf__open();
428
+    if (!obj)
429
+        return NULL;
430
+    if (rss_bpf__load(obj)) {
431
+        rss_bpf__destroy(obj);
432
+        return NULL;
433
+    }
434
+    return obj;
435
+}
436
+
437
+static inline int
438
+rss_bpf__attach(struct rss_bpf *obj)
439
+{
440
+    return bpf_object__attach_skeleton(obj->skeleton);
441
+}
442
+
443
+static inline void
444
+rss_bpf__detach(struct rss_bpf *obj)
445
+{
446
+    return bpf_object__detach_skeleton(obj->skeleton);
447
+}
448
+
449
+static inline int
450
+rss_bpf__create_skeleton(struct rss_bpf *obj)
451
+{
452
+    struct bpf_object_skeleton *s;
453
+
454
+    s = (struct bpf_object_skeleton *)calloc(1, sizeof(*s));
455
+    if (!s)
456
+        return -1;
457
+    obj->skeleton = s;
458
+
459
+    s->sz = sizeof(*s);
460
+    s->name = "rss_bpf";
461
+    s->obj = &obj->obj;
462
+
463
+    /* maps */
464
+    s->map_cnt = 3;
465
+    s->map_skel_sz = sizeof(*s->maps);
466
+    s->maps = (struct bpf_map_skeleton *)calloc(s->map_cnt, s->map_skel_sz);
467
+    if (!s->maps)
468
+        goto err;
469
+
470
+    s->maps[0].name = "tap_rss_map_configurations";
471
+    s->maps[0].map = &obj->maps.tap_rss_map_configurations;
472
+
473
+    s->maps[1].name = "tap_rss_map_indirection_table";
474
+    s->maps[1].map = &obj->maps.tap_rss_map_indirection_table;
475
+
476
+    s->maps[2].name = "tap_rss_map_toeplitz_key";
477
+    s->maps[2].map = &obj->maps.tap_rss_map_toeplitz_key;
478
+
479
+    /* programs */
480
+    s->prog_cnt = 1;
481
+    s->prog_skel_sz = sizeof(*s->progs);
482
+    s->progs = (struct bpf_prog_skeleton *)calloc(s->prog_cnt, s->prog_skel_sz);
483
+    if (!s->progs)
484
+        goto err;
485
+
486
+    s->progs[0].name = "tun_rss_steering_prog";
487
+    s->progs[0].prog = &obj->progs.tun_rss_steering_prog;
488
+    s->progs[0].link = &obj->links.tun_rss_steering_prog;
489
+
490
+    s->data_sz = 8088;
491
+    s->data = (void *)"\
492
+\x7f\x45\x4c\x46\x02\x01\x01\0\0\0\0\0\0\0\0\0\x01\0\xf7\0\x01\0\0\0\0\0\0\0\0\
493
+\0\0\0\0\0\0\0\0\0\0\0\x18\x1d\0\0\0\0\0\0\0\0\0\0\x40\0\0\0\0\0\x40\0\x0a\0\
494
+\x01\0\xbf\x18\0\0\0\0\0\0\xb7\x01\0\0\0\0\0\0\x63\x1a\x4c\xff\0\0\0\0\xbf\xa7\
495
+\0\0\0\0\0\0\x07\x07\0\0\x4c\xff\xff\xff\x18\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
496
+\xbf\x72\0\0\0\0\0\0\x85\0\0\0\x01\0\0\0\xbf\x06\0\0\0\0\0\0\x18\x01\0\0\0\0\0\
497
+\0\0\0\0\0\0\0\0\0\xbf\x72\0\0\0\0\0\0\x85\0\0\0\x01\0\0\0\xbf\x07\0\0\0\0\0\0\
498
+\x18\0\0\0\xff\xff\xff\xff\0\0\0\0\0\0\0\0\x15\x06\x66\x02\0\0\0\0\xbf\x79\0\0\
499
+\0\0\0\0\x15\x09\x64\x02\0\0\0\0\x71\x61\0\0\0\0\0\0\x55\x01\x01\0\0\0\0\0\x05\
500
+\0\x5d\x02\0\0\0\0\xb7\x01\0\0\0\0\0\0\x63\x1a\xc0\xff\0\0\0\0\x7b\x1a\xb8\xff\
501
+\0\0\0\0\x7b\x1a\xb0\xff\0\0\0\0\x7b\x1a\xa8\xff\0\0\0\0\x7b\x1a\xa0\xff\0\0\0\
502
+\0\x63\x1a\x98\xff\0\0\0\0\x7b\x1a\x90\xff\0\0\0\0\x7b\x1a\x88\xff\0\0\0\0\x7b\
503
+\x1a\x80\xff\0\0\0\0\x7b\x1a\x78\xff\0\0\0\0\x7b\x1a\x70\xff\0\0\0\0\x7b\x1a\
504
+\x68\xff\0\0\0\0\x7b\x1a\x60\xff\0\0\0\0\x7b\x1a\x58\xff\0\0\0\0\x7b\x1a\x50\
505
+\xff\0\0\0\0\x15\x08\x4c\x02\0\0\0\0\x6b\x1a\xd0\xff\0\0\0\0\xbf\xa3\0\0\0\0\0\
506
+\0\x07\x03\0\0\xd0\xff\xff\xff\xbf\x81\0\0\0\0\0\0\xb7\x02\0\0\x0c\0\0\0\xb7\
507
+\x04\0\0\x02\0\0\0\xb7\x05\0\0\0\0\0\0\x85\0\0\0\x44\0\0\0\x67\0\0\0\x20\0\0\0\
508
+\x77\0\0\0\x20\0\0\0\x55\0\x11\0\0\0\0\0\xb7\x02\0\0\x10\0\0\0\x69\xa1\xd0\xff\
509
+\0\0\0\0\xbf\x13\0\0\0\0\0\0\xdc\x03\0\0\x10\0\0\0\x15\x03\x02\0\0\x81\0\0\x55\
510
+\x03\x0c\0\xa8\x88\0\0\xb7\x02\0\0\x14\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\
511
+\xd0\xff\xff\xff\xbf\x81\0\0\0\0\0\0\xb7\x04\0\0\x02\0\0\0\xb7\x05\0\0\0\0\0\0\
512
+\x85\0\0\0\x44\0\0\0\x69\xa1\xd0\xff\0\0\0\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\
513
+\0\0\0\x15\0\x01\0\0\0\0\0\x05\0\x2f\x02\0\0\0\0\x15\x01\x2e\x02\0\0\0\0\x7b\
514
+\x9a\x30\xff\0\0\0\0\x15\x01\x57\0\x86\xdd\0\0\x55\x01\x3b\0\x08\0\0\0\x7b\x7a\
515
+\x20\xff\0\0\0\0\xb7\x07\0\0\x01\0\0\0\x73\x7a\x50\xff\0\0\0\0\xb7\x01\0\0\0\0\
516
+\0\0\x63\x1a\xe0\xff\0\0\0\0\x7b\x1a\xd8\xff\0\0\0\0\x7b\x1a\xd0\xff\0\0\0\0\
517
+\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\xd0\xff\xff\xff\xbf\x81\0\0\0\0\0\0\xb7\x02\0\
518
+\0\0\0\0\0\xb7\x04\0\0\x14\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\0\x67\
519
+\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\x55\0\x1a\x02\0\0\0\0\x69\xa1\xd6\xff\0\0\
520
+\0\0\x55\x01\x01\0\0\0\0\0\xb7\x07\0\0\0\0\0\0\x61\xa1\xdc\xff\0\0\0\0\x63\x1a\
521
+\x5c\xff\0\0\0\0\x61\xa1\xe0\xff\0\0\0\0\x63\x1a\x60\xff\0\0\0\0\x73\x7a\x56\
522
+\xff\0\0\0\0\x71\xa9\xd9\xff\0\0\0\0\x71\xa1\xd0\xff\0\0\0\0\x67\x01\0\0\x02\0\
523
+\0\0\x57\x01\0\0\x3c\0\0\0\x7b\x1a\x40\xff\0\0\0\0\x79\xa7\x20\xff\0\0\0\0\xbf\
524
+\x91\0\0\0\0\0\0\x57\x01\0\0\xff\0\0\0\x15\x01\x19\0\0\0\0\0\x71\xa1\x56\xff\0\
525
+\0\0\0\x55\x01\x17\0\0\0\0\0\x57\x09\0\0\xff\0\0\0\x15\x09\x7a\x01\x11\0\0\0\
526
+\x55\x09\x14\0\x06\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\x1a\x53\xff\0\0\0\0\xb7\x01\
527
+\0\0\0\0\0\0\x63\x1a\xe0\xff\0\0\0\0\x7b\x1a\xd8\xff\0\0\0\0\x7b\x1a\xd0\xff\0\
528
+\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\xd0\xff\xff\xff\xbf\x81\0\0\0\0\0\0\x79\
529
+\xa2\x40\xff\0\0\0\0\xb7\x04\0\0\x14\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\
530
+\0\0\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\x55\0\xf4\x01\0\0\0\0\x69\xa1\
531
+\xd0\xff\0\0\0\0\x6b\x1a\x58\xff\0\0\0\0\x69\xa1\xd2\xff\0\0\0\0\x6b\x1a\x5a\
532
+\xff\0\0\0\0\x71\xa1\x50\xff\0\0\0\0\x15\x01\xd4\0\0\0\0\0\x71\x62\x03\0\0\0\0\
533
+\0\x67\x02\0\0\x08\0\0\0\x71\x61\x02\0\0\0\0\0\x4f\x12\0\0\0\0\0\0\x71\x63\x04\
534
+\0\0\0\0\0\x71\x61\x05\0\0\0\0\0\x67\x01\0\0\x08\0\0\0\x4f\x31\0\0\0\0\0\0\x67\
535
+\x01\0\0\x10\0\0\0\x4f\x21\0\0\0\0\0\0\x71\xa2\x53\xff\0\0\0\0\x79\xa0\x30\xff\
536
+\0\0\0\0\x15\x02\x06\x01\0\0\0\0\xbf\x12\0\0\0\0\0\0\x57\x02\0\0\x02\0\0\0\x15\
537
+\x02\x03\x01\0\0\0\0\x61\xa1\x5c\xff\0\0\0\0\x63\x1a\xa0\xff\0\0\0\0\x61\xa1\
538
+\x60\xff\0\0\0\0\x63\x1a\xa4\xff\0\0\0\0\x69\xa1\x58\xff\0\0\0\0\x6b\x1a\xa8\
539
+\xff\0\0\0\0\x69\xa1\x5a\xff\0\0\0\0\x6b\x1a\xaa\xff\0\0\0\0\x05\0\x65\x01\0\0\
540
+\0\0\xb7\x01\0\0\x01\0\0\0\x73\x1a\x51\xff\0\0\0\0\xb7\x01\0\0\0\0\0\0\x7b\x1a\
541
+\xf0\xff\0\0\0\0\x7b\x1a\xe8\xff\0\0\0\0\x7b\x1a\xe0\xff\0\0\0\0\x7b\x1a\xd8\
542
+\xff\0\0\0\0\x7b\x1a\xd0\xff\0\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\xd0\xff\
543
+\xff\xff\xb7\x01\0\0\x28\0\0\0\x7b\x1a\x40\xff\0\0\0\0\xbf\x81\0\0\0\0\0\0\xb7\
544
+\x02\0\0\0\0\0\0\xb7\x04\0\0\x28\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\
545
+\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\x55\0\x10\x01\0\0\0\0\x79\xa1\xe0\
546
+\xff\0\0\0\0\x63\x1a\x64\xff\0\0\0\0\x77\x01\0\0\x20\0\0\0\x63\x1a\x68\xff\0\0\
547
+\0\0\x79\xa1\xd8\xff\0\0\0\0\x63\x1a\x5c\xff\0\0\0\0\x77\x01\0\0\x20\0\0\0\x63\
548
+\x1a\x60\xff\0\0\0\0\x79\xa1\xe8\xff\0\0\0\0\x63\x1a\x6c\xff\0\0\0\0\x77\x01\0\
549
+\0\x20\0\0\0\x63\x1a\x70\xff\0\0\0\0\x79\xa1\xf0\xff\0\0\0\0\x63\x1a\x74\xff\0\
550
+\0\0\0\x77\x01\0\0\x20\0\0\0\x63\x1a\x78\xff\0\0\0\0\x71\xa9\xd6\xff\0\0\0\0\
551
+\x25\x09\xff\0\x3c\0\0\0\xb7\x01\0\0\x01\0\0\0\x6f\x91\0\0\0\0\0\0\x18\x02\0\0\
552
+\x01\0\0\0\0\0\0\0\0\x18\0\x1c\x5f\x21\0\0\0\0\0\0\x55\x01\x01\0\0\0\0\0\x05\0\
553
+\xf8\0\0\0\0\0\xb7\x01\0\0\0\0\0\0\x6b\x1a\xfe\xff\0\0\0\0\xb7\x01\0\0\x28\0\0\
554
+\0\x7b\x1a\x40\xff\0\0\0\0\xbf\xa1\0\0\0\0\0\0\x07\x01\0\0\x8c\xff\xff\xff\x7b\
555
+\x1a\x18\xff\0\0\0\0\xbf\xa1\0\0\0\0\0\0\x07\x01\0\0\x7c\xff\xff\xff\x7b\x1a\
556
+\x10\xff\0\0\0\0\xb7\x01\0\0\0\0\0\0\x7b\x1a\x28\xff\0\0\0\0\x7b\x7a\x20\xff\0\
557
+\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\xfe\xff\xff\xff\xbf\x81\0\0\0\0\0\0\x79\
558
+\xa2\x40\xff\0\0\0\0\xb7\x04\0\0\x02\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\
559
+\0\0\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\x15\0\x01\0\0\0\0\0\x05\0\x90\
560
+\x01\0\0\0\0\xbf\x91\0\0\0\0\0\0\x15\x01\x23\0\x3c\0\0\0\x15\x01\x59\0\x2c\0\0\
561
+\0\x55\x01\x5a\0\x2b\0\0\0\xb7\x01\0\0\0\0\0\0\x63\x1a\xf8\xff\0\0\0\0\xbf\xa3\
562
+\0\0\0\0\0\0\x07\x03\0\0\xf8\xff\xff\xff\xbf\x81\0\0\0\0\0\0\x79\xa2\x40\xff\0\
563
+\0\0\0\xb7\x04\0\0\x04\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\0\xbf\x01\
564
+\0\0\0\0\0\0\x67\x01\0\0\x20\0\0\0\x77\x01\0\0\x20\0\0\0\x55\x01\x03\x01\0\0\0\
565
+\0\x71\xa1\xfa\xff\0\0\0\0\x55\x01\x4b\0\x02\0\0\0\x71\xa1\xf9\xff\0\0\0\0\x55\
566
+\x01\x49\0\x02\0\0\0\x71\xa1\xfb\xff\0\0\0\0\x55\x01\x47\0\x01\0\0\0\x79\xa2\
567
+\x40\xff\0\0\0\0\x07\x02\0\0\x08\0\0\0\xbf\x81\0\0\0\0\0\0\x79\xa3\x18\xff\0\0\
568
+\0\0\xb7\x04\0\0\x10\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\0\xbf\x01\0\
569
+\0\0\0\0\0\x67\x01\0\0\x20\0\0\0\x77\x01\0\0\x20\0\0\0\x55\x01\xf2\0\0\0\0\0\
570
+\xb7\x01\0\0\x01\0\0\0\x73\x1a\x55\xff\0\0\0\0\x05\0\x39\0\0\0\0\0\xb7\x01\0\0\
571
+\0\0\0\0\x6b\x1a\xf8\xff\0\0\0\0\xb7\x09\0\0\x02\0\0\0\xb7\x07\0\0\x1e\0\0\0\
572
+\x05\0\x0e\0\0\0\0\0\x79\xa2\x38\xff\0\0\0\0\x0f\x29\0\0\0\0\0\0\xbf\x92\0\0\0\
573
+\0\0\0\x07\x02\0\0\x01\0\0\0\x71\xa3\xff\xff\0\0\0\0\x67\x03\0\0\x03\0\0\0\x2d\
574
+\x23\x02\0\0\0\0\0\x79\xa7\x20\xff\0\0\0\0\x05\0\x2b\0\0\0\0\0\x07\x07\0\0\xff\
575
+\xff\xff\xff\xbf\x72\0\0\0\0\0\0\x67\x02\0\0\x20\0\0\0\x77\x02\0\0\x20\0\0\0\
576
+\x15\x02\xf9\xff\0\0\0\0\x7b\x9a\x38\xff\0\0\0\0\x79\xa1\x40\xff\0\0\0\0\x0f\
577
+\x19\0\0\0\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\xf8\xff\xff\xff\xbf\x81\0\0\0\
578
+\0\0\0\xbf\x92\0\0\0\0\0\0\xb7\x04\0\0\x02\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\
579
+\0\x44\0\0\0\xbf\x01\0\0\0\0\0\0\x67\x01\0\0\x20\0\0\0\x77\x01\0\0\x20\0\0\0\
580
+\x55\x01\x94\0\0\0\0\0\x71\xa2\xf8\xff\0\0\0\0\x55\x02\x0f\0\xc9\0\0\0\x07\x09\
581
+\0\0\x02\0\0\0\xbf\x81\0\0\0\0\0\0\xbf\x92\0\0\0\0\0\0\x79\xa3\x10\xff\0\0\0\0\
582
+\xb7\x04\0\0\x10\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\0\xbf\x01\0\0\0\
583
+\0\0\0\x67\x01\0\0\x20\0\0\0\x77\x01\0\0\x20\0\0\0\x55\x01\x87\0\0\0\0\0\xb7\
584
+\x01\0\0\x01\0\0\0\x73\x1a\x54\xff\0\0\0\0\x79\xa7\x20\xff\0\0\0\0\x05\0\x07\0\
585
+\0\0\0\0\xb7\x09\0\0\x01\0\0\0\x15\x02\xd1\xff\0\0\0\0\x71\xa9\xf9\xff\0\0\0\0\
586
+\x07\x09\0\0\x02\0\0\0\x05\0\xce\xff\0\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\x1a\x56\
587
+\xff\0\0\0\0\x71\xa1\xff\xff\0\0\0\0\x67\x01\0\0\x03\0\0\0\x79\xa2\x40\xff\0\0\
588
+\0\0\x0f\x12\0\0\0\0\0\0\x07\x02\0\0\x08\0\0\0\x7b\x2a\x40\xff\0\0\0\0\x71\xa9\
589
+\xfe\xff\0\0\0\0\x25\x09\x0e\0\x3c\0\0\0\xb7\x01\0\0\x01\0\0\0\x6f\x91\0\0\0\0\
590
+\0\0\x18\x02\0\0\x01\0\0\0\0\0\0\0\0\x18\0\x1c\x5f\x21\0\0\0\0\0\0\x55\x01\x01\
591
+\0\0\0\0\0\x05\0\x07\0\0\0\0\0\x79\xa1\x28\xff\0\0\0\0\x07\x01\0\0\x01\0\0\0\
592
+\x7b\x1a\x28\xff\0\0\0\0\x67\x01\0\0\x20\0\0\0\x77\x01\0\0\x20\0\0\0\x55\x01\
593
+\x82\xff\x0b\0\0\0\x05\0\x10\xff\0\0\0\0\x15\x09\xf8\xff\x87\0\0\0\x05\0\xfd\
594
+\xff\0\0\0\0\x71\xa1\x51\xff\0\0\0\0\x79\xa0\x30\xff\0\0\0\0\x15\x01\x17\x01\0\
595
+\0\0\0\x71\x62\x03\0\0\0\0\0\x67\x02\0\0\x08\0\0\0\x71\x61\x02\0\0\0\0\0\x4f\
596
+\x12\0\0\0\0\0\0\x71\x63\x04\0\0\0\0\0\x71\x61\x05\0\0\0\0\0\x67\x01\0\0\x08\0\
597
+\0\0\x4f\x31\0\0\0\0\0\0\x67\x01\0\0\x10\0\0\0\x4f\x21\0\0\0\0\0\0\x71\xa2\x53\
598
+\xff\0\0\0\0\x15\x02\x3d\0\0\0\0\0\xbf\x12\0\0\0\0\0\0\x57\x02\0\0\x10\0\0\0\
599
+\x15\x02\x3a\0\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\x5c\xff\xff\xff\x71\xa4\
600
+\x54\xff\0\0\0\0\xbf\x23\0\0\0\0\0\0\x15\x04\x02\0\0\0\0\0\xbf\xa3\0\0\0\0\0\0\
601
+\x07\x03\0\0\x7c\xff\xff\xff\x67\x01\0\0\x38\0\0\0\xc7\x01\0\0\x38\0\0\0\x65\
602
+\x01\x01\0\xff\xff\xff\xff\xbf\x32\0\0\0\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\
603
+\x6c\xff\xff\xff\x71\xa5\x55\xff\0\0\0\0\xbf\x34\0\0\0\0\0\0\x15\x05\x02\0\0\0\
604
+\0\0\xbf\xa4\0\0\0\0\0\0\x07\x04\0\0\x8c\xff\xff\xff\x65\x01\x01\0\xff\xff\xff\
605
+\xff\xbf\x43\0\0\0\0\0\0\x61\x21\x04\0\0\0\0\0\x67\x01\0\0\x20\0\0\0\x61\x24\0\
606
+\0\0\0\0\0\x4f\x41\0\0\0\0\0\0\x7b\x1a\xa0\xff\0\0\0\0\x61\x21\x08\0\0\0\0\0\
607
+\x61\x22\x0c\0\0\0\0\0\x67\x02\0\0\x20\0\0\0\x4f\x12\0\0\0\0\0\0\x7b\x2a\xa8\
608
+\xff\0\0\0\0\x61\x31\0\0\0\0\0\0\x61\x32\x04\0\0\0\0\0\x61\x34\x08\0\0\0\0\0\
609
+\x61\x33\x0c\0\0\0\0\0\x69\xa5\x5a\xff\0\0\0\0\x6b\x5a\xc2\xff\0\0\0\0\x69\xa5\
610
+\x58\xff\0\0\0\0\x6b\x5a\xc0\xff\0\0\0\0\x67\x03\0\0\x20\0\0\0\x4f\x43\0\0\0\0\
611
+\0\0\x7b\x3a\xb8\xff\0\0\0\0\x67\x02\0\0\x20\0\0\0\x4f\x12\0\0\0\0\0\0\x7b\x2a\
612
+\xb0\xff\0\0\0\0\x05\0\x6b\0\0\0\0\0\x71\xa2\x52\xff\0\0\0\0\x15\x02\x04\0\0\0\
613
+\0\0\xbf\x12\0\0\0\0\0\0\x57\x02\0\0\x04\0\0\0\x15\x02\x01\0\0\0\0\0\x05\0\xf7\
614
+\xfe\0\0\0\0\x57\x01\0\0\x01\0\0\0\x15\x01\xd3\0\0\0\0\0\x61\xa1\x5c\xff\0\0\0\
615
+\0\x63\x1a\xa0\xff\0\0\0\0\x61\xa1\x60\xff\0\0\0\0\x63\x1a\xa4\xff\0\0\0\0\x05\
616
+\0\x5e\0\0\0\0\0\x71\xa2\x52\xff\0\0\0\0\x15\x02\x1e\0\0\0\0\0\xbf\x12\0\0\0\0\
617
+\0\0\x57\x02\0\0\x20\0\0\0\x15\x02\x1b\0\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\x02\0\
618
+\0\x5c\xff\xff\xff\x71\xa4\x54\xff\0\0\0\0\xbf\x23\0\0\0\0\0\0\x15\x04\x02\0\0\
619
+\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\x7c\xff\xff\xff\x57\x01\0\0\0\x01\0\0\
620
+\x15\x01\x01\0\0\0\0\0\xbf\x32\0\0\0\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\x6c\
621
+\xff\xff\xff\x71\xa5\x55\xff\0\0\0\0\xbf\x34\0\0\0\0\0\0\x15\x05\x02\0\0\0\0\0\
622
+\xbf\xa4\0\0\0\0\0\0\x07\x04\0\0\x8c\xff\xff\xff\x15\x01\xc3\xff\0\0\0\0\x05\0\
623
+\xc1\xff\0\0\0\0\xb7\x09\0\0\x3c\0\0\0\x79\xa7\x20\xff\0\0\0\0\x67\0\0\0\x20\0\
624
+\0\0\x77\0\0\0\x20\0\0\0\x15\0\xa5\xfe\0\0\0\0\x05\0\xb0\0\0\0\0\0\x15\x09\x07\
625
+\xff\x87\0\0\0\x05\0\xa2\xfe\0\0\0\0\xbf\x12\0\0\0\0\0\0\x57\x02\0\0\x08\0\0\0\
626
+\x15\x02\xab\0\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\x5c\xff\xff\xff\x71\xa4\
627
+\x54\xff\0\0\0\0\xbf\x23\0\0\0\0\0\0\x15\x04\x02\0\0\0\0\0\xbf\xa3\0\0\0\0\0\0\
628
+\x07\x03\0\0\x7c\xff\xff\xff\x57\x01\0\0\x40\0\0\0\x15\x01\x01\0\0\0\0\0\xbf\
629
+\x32\0\0\0\0\0\0\x61\x23\x04\0\0\0\0\0\x67\x03\0\0\x20\0\0\0\x61\x24\0\0\0\0\0\
630
+\0\x4f\x43\0\0\0\0\0\0\x7b\x3a\xa0\xff\0\0\0\0\x61\x23\x08\0\0\0\0\0\x61\x22\
631
+\x0c\0\0\0\0\0\x67\x02\0\0\x20\0\0\0\x4f\x32\0\0\0\0\0\0\x7b\x2a\xa8\xff\0\0\0\
632
+\0\x15\x01\x1c\0\0\0\0\0\x71\xa1\x55\xff\0\0\0\0\x15\x01\x1a\0\0\0\0\0\x61\xa1\
633
+\x98\xff\0\0\0\0\x67\x01\0\0\x20\0\0\0\x61\xa2\x94\xff\0\0\0\0\x4f\x21\0\0\0\0\
634
+\0\0\x7b\x1a\xb8\xff\0\0\0\0\x61\xa1\x90\xff\0\0\0\0\x67\x01\0\0\x20\0\0\0\x61\
635
+\xa2\x8c\xff\0\0\0\0\x05\0\x19\0\0\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\x1a\x52\xff\
636
+\0\0\0\0\xb7\x01\0\0\0\0\0\0\x7b\x1a\xd0\xff\0\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\
637
+\x03\0\0\xd0\xff\xff\xff\xbf\x81\0\0\0\0\0\0\x79\xa2\x40\xff\0\0\0\0\xb7\x04\0\
638
+\0\x08\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\0\x67\0\0\0\x20\0\0\0\x77\
639
+\0\0\0\x20\0\0\0\x55\0\x7d\0\0\0\0\0\x05\0\x88\xfe\0\0\0\0\xb7\x09\0\0\x2b\0\0\
640
+\0\x05\0\xc6\xff\0\0\0\0\x61\xa1\x78\xff\0\0\0\0\x67\x01\0\0\x20\0\0\0\x61\xa2\
641
+\x74\xff\0\0\0\0\x4f\x21\0\0\0\0\0\0\x7b\x1a\xb8\xff\0\0\0\0\x61\xa1\x70\xff\0\
642
+\0\0\0\x67\x01\0\0\x20\0\0\0\x61\xa2\x6c\xff\0\0\0\0\x4f\x21\0\0\0\0\0\0\x7b\
643
+\x1a\xb0\xff\0\0\0\0\xb7\x01\0\0\0\0\0\0\x07\x07\0\0\x04\0\0\0\x61\x03\0\0\0\0\
644
+\0\0\xb7\x05\0\0\0\0\0\0\x05\0\x4e\0\0\0\0\0\xaf\x52\0\0\0\0\0\0\xbf\x75\0\0\0\
645
+\0\0\0\x0f\x15\0\0\0\0\0\0\x71\x55\0\0\0\0\0\0\x67\x03\0\0\x01\0\0\0\xbf\x50\0\
646
+\0\0\0\0\0\x77\0\0\0\x07\0\0\0\x4f\x03\0\0\0\0\0\0\xbf\x40\0\0\0\0\0\0\x67\0\0\
647
+\0\x39\0\0\0\xc7\0\0\0\x3f\0\0\0\x5f\x30\0\0\0\0\0\0\xaf\x02\0\0\0\0\0\0\xbf\
648
+\x50\0\0\0\0\0\0\x77\0\0\0\x06\0\0\0\x57\0\0\0\x01\0\0\0\x67\x03\0\0\x01\0\0\0\
649
+\x4f\x03\0\0\0\0\0\0\xbf\x40\0\0\0\0\0\0\x67\0\0\0\x3a\0\0\0\xc7\0\0\0\x3f\0\0\
650
+\0\x5f\x30\0\0\0\0\0\0\xaf\x02\0\0\0\0\0\0\x67\x03\0\0\x01\0\0\0\xbf\x50\0\0\0\
651
+\0\0\0\x77\0\0\0\x05\0\0\0\x57\0\0\0\x01\0\0\0\x4f\x03\0\0\0\0\0\0\xbf\x40\0\0\
652
+\0\0\0\0\x67\0\0\0\x3b\0\0\0\xc7\0\0\0\x3f\0\0\0\x5f\x30\0\0\0\0\0\0\xaf\x02\0\
653
+\0\0\0\0\0\x67\x03\0\0\x01\0\0\0\xbf\x50\0\0\0\0\0\0\x77\0\0\0\x04\0\0\0\x57\0\
654
+\0\0\x01\0\0\0\x4f\x03\0\0\0\0\0\0\xbf\x40\0\0\0\0\0\0\x67\0\0\0\x3c\0\0\0\xc7\
655
+\0\0\0\x3f\0\0\0\x5f\x30\0\0\0\0\0\0\xaf\x02\0\0\0\0\0\0\xbf\x50\0\0\0\0\0\0\
656
+\x77\0\0\0\x03\0\0\0\x57\0\0\0\x01\0\0\0\x67\x03\0\0\x01\0\0\0\x4f\x03\0\0\0\0\
657
+\0\0\xbf\x40\0\0\0\0\0\0\x67\0\0\0\x3d\0\0\0\xc7\0\0\0\x3f\0\0\0\x5f\x30\0\0\0\
658
+\0\0\0\xaf\x02\0\0\0\0\0\0\xbf\x50\0\0\0\0\0\0\x77\0\0\0\x02\0\0\0\x57\0\0\0\
659
+\x01\0\0\0\x67\x03\0\0\x01\0\0\0\x4f\x03\0\0\0\0\0\0\xbf\x40\0\0\0\0\0\0\x67\0\
660
+\0\0\x3e\0\0\0\xc7\0\0\0\x3f\0\0\0\x5f\x30\0\0\0\0\0\0\xaf\x02\0\0\0\0\0\0\xbf\
661
+\x50\0\0\0\0\0\0\x77\0\0\0\x01\0\0\0\x57\0\0\0\x01\0\0\0\x67\x03\0\0\x01\0\0\0\
662
+\x4f\x03\0\0\0\0\0\0\x57\x04\0\0\x01\0\0\0\x87\x04\0\0\0\0\0\0\x5f\x34\0\0\0\0\
663
+\0\0\xaf\x42\0\0\0\0\0\0\x57\x05\0\0\x01\0\0\0\x67\x03\0\0\x01\0\0\0\x4f\x53\0\
664
+\0\0\0\0\0\x07\x01\0\0\x01\0\0\0\xbf\x25\0\0\0\0\0\0\x15\x01\x0b\0\x24\0\0\0\
665
+\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\xa0\xff\xff\xff\x0f\x12\0\0\0\0\0\0\x71\x24\0\
666
+\0\0\0\0\0\xbf\x40\0\0\0\0\0\0\x67\0\0\0\x38\0\0\0\xc7\0\0\0\x38\0\0\0\xb7\x02\
667
+\0\0\0\0\0\0\x65\0\xa9\xff\xff\xff\xff\xff\xbf\x32\0\0\0\0\0\0\x05\0\xa7\xff\0\
668
+\0\0\0\xbf\x21\0\0\0\0\0\0\x67\x01\0\0\x20\0\0\0\x77\x01\0\0\x20\0\0\0\x15\x01\
669
+\x0e\0\0\0\0\0\x71\x63\x06\0\0\0\0\0\x71\x64\x07\0\0\0\0\0\x67\x04\0\0\x08\0\0\
670
+\0\x4f\x34\0\0\0\0\0\0\x3f\x41\0\0\0\0\0\0\x2f\x41\0\0\0\0\0\0\x1f\x12\0\0\0\0\
671
+\0\0\x63\x2a\x50\xff\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\x50\xff\xff\xff\
672
+\x18\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x85\0\0\0\x01\0\0\0\x55\0\x05\0\0\0\0\0\
673
+\x71\x61\x08\0\0\0\0\0\x71\x60\x09\0\0\0\0\0\x67\0\0\0\x08\0\0\0\x4f\x10\0\0\0\
674
+\0\0\0\x95\0\0\0\0\0\0\0\x69\0\0\0\0\0\0\0\x05\0\xfd\xff\0\0\0\0\x02\0\0\0\x04\
675
+\0\0\0\x0a\0\0\0\x01\0\0\0\0\0\0\0\x02\0\0\0\x04\0\0\0\x28\0\0\0\x01\0\0\0\0\0\
676
+\0\0\x02\0\0\0\x04\0\0\0\x02\0\0\0\x80\0\0\0\0\0\0\0\x47\x50\x4c\x20\x76\x32\0\
677
+\0\0\0\0\0\x10\0\0\0\0\0\0\0\x01\x7a\x52\0\x08\x7c\x0b\x01\x0c\0\0\0\x18\0\0\0\
678
+\x18\0\0\0\0\0\0\0\0\0\0\0\xd8\x13\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
679
+\0\0\0\0\0\0\0\0\0\0\0\0\xa0\0\0\0\x04\0\xf1\xff\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
680
+\0\x60\x02\0\0\0\0\x03\0\x20\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x3f\x02\0\0\0\0\
681
+\x03\0\xd0\x0f\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xed\x01\0\0\0\0\x03\0\x10\x10\0\0\0\
682
+\0\0\0\0\0\0\0\0\0\0\0\xd4\x01\0\0\0\0\x03\0\x20\x10\0\0\0\0\0\0\0\0\0\0\0\0\0\
683
+\0\xa3\x01\0\0\0\0\x03\0\xb8\x12\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x63\x01\0\0\0\0\
684
+\x03\0\x48\x10\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x2a\x01\0\0\0\0\x03\0\x10\x13\0\0\0\
685
+\0\0\0\0\0\0\0\0\0\0\0\xe1\0\0\0\0\0\x03\0\xa0\x13\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
686
+\x2e\x02\0\0\0\0\x03\0\x28\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x68\x02\0\0\0\0\x03\
687
+\0\xc0\x13\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x36\x02\0\0\0\0\x03\0\xc8\x13\0\0\0\0\0\
688
+\0\0\0\0\0\0\0\0\0\x22\x01\0\0\0\0\x03\0\xe8\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
689
+\x02\x01\0\0\0\0\x03\0\x40\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xd9\0\0\0\0\0\x03\0\
690
+\xf8\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x26\x02\0\0\0\0\x03\0\x20\x0e\0\0\0\0\0\0\
691
+\0\0\0\0\0\0\0\0\xcc\x01\0\0\0\0\x03\0\x60\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x9b\
692
+\x01\0\0\0\0\x03\0\xc8\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x5b\x01\0\0\0\0\x03\0\
693
+\x20\x07\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x7c\x01\0\0\0\0\x03\0\x48\x08\0\0\0\0\0\0\
694
+\0\0\0\0\0\0\0\0\x53\x01\0\0\0\0\x03\0\xb8\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x1a\
695
+\x01\0\0\0\0\x03\0\xe0\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x84\x01\0\0\0\0\x03\0\
696
+\xb8\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x1e\x02\0\0\0\0\x03\0\xd8\x09\0\0\0\0\0\0\0\
697
+\0\0\0\0\0\0\0\xc4\x01\0\0\0\0\x03\0\x70\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x93\
698
+\x01\0\0\0\0\x03\0\xa8\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x74\x01\0\0\0\0\x03\0\
699
+\xf0\x0d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x4b\x01\0\0\0\0\x03\0\0\x0a\0\0\0\0\0\0\0\
700
+\0\0\0\0\0\0\0\x12\x01\0\0\0\0\x03\0\x10\x0a\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xfa\0\
701
+\0\0\0\0\x03\0\xc0\x0a\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x58\x02\0\0\0\0\x03\0\x88\
702
+\x0a\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x02\0\0\0\0\x03\0\xb8\x0a\0\0\0\0\0\0\0\0\
703
+\0\0\0\0\0\0\xe5\x01\0\0\0\0\x03\0\xc0\x0f\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xbc\x01\
704
+\0\0\0\0\x03\0\0\x0e\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x8b\x01\0\0\0\0\x03\0\x18\x0e\
705
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xd1\0\0\0\0\0\x03\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\
706
+\0\0\x50\x02\0\0\0\0\x03\0\x20\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0e\x02\0\0\0\0\
707
+\x03\0\x48\x0f\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x6c\x01\0\0\0\0\x03\0\xb0\x04\0\0\0\
708
+\0\0\0\0\0\0\0\0\0\0\0\x43\x01\0\0\0\0\x03\0\xc8\x0c\0\0\0\0\0\0\0\0\0\0\0\0\0\
709
+\0\xc9\0\0\0\0\0\x03\0\xf8\x0c\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x06\x02\0\0\0\0\x03\
710
+\0\xd0\x0a\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x3b\x01\0\0\0\0\x03\0\x98\x0b\0\0\0\0\0\
711
+\0\0\0\0\0\0\0\0\0\xf2\0\0\0\0\0\x03\0\xb8\x0b\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x48\
712
+\x02\0\0\0\0\x03\0\xf0\x0b\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xfe\x01\0\0\0\0\x03\0\
713
+\xf8\x0b\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xdd\x01\0\0\0\0\x03\0\0\x0c\0\0\0\0\0\0\0\
714
+\0\0\0\0\0\0\0\xb4\x01\0\0\0\0\x03\0\x30\x0d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0a\
715
+\x01\0\0\0\0\x03\0\x90\x0d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xc1\0\0\0\0\0\x03\0\xa8\
716
+\x0d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xba\0\0\0\0\0\x03\0\xd0\x01\0\0\0\0\0\0\0\0\0\
717
+\0\0\0\0\0\xf6\x01\0\0\0\0\x03\0\xe0\x0d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xac\x01\0\
718
+\0\0\0\x03\0\x30\x0e\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x33\x01\0\0\0\0\x03\0\x80\x0e\
719
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xea\0\0\0\0\0\x03\0\x98\x0e\0\0\0\0\0\0\0\0\0\0\0\
720
+\0\0\0\0\0\0\0\x03\0\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x6b\0\0\0\x11\0\x06\
721
+\0\0\0\0\0\0\0\0\0\x07\0\0\0\0\0\0\0\x25\0\0\0\x11\0\x05\0\0\0\0\0\0\0\0\0\x14\
722
+\0\0\0\0\0\0\0\x82\0\0\0\x11\0\x05\0\x28\0\0\0\0\0\0\0\x14\0\0\0\0\0\0\0\x01\0\
723
+\0\0\x11\0\x05\0\x14\0\0\0\0\0\0\0\x14\0\0\0\0\0\0\0\x40\0\0\0\x12\0\x03\0\0\0\
724
+\0\0\0\0\0\0\xd8\x13\0\0\0\0\0\0\x28\0\0\0\0\0\0\0\x01\0\0\0\x3a\0\0\0\x50\0\0\
725
+\0\0\0\0\0\x01\0\0\0\x3c\0\0\0\x80\x13\0\0\0\0\0\0\x01\0\0\0\x3b\0\0\0\x1c\0\0\
726
+\0\0\0\0\0\x01\0\0\0\x38\0\0\0\0\x74\x61\x70\x5f\x72\x73\x73\x5f\x6d\x61\x70\
727
+\x5f\x74\x6f\x65\x70\x6c\x69\x74\x7a\x5f\x6b\x65\x79\0\x2e\x74\x65\x78\x74\0\
728
+\x6d\x61\x70\x73\0\x74\x61\x70\x5f\x72\x73\x73\x5f\x6d\x61\x70\x5f\x63\x6f\x6e\
729
+\x66\x69\x67\x75\x72\x61\x74\x69\x6f\x6e\x73\0\x74\x75\x6e\x5f\x72\x73\x73\x5f\
730
+\x73\x74\x65\x65\x72\x69\x6e\x67\x5f\x70\x72\x6f\x67\0\x2e\x72\x65\x6c\x74\x75\
731
+\x6e\x5f\x72\x73\x73\x5f\x73\x74\x65\x65\x72\x69\x6e\x67\0\x5f\x6c\x69\x63\x65\
732
+\x6e\x73\x65\0\x2e\x72\x65\x6c\x2e\x65\x68\x5f\x66\x72\x61\x6d\x65\0\x74\x61\
733
+\x70\x5f\x72\x73\x73\x5f\x6d\x61\x70\x5f\x69\x6e\x64\x69\x72\x65\x63\x74\x69\
734
+\x6f\x6e\x5f\x74\x61\x62\x6c\x65\0\x72\x73\x73\x2e\x62\x70\x66\x2e\x63\0\x2e\
735
+\x73\x74\x72\x74\x61\x62\0\x2e\x73\x79\x6d\x74\x61\x62\0\x4c\x42\x42\x30\x5f\
736
+\x39\0\x4c\x42\x42\x30\x5f\x38\x39\0\x4c\x42\x42\x30\x5f\x36\x39\0\x4c\x42\x42\
737
+\x30\x5f\x35\x39\0\x4c\x42\x42\x30\x5f\x31\x39\0\x4c\x42\x42\x30\x5f\x31\x30\
738
+\x39\0\x4c\x42\x42\x30\x5f\x39\x38\0\x4c\x42\x42\x30\x5f\x37\x38\0\x4c\x42\x42\
739
+\x30\x5f\x34\x38\0\x4c\x42\x42\x30\x5f\x31\x38\0\x4c\x42\x42\x30\x5f\x38\x37\0\
740
+\x4c\x42\x42\x30\x5f\x34\x37\0\x4c\x42\x42\x30\x5f\x33\x37\0\x4c\x42\x42\x30\
741
+\x5f\x31\x37\0\x4c\x42\x42\x30\x5f\x31\x30\x37\0\x4c\x42\x42\x30\x5f\x39\x36\0\
742
+\x4c\x42\x42\x30\x5f\x37\x36\0\x4c\x42\x42\x30\x5f\x36\x36\0\x4c\x42\x42\x30\
743
+\x5f\x34\x36\0\x4c\x42\x42\x30\x5f\x33\x36\0\x4c\x42\x42\x30\x5f\x32\x36\0\x4c\
744
+\x42\x42\x30\x5f\x31\x30\x36\0\x4c\x42\x42\x30\x5f\x36\x35\0\x4c\x42\x42\x30\
745
+\x5f\x34\x35\0\x4c\x42\x42\x30\x5f\x33\x35\0\x4c\x42\x42\x30\x5f\x34\0\x4c\x42\
746
+\x42\x30\x5f\x35\x34\0\x4c\x42\x42\x30\x5f\x34\x34\0\x4c\x42\x42\x30\x5f\x32\
747
+\x34\0\x4c\x42\x42\x30\x5f\x31\x30\x34\0\x4c\x42\x42\x30\x5f\x39\x33\0\x4c\x42\
748
+\x42\x30\x5f\x38\x33\0\x4c\x42\x42\x30\x5f\x35\x33\0\x4c\x42\x42\x30\x5f\x34\
749
+\x33\0\x4c\x42\x42\x30\x5f\x32\x33\0\x4c\x42\x42\x30\x5f\x31\x30\x33\0\x4c\x42\
750
+\x42\x30\x5f\x38\x32\0\x4c\x42\x42\x30\x5f\x35\x32\0\x4c\x42\x42\x30\x5f\x31\
751
+\x30\x32\0\x4c\x42\x42\x30\x5f\x39\x31\0\x4c\x42\x42\x30\x5f\x38\x31\0\x4c\x42\
752
+\x42\x30\x5f\x37\x31\0\x4c\x42\x42\x30\x5f\x36\x31\0\x4c\x42\x42\x30\x5f\x35\
753
+\x31\0\x4c\x42\x42\x30\x5f\x34\x31\0\x4c\x42\x42\x30\x5f\x32\x31\0\x4c\x42\x42\
754
+\x30\x5f\x31\x31\0\x4c\x42\x42\x30\x5f\x31\x31\x31\0\x4c\x42\x42\x30\x5f\x31\
755
+\x30\x31\0\x4c\x42\x42\x30\x5f\x38\x30\0\x4c\x42\x42\x30\x5f\x36\x30\0\x4c\x42\
756
+\x42\x30\x5f\x35\x30\0\x4c\x42\x42\x30\x5f\x31\x30\0\x4c\x42\x42\x30\x5f\x31\
757
+\x31\x30\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
758
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xaa\
759
+\0\0\0\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xa0\x1a\0\0\0\0\0\0\x71\x02\0\
760
+\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x1a\0\0\0\x01\0\0\
761
+\0\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
762
+\0\0\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x5a\0\0\0\x01\0\0\0\x06\0\0\0\0\0\0\
763
+\0\0\0\0\0\0\0\0\0\x40\0\0\0\0\0\0\0\xd8\x13\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x08\0\
764
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x56\0\0\0\x09\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
765
+\0\x60\x1a\0\0\0\0\0\0\x30\0\0\0\0\0\0\0\x09\0\0\0\x03\0\0\0\x08\0\0\0\0\0\0\0\
766
+\x10\0\0\0\0\0\0\0\x20\0\0\0\x01\0\0\0\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x18\
767
+\x14\0\0\0\0\0\0\x3c\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\
768
+\0\0\0\x6c\0\0\0\x01\0\0\0\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x54\x14\0\0\0\0\0\
769
+\0\x07\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x78\0\0\
770
+\0\x01\0\0\0\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x60\x14\0\0\0\0\0\0\x30\0\0\0\0\
771
+\0\0\0\0\0\0\0\0\0\0\0\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x74\0\0\0\x09\0\0\0\0\
772
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x90\x1a\0\0\0\0\0\0\x10\0\0\0\0\0\0\0\x09\0\0\0\
773
+\x07\0\0\0\x08\0\0\0\0\0\0\0\x10\0\0\0\0\0\0\0\xb2\0\0\0\x02\0\0\0\0\0\0\0\0\0\
774
+\0\0\0\0\0\0\0\0\0\0\x90\x14\0\0\0\0\0\0\xd0\x05\0\0\0\0\0\0\x01\0\0\0\x39\0\0\
775
+\0\x08\0\0\0\0\0\0\0\x18\0\0\0\0\0\0\0";
776
+
777
+    return 0;
778
+err:
779
+    bpf_object__destroy_skeleton(s);
780
+    return -1;
781
+}
782
+
783
+#endif /* __RSS_BPF_SKEL_H__ */
784
diff --git a/ebpf/trace-events b/ebpf/trace-events
785
new file mode 100644
786
index XXXXXXX..XXXXXXX
787
--- /dev/null
788
+++ b/ebpf/trace-events
789
@@ -XXX,XX +XXX,XX @@
790
+# See docs/devel/tracing.txt for syntax documentation.
791
+
792
+# ebpf-rss.c
793
+ebpf_error(const char *s1, const char *s2) "error in %s: %s"
794
diff --git a/ebpf/trace.h b/ebpf/trace.h
795
new file mode 100644
796
index XXXXXXX..XXXXXXX
797
--- /dev/null
798
+++ b/ebpf/trace.h
799
@@ -0,0 +1 @@
800
+#include "trace/trace-ebpf.h"
801
diff --git a/meson.build b/meson.build
12
index XXXXXXX..XXXXXXX 100644
802
index XXXXXXX..XXXXXXX 100644
13
--- a/net/colo-compare.c
803
--- a/meson.build
14
+++ b/net/colo-compare.c
804
+++ b/meson.build
15
@@ -XXX,XX +XXX,XX @@ enum {
805
@@ -XXX,XX +XXX,XX @@ if not get_option('fuse_lseek').disabled()
16
SECONDARY_IN,
806
endif
17
};
807
endif
18
808
19
-static void colo_compare_inconsistency_notify(void)
809
+# libbpf
20
-{
810
+libbpf = dependency('libbpf', required: get_option('bpf'), method: 'pkg-config')
21
- notifier_list_notify(&colo_compare_notifiers,
811
+if libbpf.found() and not cc.links('''
22
- migrate_get_current());
812
+ #include <bpf/libbpf.h>
23
-}
813
+ int main(void)
24
814
+ {
25
static int compare_chr_send(CompareState *s,
815
+ bpf_object__destroy_skeleton(NULL);
26
const uint8_t *buf,
816
+ return 0;
27
@@ -XXX,XX +XXX,XX @@ static int compare_chr_send(CompareState *s,
817
+ }''', dependencies: libbpf)
28
uint32_t vnet_hdr_len,
818
+ libbpf = not_found
29
bool notify_remote_frame);
819
+ if get_option('bpf').enabled()
30
820
+ error('libbpf skeleton test failed')
31
+static void notify_remote_frame(CompareState *s)
821
+ else
32
+{
822
+ warning('libbpf skeleton test failed, disabling')
33
+ char msg[] = "DO_CHECKPOINT";
823
+ endif
34
+ int ret = 0;
824
+endif
35
+
825
+
36
+ ret = compare_chr_send(s, (uint8_t *)msg, strlen(msg), 0, true);
826
if get_option('cfi')
37
+ if (ret < 0) {
827
cfi_flags=[]
38
+ error_report("Notify Xen COLO-frame failed");
828
# Check for dependency on LTO
39
+ }
829
@@ -XXX,XX +XXX,XX @@ endif
40
+}
830
config_host_data.set('CONFIG_GTK', gtk.found())
41
+
831
config_host_data.set('CONFIG_LIBATTR', have_old_libattr)
42
+static void colo_compare_inconsistency_notify(CompareState *s)
832
config_host_data.set('CONFIG_LIBCAP_NG', libcap_ng.found())
43
+{
833
+config_host_data.set('CONFIG_EBPF', libbpf.found())
44
+ if (s->notify_dev) {
834
config_host_data.set('CONFIG_LIBISCSI', libiscsi.found())
45
+ notify_remote_frame(s);
835
config_host_data.set('CONFIG_LIBNFS', libnfs.found())
46
+ } else {
836
config_host_data.set('CONFIG_RBD', rbd.found())
47
+ notifier_list_notify(&colo_compare_notifiers,
837
@@ -XXX,XX +XXX,XX @@ if have_system
48
+ migrate_get_current());
838
'backends',
49
+ }
839
'backends/tpm',
50
+}
840
'chardev',
51
+
841
+ 'ebpf',
52
static gint seq_sorter(Packet *a, Packet *b, gpointer data)
842
'hw/9pfs',
53
{
843
'hw/acpi',
54
struct tcp_hdr *atcp, *btcp;
844
'hw/adc',
55
@@ -XXX,XX +XXX,XX @@ sec:
845
@@ -XXX,XX +XXX,XX @@ subdir('accel')
56
qemu_hexdump((char *)spkt->data, stderr,
846
subdir('plugins')
57
"colo-compare spkt", spkt->size);
847
subdir('bsd-user')
58
848
subdir('linux-user')
59
- colo_compare_inconsistency_notify();
849
+subdir('ebpf')
60
+ colo_compare_inconsistency_notify(s);
850
+
61
}
851
+common_ss.add(libbpf)
62
}
852
63
853
bsd_user_ss.add(files('gdbstub.c'))
64
@@ -XXX,XX +XXX,XX @@ void colo_compare_unregister_notifier(Notifier *notify)
854
specific_ss.add_all(when: 'CONFIG_BSD_USER', if_true: bsd_user_ss)
65
}
855
@@ -XXX,XX +XXX,XX @@ summary_info += {'RDMA support': config_host.has_key('CONFIG_RDMA')}
66
856
summary_info += {'PVRDMA support': config_host.has_key('CONFIG_PVRDMA')}
67
static int colo_old_packet_check_one_conn(Connection *conn,
857
summary_info += {'fdt support': fdt_opt == 'disabled' ? false : fdt_opt}
68
- void *user_data)
858
summary_info += {'libcap-ng support': libcap_ng.found()}
69
+ CompareState *s)
859
+summary_info += {'bpf support': libbpf.found()}
70
{
860
# TODO: add back protocol and server version
71
GList *result = NULL;
861
summary_info += {'spice support': config_host.has_key('CONFIG_SPICE')}
72
int64_t check_time = REGULAR_PACKET_CHECK_MS;
862
summary_info += {'rbd support': rbd.found()}
73
@@ -XXX,XX +XXX,XX @@ static int colo_old_packet_check_one_conn(Connection *conn,
863
diff --git a/meson_options.txt b/meson_options.txt
74
864
index XXXXXXX..XXXXXXX 100644
75
if (result) {
865
--- a/meson_options.txt
76
/* Do checkpoint will flush old packet */
866
+++ b/meson_options.txt
77
- colo_compare_inconsistency_notify();
867
@@ -XXX,XX +XXX,XX @@ option('bzip2', type : 'feature', value : 'auto',
78
+ colo_compare_inconsistency_notify(s);
868
description: 'bzip2 support for DMG images')
79
return 0;
869
option('cap_ng', type : 'feature', value : 'auto',
80
}
870
description: 'cap_ng support')
81
871
+option('bpf', type : 'feature', value : 'auto',
82
@@ -XXX,XX +XXX,XX @@ static void colo_old_packet_check(void *opaque)
872
+ description: 'eBPF support')
83
* If we find one old packet, stop finding job and notify
873
option('cocoa', type : 'feature', value : 'auto',
84
* COLO frame do checkpoint.
874
description: 'Cocoa user interface (macOS only)')
85
*/
875
option('curl', type : 'feature', value : 'auto',
86
- g_queue_find_custom(&s->conn_list, NULL,
87
+ g_queue_find_custom(&s->conn_list, s,
88
(GCompareFunc)colo_old_packet_check_one_conn);
89
}
90
91
@@ -XXX,XX +XXX,XX @@ static void colo_compare_packet(CompareState *s, Connection *conn,
92
*/
93
trace_colo_compare_main("packet different");
94
g_queue_push_head(&conn->primary_list, pkt);
95
- colo_compare_inconsistency_notify();
96
+
97
+ colo_compare_inconsistency_notify(s);
98
break;
99
}
100
}
101
@@ -XXX,XX +XXX,XX @@ static void compare_sec_rs_finalize(SocketReadState *sec_rs)
102
103
static void compare_notify_rs_finalize(SocketReadState *notify_rs)
104
{
105
+ CompareState *s = container_of(notify_rs, CompareState, notify_rs);
106
+
107
/* Get Xen colo-frame's notify and handle the message */
108
+ char *data = g_memdup(notify_rs->buf, notify_rs->packet_len);
109
+ char msg[] = "COLO_COMPARE_GET_XEN_INIT";
110
+ int ret;
111
+
112
+ if (!strcmp(data, "COLO_USERSPACE_PROXY_INIT")) {
113
+ ret = compare_chr_send(s, (uint8_t *)msg, strlen(msg), 0, true);
114
+ if (ret < 0) {
115
+ error_report("Notify Xen COLO-frame INIT failed");
116
+ }
117
+ }
118
+
119
+ if (!strcmp(data, "COLO_CHECKPOINT")) {
120
+ /* colo-compare do checkpoint, flush pri packet and remove sec packet */
121
+ g_queue_foreach(&s->conn_list, colo_flush_packets, s);
122
+ }
123
}
124
125
/*
126
--
876
--
127
2.5.0
877
2.7.4
128
878
129
879
diff view generated by jsdifflib
1
From: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
1
From: Andrew Melnychenko <andrew@daynix.com>
2
2
3
Previously there was a single instance of the timer used by
3
When RSS is enabled the device tries to load the eBPF program
4
monitor triggered announces, that's OK, but when combined with the
4
to select RX virtqueue in the TUN. If eBPF can be loaded
5
previous change that lets you have announces for subsets of interfaces
5
the RSS will function also with vhost (works with kernel 5.8 and later).
6
it's a bit restrictive if you want to do different things to different
6
Software RSS is used as a fallback with vhost=off when eBPF can't be loaded
7
interfaces.
7
or when hash population requested by the guest.
8
8
9
Add an 'id' field to the announce, and maintain a list of the
9
Signed-off-by: Yuri Benditovich <yuri.benditovich@daynix.com>
10
timers based on id.
10
Signed-off-by: Andrew Melnychenko <andrew@daynix.com>
11
12
This allows you to for example:
13
a) Start an announce going on interface eth0 for a long time
14
b) Start an announce going on interface eth1 for a long time
15
c) Kill the announce on eth0 while leaving eth1 going.
16
17
Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
18
Signed-off-by: Jason Wang <jasowang@redhat.com>
11
Signed-off-by: Jason Wang <jasowang@redhat.com>
19
---
12
---
20
hw/net/virtio-net.c | 4 ++--
13
hw/net/vhost_net.c | 3 ++
21
include/net/announce.h | 8 ++++++--
14
hw/net/virtio-net.c | 116 +++++++++++++++++++++++++++++++++++++++--
22
net/announce.c | 52 +++++++++++++++++++++++++++++++++++++++++++-------
15
include/hw/virtio/virtio-net.h | 4 ++
23
net/trace-events | 3 ++-
16
net/vhost-vdpa.c | 2 +
24
qapi/net.json | 9 +++++++--
17
4 files changed, 122 insertions(+), 3 deletions(-)
25
5 files changed, 62 insertions(+), 14 deletions(-)
18
26
19
diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c
20
index XXXXXXX..XXXXXXX 100644
21
--- a/hw/net/vhost_net.c
22
+++ b/hw/net/vhost_net.c
23
@@ -XXX,XX +XXX,XX @@ static const int kernel_feature_bits[] = {
24
VIRTIO_NET_F_MTU,
25
VIRTIO_F_IOMMU_PLATFORM,
26
VIRTIO_F_RING_PACKED,
27
+ VIRTIO_NET_F_HASH_REPORT,
28
VHOST_INVALID_FEATURE_BIT
29
};
30
31
@@ -XXX,XX +XXX,XX @@ static const int user_feature_bits[] = {
32
VIRTIO_NET_F_MTU,
33
VIRTIO_F_IOMMU_PLATFORM,
34
VIRTIO_F_RING_PACKED,
35
+ VIRTIO_NET_F_RSS,
36
+ VIRTIO_NET_F_HASH_REPORT,
37
38
/* This bit implies RARP isn't sent by QEMU out of band */
39
VIRTIO_NET_F_GUEST_ANNOUNCE,
27
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
40
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
28
index XXXXXXX..XXXXXXX 100644
41
index XXXXXXX..XXXXXXX 100644
29
--- a/hw/net/virtio-net.c
42
--- a/hw/net/virtio-net.c
30
+++ b/hw/net/virtio-net.c
43
+++ b/hw/net/virtio-net.c
44
@@ -XXX,XX +XXX,XX @@ static uint64_t virtio_net_get_features(VirtIODevice *vdev, uint64_t features,
45
return features;
46
}
47
48
- virtio_clear_feature(&features, VIRTIO_NET_F_RSS);
49
- virtio_clear_feature(&features, VIRTIO_NET_F_HASH_REPORT);
50
+ if (!ebpf_rss_is_loaded(&n->ebpf_rss)) {
51
+ virtio_clear_feature(&features, VIRTIO_NET_F_RSS);
52
+ }
53
features = vhost_net_get_features(get_vhost_net(nc->peer), features);
54
vdev->backend_features = features;
55
56
@@ -XXX,XX +XXX,XX @@ static int virtio_net_handle_announce(VirtIONet *n, uint8_t cmd,
57
}
58
}
59
60
+static void virtio_net_detach_epbf_rss(VirtIONet *n);
61
+
62
static void virtio_net_disable_rss(VirtIONet *n)
63
{
64
if (n->rss_data.enabled) {
65
trace_virtio_net_rss_disable();
66
}
67
n->rss_data.enabled = false;
68
+
69
+ virtio_net_detach_epbf_rss(n);
70
+}
71
+
72
+static bool virtio_net_attach_ebpf_to_backend(NICState *nic, int prog_fd)
73
+{
74
+ NetClientState *nc = qemu_get_peer(qemu_get_queue(nic), 0);
75
+ if (nc == NULL || nc->info->set_steering_ebpf == NULL) {
76
+ return false;
77
+ }
78
+
79
+ return nc->info->set_steering_ebpf(nc, prog_fd);
80
+}
81
+
82
+static void rss_data_to_rss_config(struct VirtioNetRssData *data,
83
+ struct EBPFRSSConfig *config)
84
+{
85
+ config->redirect = data->redirect;
86
+ config->populate_hash = data->populate_hash;
87
+ config->hash_types = data->hash_types;
88
+ config->indirections_len = data->indirections_len;
89
+ config->default_queue = data->default_queue;
90
+}
91
+
92
+static bool virtio_net_attach_epbf_rss(VirtIONet *n)
93
+{
94
+ struct EBPFRSSConfig config = {};
95
+
96
+ if (!ebpf_rss_is_loaded(&n->ebpf_rss)) {
97
+ return false;
98
+ }
99
+
100
+ rss_data_to_rss_config(&n->rss_data, &config);
101
+
102
+ if (!ebpf_rss_set_all(&n->ebpf_rss, &config,
103
+ n->rss_data.indirections_table, n->rss_data.key)) {
104
+ return false;
105
+ }
106
+
107
+ if (!virtio_net_attach_ebpf_to_backend(n->nic, n->ebpf_rss.program_fd)) {
108
+ return false;
109
+ }
110
+
111
+ return true;
112
+}
113
+
114
+static void virtio_net_detach_epbf_rss(VirtIONet *n)
115
+{
116
+ virtio_net_attach_ebpf_to_backend(n->nic, -1);
117
+}
118
+
119
+static bool virtio_net_load_ebpf(VirtIONet *n)
120
+{
121
+ if (!virtio_net_attach_ebpf_to_backend(n->nic, -1)) {
122
+ /* backend does't support steering ebpf */
123
+ return false;
124
+ }
125
+
126
+ return ebpf_rss_load(&n->ebpf_rss);
127
+}
128
+
129
+static void virtio_net_unload_ebpf(VirtIONet *n)
130
+{
131
+ virtio_net_attach_ebpf_to_backend(n->nic, -1);
132
+ ebpf_rss_unload(&n->ebpf_rss);
133
}
134
135
static uint16_t virtio_net_handle_rss(VirtIONet *n,
136
@@ -XXX,XX +XXX,XX @@ static uint16_t virtio_net_handle_rss(VirtIONet *n,
137
goto error;
138
}
139
n->rss_data.enabled = true;
140
+
141
+ if (!n->rss_data.populate_hash) {
142
+ if (!virtio_net_attach_epbf_rss(n)) {
143
+ /* EBPF must be loaded for vhost */
144
+ if (get_vhost_net(qemu_get_queue(n->nic)->peer)) {
145
+ warn_report("Can't load eBPF RSS for vhost");
146
+ goto error;
147
+ }
148
+ /* fallback to software RSS */
149
+ warn_report("Can't load eBPF RSS - fallback to software RSS");
150
+ n->rss_data.enabled_software_rss = true;
151
+ }
152
+ } else {
153
+ /* use software RSS for hash populating */
154
+ /* and detach eBPF if was loaded before */
155
+ virtio_net_detach_epbf_rss(n);
156
+ n->rss_data.enabled_software_rss = true;
157
+ }
158
+
159
trace_virtio_net_rss_enable(n->rss_data.hash_types,
160
n->rss_data.indirections_len,
161
temp.b);
162
@@ -XXX,XX +XXX,XX @@ static ssize_t virtio_net_receive_rcu(NetClientState *nc, const uint8_t *buf,
163
return -1;
164
}
165
166
- if (!no_rss && n->rss_data.enabled) {
167
+ if (!no_rss && n->rss_data.enabled && n->rss_data.enabled_software_rss) {
168
int index = virtio_net_process_rss(nc, buf, size);
169
if (index >= 0) {
170
NetClientState *nc2 = qemu_get_subqueue(n->nic, index);
31
@@ -XXX,XX +XXX,XX @@ static int virtio_net_post_load_device(void *opaque, int version_id)
171
@@ -XXX,XX +XXX,XX @@ static int virtio_net_post_load_device(void *opaque, int version_id)
32
timer_mod(n->announce_timer.tm,
172
}
33
qemu_clock_get_ms(n->announce_timer.type));
173
34
} else {
174
if (n->rss_data.enabled) {
35
- qemu_announce_timer_del(&n->announce_timer);
175
+ n->rss_data.enabled_software_rss = n->rss_data.populate_hash;
36
+ qemu_announce_timer_del(&n->announce_timer, false);
176
+ if (!n->rss_data.populate_hash) {
37
}
177
+ if (!virtio_net_attach_epbf_rss(n)) {
38
}
178
+ if (get_vhost_net(qemu_get_queue(n->nic)->peer)) {
39
179
+ warn_report("Can't post-load eBPF RSS for vhost");
40
@@ -XXX,XX +XXX,XX @@ static void virtio_net_device_unrealize(DeviceState *dev, Error **errp)
180
+ } else {
41
virtio_net_del_queue(n, i);
181
+ warn_report("Can't post-load eBPF RSS - "
42
}
182
+ "fallback to software RSS");
43
183
+ n->rss_data.enabled_software_rss = true;
44
- qemu_announce_timer_del(&n->announce_timer);
184
+ }
45
+ qemu_announce_timer_del(&n->announce_timer, false);
185
+ }
46
g_free(n->vqs);
186
+ }
47
qemu_del_nic(n->nic);
187
+
48
virtio_net_rsc_cleanup(n);
188
trace_virtio_net_rss_enable(n->rss_data.hash_types,
49
diff --git a/include/net/announce.h b/include/net/announce.h
189
n->rss_data.indirections_len,
50
index XXXXXXX..XXXXXXX 100644
190
sizeof(n->rss_data.key));
51
--- a/include/net/announce.h
191
@@ -XXX,XX +XXX,XX @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp)
52
+++ b/include/net/announce.h
192
n->qdev = dev;
53
@@ -XXX,XX +XXX,XX @@ struct AnnounceTimer {
193
54
/* Returns: update the timer to the next time point */
194
net_rx_pkt_init(&n->rx_pkt, false);
55
int64_t qemu_announce_timer_step(AnnounceTimer *timer);
195
+
56
196
+ if (virtio_has_feature(n->host_features, VIRTIO_NET_F_RSS)) {
57
-/* Delete the underlying timer and other data */
197
+ virtio_net_load_ebpf(n);
58
-void qemu_announce_timer_del(AnnounceTimer *timer);
198
+ }
59
+/*
199
}
60
+ * Delete the underlying timer and other data
200
61
+ * If 'free_named' true and the timer is a named timer, then remove
201
static void virtio_net_device_unrealize(DeviceState *dev)
62
+ * it from the list of named timers and free the AnnounceTimer itself.
202
@@ -XXX,XX +XXX,XX @@ static void virtio_net_device_unrealize(DeviceState *dev)
63
+ */
203
VirtIONet *n = VIRTIO_NET(dev);
64
+void qemu_announce_timer_del(AnnounceTimer *timer, bool free_named);
204
int i, max_queues;
65
205
66
/*
206
+ if (virtio_has_feature(n->host_features, VIRTIO_NET_F_RSS)) {
67
* Under BQL/main thread
207
+ virtio_net_unload_ebpf(n);
68
diff --git a/net/announce.c b/net/announce.c
208
+ }
69
index XXXXXXX..XXXXXXX 100644
209
+
70
--- a/net/announce.c
210
/* This will stop vhost backend if appropriate. */
71
+++ b/net/announce.c
211
virtio_net_set_status(vdev, 0);
212
213
@@ -XXX,XX +XXX,XX @@ static void virtio_net_instance_init(Object *obj)
214
device_add_bootindex_property(obj, &n->nic_conf.bootindex,
215
"bootindex", "/ethernet-phy@0",
216
DEVICE(n));
217
+
218
+ ebpf_rss_init(&n->ebpf_rss);
219
}
220
221
static int virtio_net_pre_save(void *opaque)
222
diff --git a/include/hw/virtio/virtio-net.h b/include/hw/virtio/virtio-net.h
223
index XXXXXXX..XXXXXXX 100644
224
--- a/include/hw/virtio/virtio-net.h
225
+++ b/include/hw/virtio/virtio-net.h
72
@@ -XXX,XX +XXX,XX @@
226
@@ -XXX,XX +XXX,XX @@
73
#include "qapi/qapi-commands-net.h"
227
#include "qemu/option_int.h"
74
#include "trace.h"
228
#include "qom/object.h"
75
229
76
+static GData *named_timers;
230
+#include "ebpf/ebpf_rss.h"
77
+
231
+
78
int64_t qemu_announce_timer_step(AnnounceTimer *timer)
232
#define TYPE_VIRTIO_NET "virtio-net-device"
79
{
233
OBJECT_DECLARE_SIMPLE_TYPE(VirtIONet, VIRTIO_NET)
80
int64_t step;
234
81
@@ -XXX,XX +XXX,XX @@ int64_t qemu_announce_timer_step(AnnounceTimer *timer)
235
@@ -XXX,XX +XXX,XX @@ typedef struct VirtioNetRscChain {
82
return step;
236
83
}
237
typedef struct VirtioNetRssData {
84
238
bool enabled;
85
-void qemu_announce_timer_del(AnnounceTimer *timer)
239
+ bool enabled_software_rss;
86
+/*
240
bool redirect;
87
+ * If 'free_named' is true, then remove the timer from the list
241
bool populate_hash;
88
+ * and free the timer itself.
242
uint32_t hash_types;
89
+ */
243
@@ -XXX,XX +XXX,XX @@ struct VirtIONet {
90
+void qemu_announce_timer_del(AnnounceTimer *timer, bool free_named)
244
Notifier migration_state;
91
{
245
VirtioNetRssData rss_data;
92
+ bool free_timer = false;
246
struct NetRxPkt *rx_pkt;
93
if (timer->tm) {
247
+ struct EBPFRSSContext ebpf_rss;
94
timer_del(timer->tm);
248
};
95
timer_free(timer->tm);
249
96
@@ -XXX,XX +XXX,XX @@ void qemu_announce_timer_del(AnnounceTimer *timer)
250
void virtio_net_set_netclient_name(VirtIONet *n, const char *name,
97
}
251
diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c
98
qapi_free_strList(timer->params.interfaces);
252
index XXXXXXX..XXXXXXX 100644
99
timer->params.interfaces = NULL;
253
--- a/net/vhost-vdpa.c
100
+ if (free_named && timer->params.has_id) {
254
+++ b/net/vhost-vdpa.c
101
+ AnnounceTimer *list_timer;
255
@@ -XXX,XX +XXX,XX @@ const int vdpa_feature_bits[] = {
102
+ /*
256
VIRTIO_NET_F_MTU,
103
+ * Sanity check: There should only be one timer on the list with
257
VIRTIO_F_IOMMU_PLATFORM,
104
+ * the id.
258
VIRTIO_F_RING_PACKED,
105
+ */
259
+ VIRTIO_NET_F_RSS,
106
+ list_timer = g_datalist_get_data(&named_timers, timer->params.id);
260
+ VIRTIO_NET_F_HASH_REPORT,
107
+ assert(timer == list_timer);
261
VIRTIO_NET_F_GUEST_ANNOUNCE,
108
+ free_timer = true;
262
VIRTIO_NET_F_STATUS,
109
+ g_datalist_remove_data(&named_timers, timer->params.id);
263
VHOST_INVALID_FEATURE_BIT
110
+ }
111
+ trace_qemu_announce_timer_del(free_named, free_timer, timer->params.id);
112
+ g_free(timer->params.id);
113
+ timer->params.id = NULL;
114
+
115
+ if (free_timer) {
116
+ g_free(timer);
117
+ }
118
}
119
120
/*
121
@@ -XXX,XX +XXX,XX @@ void qemu_announce_timer_reset(AnnounceTimer *timer,
122
* We're under the BQL, so the current timer can't
123
* be firing, so we should be able to delete it.
124
*/
125
- qemu_announce_timer_del(timer);
126
+ qemu_announce_timer_del(timer, false);
127
128
QAPI_CLONE_MEMBERS(AnnounceParameters, &timer->params, params);
129
timer->round = params->rounds;
130
@@ -XXX,XX +XXX,XX @@ static void qemu_announce_self_iter(NICState *nic, void *opaque)
131
skip = false;
132
}
133
134
- trace_qemu_announce_self_iter(nic->ncs->name,
135
+ trace_qemu_announce_self_iter(timer->params.has_id ? timer->params.id : "_",
136
+ nic->ncs->name,
137
qemu_ether_ntoa(&nic->conf->macaddr), skip);
138
139
if (!skip) {
140
@@ -XXX,XX +XXX,XX @@ static void qemu_announce_self_once(void *opaque)
141
if (--timer->round) {
142
qemu_announce_timer_step(timer);
143
} else {
144
- qemu_announce_timer_del(timer);
145
+ qemu_announce_timer_del(timer, true);
146
}
147
}
148
149
@@ -XXX,XX +XXX,XX @@ void qemu_announce_self(AnnounceTimer *timer, AnnounceParameters *params)
150
if (params->rounds) {
151
qemu_announce_self_once(timer);
152
} else {
153
- qemu_announce_timer_del(timer);
154
+ qemu_announce_timer_del(timer, true);
155
}
156
}
157
158
void qmp_announce_self(AnnounceParameters *params, Error **errp)
159
{
160
- static AnnounceTimer announce_timer;
161
- qemu_announce_self(&announce_timer, params);
162
+ AnnounceTimer *named_timer;
163
+ if (!params->has_id) {
164
+ params->id = g_strdup("");
165
+ params->has_id = true;
166
+ }
167
+
168
+ named_timer = g_datalist_get_data(&named_timers, params->id);
169
+
170
+ if (!named_timer) {
171
+ named_timer = g_new0(AnnounceTimer, 1);
172
+ g_datalist_set_data(&named_timers, params->id, named_timer);
173
+ }
174
+
175
+ qemu_announce_self(named_timer, params);
176
}
177
diff --git a/net/trace-events b/net/trace-events
178
index XXXXXXX..XXXXXXX 100644
179
--- a/net/trace-events
180
+++ b/net/trace-events
181
@@ -XXX,XX +XXX,XX @@
182
# See docs/devel/tracing.txt for syntax documentation.
183
184
# announce.c
185
-qemu_announce_self_iter(const char *name, const char *mac, int skip) "%s:%s skip: %d"
186
+qemu_announce_self_iter(const char *id, const char *name, const char *mac, int skip) "%s:%s:%s skip: %d"
187
+qemu_announce_timer_del(bool free_named, bool free_timer, char *id) "free named: %d free timer: %d id: %s"
188
189
# vhost-user.c
190
vhost_user_event(const char *chr, int event) "chr: %s got event: %d"
191
diff --git a/qapi/net.json b/qapi/net.json
192
index XXXXXXX..XXXXXXX 100644
193
--- a/qapi/net.json
194
+++ b/qapi/net.json
195
@@ -XXX,XX +XXX,XX @@
196
# @interfaces: An optional list of interface names, which restricts the
197
# announcement to the listed interfaces. (Since 4.1)
198
#
199
+# @id: A name to be used to identify an instance of announce-timers
200
+# and to allow it to modified later. Not for use as
201
+# part of the migration parameters. (Since 4.1)
202
+#
203
# Since: 4.0
204
##
205
206
@@ -XXX,XX +XXX,XX @@
207
'max': 'int',
208
'rounds': 'int',
209
'step': 'int',
210
- '*interfaces': ['str'] } }
211
+ '*interfaces': ['str'],
212
+ '*id' : 'str' } }
213
214
##
215
# @announce-self:
216
@@ -XXX,XX +XXX,XX @@
217
# -> { "execute": "announce-self",
218
# "arguments": {
219
# "initial": 50, "max": 550, "rounds": 10, "step": 50,
220
-# "interfaces": ["vn2", "vn3"] } }
221
+# "interfaces": ["vn2", "vn3"], "id": "bob" } }
222
# <- { "return": {} }
223
#
224
# Since: 4.0
225
--
264
--
226
2.5.0
265
2.7.4
227
266
228
267
diff view generated by jsdifflib
1
From: Zhang Chen <chen.zhang@intel.com>
1
From: Andrew Melnychenko <andrew@daynix.com>
2
2
3
We need use this function to send notification message for remote colo-frame(Xen).
3
Signed-off-by: Yuri Benditovich <yuri.benditovich@daynix.com>
4
So we add new parameter for this job.
4
Signed-off-by: Andrew Melnychenko <andrew@daynix.com>
5
6
Signed-off-by: Zhang Chen <chen.zhang@intel.com>
7
Signed-off-by: Jason Wang <jasowang@redhat.com>
5
Signed-off-by: Jason Wang <jasowang@redhat.com>
8
---
6
---
9
net/colo-compare.c | 41 +++++++++++++++++++++++++++++++++--------
7
docs/devel/ebpf_rss.rst | 125 ++++++++++++++++++++++++++++++++++++++++++++++++
10
1 file changed, 33 insertions(+), 8 deletions(-)
8
docs/devel/index.rst | 1 +
9
2 files changed, 126 insertions(+)
10
create mode 100644 docs/devel/ebpf_rss.rst
11
11
12
diff --git a/net/colo-compare.c b/net/colo-compare.c
12
diff --git a/docs/devel/ebpf_rss.rst b/docs/devel/ebpf_rss.rst
13
new file mode 100644
14
index XXXXXXX..XXXXXXX
15
--- /dev/null
16
+++ b/docs/devel/ebpf_rss.rst
17
@@ -XXX,XX +XXX,XX @@
18
+===========================
19
+eBPF RSS virtio-net support
20
+===========================
21
+
22
+RSS(Receive Side Scaling) is used to distribute network packets to guest virtqueues
23
+by calculating packet hash. Usually every queue is processed then by a specific guest CPU core.
24
+
25
+For now there are 2 RSS implementations in qemu:
26
+- 'in-qemu' RSS (functions if qemu receives network packets, i.e. vhost=off)
27
+- eBPF RSS (can function with also with vhost=on)
28
+
29
+eBPF support (CONFIG_EBPF) is enabled by 'configure' script.
30
+To enable eBPF RSS support use './configure --enable-bpf'.
31
+
32
+If steering BPF is not set for kernel's TUN module, the TUN uses automatic selection
33
+of rx virtqueue based on lookup table built according to calculated symmetric hash
34
+of transmitted packets.
35
+If steering BPF is set for TUN the BPF code calculates the hash of packet header and
36
+returns the virtqueue number to place the packet to.
37
+
38
+Simplified decision formula:
39
+
40
+.. code:: C
41
+
42
+ queue_index = indirection_table[hash(<packet data>)%<indirection_table size>]
43
+
44
+
45
+Not for all packets, the hash can/should be calculated.
46
+
47
+Note: currently, eBPF RSS does not support hash reporting.
48
+
49
+eBPF RSS turned on by different combinations of vhost-net, vitrio-net and tap configurations:
50
+
51
+- eBPF is used:
52
+
53
+ tap,vhost=off & virtio-net-pci,rss=on,hash=off
54
+
55
+- eBPF is used:
56
+
57
+ tap,vhost=on & virtio-net-pci,rss=on,hash=off
58
+
59
+- 'in-qemu' RSS is used:
60
+
61
+ tap,vhost=off & virtio-net-pci,rss=on,hash=on
62
+
63
+- eBPF is used, hash population feature is not reported to the guest:
64
+
65
+ tap,vhost=on & virtio-net-pci,rss=on,hash=on
66
+
67
+If CONFIG_EBPF is not set then only 'in-qemu' RSS is supported.
68
+Also 'in-qemu' RSS, as a fallback, is used if the eBPF program failed to load or set to TUN.
69
+
70
+RSS eBPF program
71
+----------------
72
+
73
+RSS program located in ebpf/rss.bpf.skeleton.h generated by bpftool.
74
+So the program is part of the qemu binary.
75
+Initially, the eBPF program was compiled by clang and source code located at tools/ebpf/rss.bpf.c.
76
+Prerequisites to recompile the eBPF program (regenerate ebpf/rss.bpf.skeleton.h):
77
+
78
+ llvm, clang, kernel source tree, bpftool
79
+ Adjust Makefile.ebpf to reflect the location of the kernel source tree
80
+
81
+ $ cd tools/ebpf
82
+ $ make -f Makefile.ebpf
83
+
84
+Current eBPF RSS implementation uses 'bounded loops' with 'backward jump instructions' which present in the last kernels.
85
+Overall eBPF RSS works on kernels 5.8+.
86
+
87
+eBPF RSS implementation
88
+-----------------------
89
+
90
+eBPF RSS loading functionality located in ebpf/ebpf_rss.c and ebpf/ebpf_rss.h.
91
+
92
+The `struct EBPFRSSContext` structure that holds 4 file descriptors:
93
+
94
+- ctx - pointer of the libbpf context.
95
+- program_fd - file descriptor of the eBPF RSS program.
96
+- map_configuration - file descriptor of the 'configuration' map. This map contains one element of 'struct EBPFRSSConfig'. This configuration determines eBPF program behavior.
97
+- map_toeplitz_key - file descriptor of the 'Toeplitz key' map. One element of the 40byte key prepared for the hashing algorithm.
98
+- map_indirections_table - 128 elements of queue indexes.
99
+
100
+`struct EBPFRSSConfig` fields:
101
+
102
+- redirect - "boolean" value, should the hash be calculated, on false - `default_queue` would be used as the final decision.
103
+- populate_hash - for now, not used. eBPF RSS doesn't support hash reporting.
104
+- hash_types - binary mask of different hash types. See `VIRTIO_NET_RSS_HASH_TYPE_*` defines. If for packet hash should not be calculated - `default_queue` would be used.
105
+- indirections_len - length of the indirections table, maximum 128.
106
+- default_queue - the queue index that used for packet that shouldn't be hashed. For some packets, the hash can't be calculated(g.e ARP).
107
+
108
+Functions:
109
+
110
+- `ebpf_rss_init()` - sets ctx to NULL, which indicates that EBPFRSSContext is not loaded.
111
+- `ebpf_rss_load()` - creates 3 maps and loads eBPF program from the rss.bpf.skeleton.h. Returns 'true' on success. After that, program_fd can be used to set steering for TAP.
112
+- `ebpf_rss_set_all()` - sets values for eBPF maps. `indirections_table` length is in EBPFRSSConfig. `toeplitz_key` is VIRTIO_NET_RSS_MAX_KEY_SIZE aka 40 bytes array.
113
+- `ebpf_rss_unload()` - close all file descriptors and set ctx to NULL.
114
+
115
+Simplified eBPF RSS workflow:
116
+
117
+.. code:: C
118
+
119
+ struct EBPFRSSConfig config;
120
+ config.redirect = 1;
121
+ config.hash_types = VIRTIO_NET_RSS_HASH_TYPE_UDPv4 | VIRTIO_NET_RSS_HASH_TYPE_TCPv4;
122
+ config.indirections_len = VIRTIO_NET_RSS_MAX_TABLE_LEN;
123
+ config.default_queue = 0;
124
+
125
+ uint16_t table[VIRTIO_NET_RSS_MAX_TABLE_LEN] = {...};
126
+ uint8_t key[VIRTIO_NET_RSS_MAX_KEY_SIZE] = {...};
127
+
128
+ struct EBPFRSSContext ctx;
129
+ ebpf_rss_init(&ctx);
130
+ ebpf_rss_load(&ctx);
131
+ ebpf_rss_set_all(&ctx, &config, table, key);
132
+ if (net_client->info->set_steering_ebpf != NULL) {
133
+ net_client->info->set_steering_ebpf(net_client, ctx->program_fd);
134
+ }
135
+ ...
136
+ ebpf_unload(&ctx);
137
+
138
+
139
+NetClientState SetSteeringEBPF()
140
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
141
+
142
+For now, `set_steering_ebpf()` method supported by Linux TAP NetClientState. The method requires an eBPF program file descriptor as an argument.
143
diff --git a/docs/devel/index.rst b/docs/devel/index.rst
13
index XXXXXXX..XXXXXXX 100644
144
index XXXXXXX..XXXXXXX 100644
14
--- a/net/colo-compare.c
145
--- a/docs/devel/index.rst
15
+++ b/net/colo-compare.c
146
+++ b/docs/devel/index.rst
16
@@ -XXX,XX +XXX,XX @@ static void colo_compare_inconsistency_notify(void)
147
@@ -XXX,XX +XXX,XX @@ Contents:
17
static int compare_chr_send(CompareState *s,
148
qom
18
const uint8_t *buf,
149
block-coroutine-wrapper
19
uint32_t size,
150
multi-process
20
- uint32_t vnet_hdr_len);
151
+ ebpf_rss
21
+ uint32_t vnet_hdr_len,
22
+ bool notify_remote_frame);
23
24
static gint seq_sorter(Packet *a, Packet *b, gpointer data)
25
{
26
@@ -XXX,XX +XXX,XX @@ static void colo_release_primary_pkt(CompareState *s, Packet *pkt)
27
ret = compare_chr_send(s,
28
pkt->data,
29
pkt->size,
30
- pkt->vnet_hdr_len);
31
+ pkt->vnet_hdr_len,
32
+ false);
33
if (ret < 0) {
34
error_report("colo send primary packet failed");
35
}
36
@@ -XXX,XX +XXX,XX @@ static void colo_compare_connection(void *opaque, void *user_data)
37
static int compare_chr_send(CompareState *s,
38
const uint8_t *buf,
39
uint32_t size,
40
- uint32_t vnet_hdr_len)
41
+ uint32_t vnet_hdr_len,
42
+ bool notify_remote_frame)
43
{
44
int ret = 0;
45
uint32_t len = htonl(size);
46
@@ -XXX,XX +XXX,XX @@ static int compare_chr_send(CompareState *s,
47
return 0;
48
}
49
50
- ret = qemu_chr_fe_write_all(&s->chr_out, (uint8_t *)&len, sizeof(len));
51
+ if (notify_remote_frame) {
52
+ ret = qemu_chr_fe_write_all(&s->chr_notify_dev,
53
+ (uint8_t *)&len,
54
+ sizeof(len));
55
+ } else {
56
+ ret = qemu_chr_fe_write_all(&s->chr_out, (uint8_t *)&len, sizeof(len));
57
+ }
58
+
59
if (ret != sizeof(len)) {
60
goto err;
61
}
62
@@ -XXX,XX +XXX,XX @@ static int compare_chr_send(CompareState *s,
63
* know how to parse net packet correctly.
64
*/
65
len = htonl(vnet_hdr_len);
66
- ret = qemu_chr_fe_write_all(&s->chr_out, (uint8_t *)&len, sizeof(len));
67
+
68
+ if (!notify_remote_frame) {
69
+ ret = qemu_chr_fe_write_all(&s->chr_out,
70
+ (uint8_t *)&len,
71
+ sizeof(len));
72
+ }
73
+
74
if (ret != sizeof(len)) {
75
goto err;
76
}
77
}
78
79
- ret = qemu_chr_fe_write_all(&s->chr_out, (uint8_t *)buf, size);
80
+ if (notify_remote_frame) {
81
+ ret = qemu_chr_fe_write_all(&s->chr_notify_dev,
82
+ (uint8_t *)buf,
83
+ size);
84
+ } else {
85
+ ret = qemu_chr_fe_write_all(&s->chr_out, (uint8_t *)buf, size);
86
+ }
87
+
88
if (ret != size) {
89
goto err;
90
}
91
@@ -XXX,XX +XXX,XX @@ static void compare_pri_rs_finalize(SocketReadState *pri_rs)
92
compare_chr_send(s,
93
pri_rs->buf,
94
pri_rs->packet_len,
95
- pri_rs->vnet_hdr_len);
96
+ pri_rs->vnet_hdr_len,
97
+ false);
98
} else {
99
/* compare packet in the specified connection */
100
colo_compare_connection(conn, s);
101
@@ -XXX,XX +XXX,XX @@ static void colo_flush_packets(void *opaque, void *user_data)
102
compare_chr_send(s,
103
pkt->data,
104
pkt->size,
105
- pkt->vnet_hdr_len);
106
+ pkt->vnet_hdr_len,
107
+ false);
108
packet_destroy(pkt, NULL);
109
}
110
while (!g_queue_is_empty(&conn->secondary_list)) {
111
--
152
--
112
2.5.0
153
2.7.4
113
154
114
155
diff view generated by jsdifflib
1
From: Markus Armbruster <armbru@redhat.com>
1
From: Andrew Melnychenko <andrew@daynix.com>
2
2
3
Signed-off-by: Markus Armbruster <armbru@redhat.com>
3
Signed-off-by: Yuri Benditovich <yuri.benditovich@daynix.com>
4
Signed-off-by: Andrew Melnychenko <andrew@daynix.com>
4
Signed-off-by: Jason Wang <jasowang@redhat.com>
5
Signed-off-by: Jason Wang <jasowang@redhat.com>
5
---
6
---
6
MAINTAINERS | 1 +
7
MAINTAINERS | 8 ++++++++
7
1 file changed, 1 insertion(+)
8
1 file changed, 8 insertions(+)
8
9
9
diff --git a/MAINTAINERS b/MAINTAINERS
10
diff --git a/MAINTAINERS b/MAINTAINERS
10
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
11
--- a/MAINTAINERS
12
--- a/MAINTAINERS
12
+++ b/MAINTAINERS
13
+++ b/MAINTAINERS
13
@@ -XXX,XX +XXX,XX @@ M: Jason Wang <jasowang@redhat.com>
14
@@ -XXX,XX +XXX,XX @@ F: include/hw/remote/proxy-memory-listener.h
14
S: Maintained
15
F: hw/remote/iohub.c
15
F: net/
16
F: include/hw/remote/iohub.h
16
F: include/net/
17
17
+F: qemu-bridge-helper.c
18
+EBPF:
18
T: git https://github.com/jasowang/qemu.git net
19
+M: Jason Wang <jasowang@redhat.com>
19
F: qapi/net.json
20
+R: Andrew Melnychenko <andrew@daynix.com>
20
21
+R: Yuri Benditovich <yuri.benditovich@daynix.com>
22
+S: Maintained
23
+F: ebpf/*
24
+F: tools/ebpf/*
25
+
26
Build and test automation
27
-------------------------
28
Build and test automation, general continuous integration
21
--
29
--
22
2.5.0
30
2.7.4
23
31
24
32
diff view generated by jsdifflib
Deleted patch
1
From: Cédric Le Goater <clg@kaod.org>
2
1
3
qdev_set_nic_properties() is already used in the Aspeed SoC level to
4
bind the ftgmac100 device to the netdev.
5
6
This is fixing support for multiple net devices.
7
8
Signed-off-by: Cédric Le Goater <clg@kaod.org>
9
Signed-off-by: Jason Wang <jasowang@redhat.com>
10
---
11
hw/net/ftgmac100.c | 2 --
12
1 file changed, 2 deletions(-)
13
14
diff --git a/hw/net/ftgmac100.c b/hw/net/ftgmac100.c
15
index XXXXXXX..XXXXXXX 100644
16
--- a/hw/net/ftgmac100.c
17
+++ b/hw/net/ftgmac100.c
18
@@ -XXX,XX +XXX,XX @@ static void ftgmac100_realize(DeviceState *dev, Error **errp)
19
sysbus_init_irq(sbd, &s->irq);
20
qemu_macaddr_default_if_unset(&s->conf.macaddr);
21
22
- s->conf.peers.ncs[0] = nd_table[0].netdev;
23
-
24
s->nic = qemu_new_nic(&net_ftgmac100_info, &s->conf,
25
object_get_typename(OBJECT(dev)), DEVICE(dev)->id,
26
s);
27
--
28
2.5.0
29
30
diff view generated by jsdifflib
Deleted patch
1
From: Stefano Garzarella <sgarzare@redhat.com>
2
1
3
If 'ipv6-prefixlen' is not a number, the current behaviour
4
produces an assertion failure:
5
$ qemu-system-x86_64 -net user,ipv6-net=feca::0/a
6
qemu-system-x86_64: qemu/util/qemu-option.c:1175: qemu_opts_foreach:
7
Assertion `!errp || !*errp' failed.
8
Aborted (core dumped)
9
10
This patch fixes it, jumping to the end of the function when
11
'ipv6-prefixlen' is not a number, and printing the more friendly
12
message:
13
$ qemu-system-x86_64 -net user,ipv6-net=feca::0/a
14
qemu-system-x86_64: Parameter 'ipv6-prefixlen' expects a number
15
16
Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
17
Reviewed-by: Markus Armbruster <armbru@redhat.com>
18
Signed-off-by: Jason Wang <jasowang@redhat.com>
19
---
20
net/net.c | 9 +++++----
21
1 file changed, 5 insertions(+), 4 deletions(-)
22
23
diff --git a/net/net.c b/net/net.c
24
index XXXXXXX..XXXXXXX 100644
25
--- a/net/net.c
26
+++ b/net/net.c
27
@@ -XXX,XX +XXX,XX @@ static int net_client_init(QemuOpts *opts, bool is_netdev, Error **errp)
28
29
if (err) {
30
error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
31
- "ipv6-prefix", "a number");
32
- } else {
33
- qemu_opt_set_number(opts, "ipv6-prefixlen", len,
34
- &error_abort);
35
+ "ipv6-prefixlen", "a number");
36
+ goto out;
37
}
38
+
39
+ qemu_opt_set_number(opts, "ipv6-prefixlen", len, &error_abort);
40
}
41
qemu_opt_unset(opts, "ipv6-net");
42
}
43
@@ -XXX,XX +XXX,XX @@ static int net_client_init(QemuOpts *opts, bool is_netdev, Error **errp)
44
qapi_free_NetLegacy(object);
45
}
46
47
+out:
48
error_propagate(errp, err);
49
visit_free(v);
50
return ret;
51
--
52
2.5.0
53
54
diff view generated by jsdifflib
Deleted patch
1
From: Stefano Garzarella <sgarzare@redhat.com>
2
1
3
net_client_init() uses a variable length array to store the prefix
4
of 'ipv6-net' parameter (e.g. if ipv6-net=fec0::0/64, the prefix
5
is 'fec0::0').
6
This patch introduces g_strsplit() to split the 'ipv6-net' parameter,
7
so we can remove the variable length array.
8
9
Suggested-by: Markus Armbruster <armbru@redhat.com>
10
Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
11
Reviewed-by: Markus Armbruster <armbru@redhat.com>
12
Signed-off-by: Jason Wang <jasowang@redhat.com>
13
---
14
net/net.c | 31 +++++++++++++++++++------------
15
1 file changed, 19 insertions(+), 12 deletions(-)
16
17
diff --git a/net/net.c b/net/net.c
18
index XXXXXXX..XXXXXXX 100644
19
--- a/net/net.c
20
+++ b/net/net.c
21
@@ -XXX,XX +XXX,XX @@ static void show_netdevs(void)
22
23
static int net_client_init(QemuOpts *opts, bool is_netdev, Error **errp)
24
{
25
+ gchar **substrings = NULL;
26
void *object = NULL;
27
Error *err = NULL;
28
int ret = -1;
29
@@ -XXX,XX +XXX,XX @@ static int net_client_init(QemuOpts *opts, bool is_netdev, Error **errp)
30
const char *ip6_net = qemu_opt_get(opts, "ipv6-net");
31
32
if (ip6_net) {
33
- char buf[strlen(ip6_net) + 1];
34
+ char *prefix_addr;
35
+ unsigned long prefix_len = 64; /* Default 64bit prefix length. */
36
+
37
+ substrings = g_strsplit(ip6_net, "/", 2);
38
+ if (!substrings || !substrings[0]) {
39
+ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "ipv6-net",
40
+ "a valid IPv6 prefix");
41
+ goto out;
42
+ }
43
44
- if (get_str_sep(buf, sizeof(buf), &ip6_net, '/') < 0) {
45
- /* Default 64bit prefix length. */
46
- qemu_opt_set(opts, "ipv6-prefix", ip6_net, &error_abort);
47
- qemu_opt_set_number(opts, "ipv6-prefixlen", 64, &error_abort);
48
- } else {
49
+ prefix_addr = substrings[0];
50
+
51
+ if (substrings[1]) {
52
/* User-specified prefix length. */
53
- unsigned long len;
54
int err;
55
56
- qemu_opt_set(opts, "ipv6-prefix", buf, &error_abort);
57
- err = qemu_strtoul(ip6_net, NULL, 10, &len);
58
-
59
+ err = qemu_strtoul(substrings[1], NULL, 10, &prefix_len);
60
if (err) {
61
error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
62
"ipv6-prefixlen", "a number");
63
goto out;
64
}
65
-
66
- qemu_opt_set_number(opts, "ipv6-prefixlen", len, &error_abort);
67
}
68
+
69
+ qemu_opt_set(opts, "ipv6-prefix", prefix_addr, &error_abort);
70
+ qemu_opt_set_number(opts, "ipv6-prefixlen", prefix_len,
71
+ &error_abort);
72
qemu_opt_unset(opts, "ipv6-net");
73
}
74
}
75
@@ -XXX,XX +XXX,XX @@ static int net_client_init(QemuOpts *opts, bool is_netdev, Error **errp)
76
77
out:
78
error_propagate(errp, err);
79
+ g_strfreev(substrings);
80
visit_free(v);
81
return ret;
82
}
83
--
84
2.5.0
85
86
diff view generated by jsdifflib
Deleted patch
1
From: Stefano Garzarella <sgarzare@redhat.com>
2
1
3
Use the glib function to split host address and port in
4
the parse_host_port() function.
5
6
Suggested-by: Markus Armbruster <armbru@redhat.com>
7
Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
8
Reviewed-by: Markus Armbruster <armbru@redhat.com>
9
Signed-off-by: Jason Wang <jasowang@redhat.com>
10
---
11
net/net.c | 43 +++++++++++++++++++++++++++----------------
12
1 file changed, 27 insertions(+), 16 deletions(-)
13
14
diff --git a/net/net.c b/net/net.c
15
index XXXXXXX..XXXXXXX 100644
16
--- a/net/net.c
17
+++ b/net/net.c
18
@@ -XXX,XX +XXX,XX @@ static int get_str_sep(char *buf, int buf_size, const char **pp, int sep)
19
int parse_host_port(struct sockaddr_in *saddr, const char *str,
20
Error **errp)
21
{
22
- char buf[512];
23
+ gchar **substrings;
24
struct hostent *he;
25
- const char *p, *r;
26
- int port;
27
+ const char *addr, *p, *r;
28
+ int port, ret = 0;
29
30
- p = str;
31
- if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
32
+ substrings = g_strsplit(str, ":", 2);
33
+ if (!substrings || !substrings[0] || !substrings[1]) {
34
error_setg(errp, "host address '%s' doesn't contain ':' "
35
"separating host from port", str);
36
- return -1;
37
+ ret = -1;
38
+ goto out;
39
}
40
+
41
+ addr = substrings[0];
42
+ p = substrings[1];
43
+
44
saddr->sin_family = AF_INET;
45
- if (buf[0] == '\0') {
46
+ if (addr[0] == '\0') {
47
saddr->sin_addr.s_addr = 0;
48
} else {
49
- if (qemu_isdigit(buf[0])) {
50
- if (!inet_aton(buf, &saddr->sin_addr)) {
51
+ if (qemu_isdigit(addr[0])) {
52
+ if (!inet_aton(addr, &saddr->sin_addr)) {
53
error_setg(errp, "host address '%s' is not a valid "
54
- "IPv4 address", buf);
55
- return -1;
56
+ "IPv4 address", addr);
57
+ ret = -1;
58
+ goto out;
59
}
60
} else {
61
- he = gethostbyname(buf);
62
+ he = gethostbyname(addr);
63
if (he == NULL) {
64
- error_setg(errp, "can't resolve host address '%s'", buf);
65
- return - 1;
66
+ error_setg(errp, "can't resolve host address '%s'", addr);
67
+ ret = -1;
68
+ goto out;
69
}
70
saddr->sin_addr = *(struct in_addr *)he->h_addr;
71
}
72
@@ -XXX,XX +XXX,XX @@ int parse_host_port(struct sockaddr_in *saddr, const char *str,
73
port = strtol(p, (char **)&r, 0);
74
if (r == p) {
75
error_setg(errp, "port number '%s' is invalid", p);
76
- return -1;
77
+ ret = -1;
78
+ goto out;
79
}
80
saddr->sin_port = htons(port);
81
- return 0;
82
+
83
+out:
84
+ g_strfreev(substrings);
85
+ return ret;
86
}
87
88
char *qemu_mac_strdup_printf(const uint8_t *macaddr)
89
--
90
2.5.0
91
92
diff view generated by jsdifflib
Deleted patch
1
From: Stefano Garzarella <sgarzare@redhat.com>
2
1
3
Since the get_str_sep() function is no longer used in
4
net/net.c, we can remove it.
5
6
Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
7
Reviewed-by: Markus Armbruster <armbru@redhat.com>
8
Signed-off-by: Jason Wang <jasowang@redhat.com>
9
---
10
net/net.c | 20 --------------------
11
1 file changed, 20 deletions(-)
12
13
diff --git a/net/net.c b/net/net.c
14
index XXXXXXX..XXXXXXX 100644
15
--- a/net/net.c
16
+++ b/net/net.c
17
@@ -XXX,XX +XXX,XX @@ static QTAILQ_HEAD(, NetClientState) net_clients;
18
/***********************************************************/
19
/* network device redirectors */
20
21
-static int get_str_sep(char *buf, int buf_size, const char **pp, int sep)
22
-{
23
- const char *p, *p1;
24
- int len;
25
- p = *pp;
26
- p1 = strchr(p, sep);
27
- if (!p1)
28
- return -1;
29
- len = p1 - p;
30
- p1++;
31
- if (buf_size > 0) {
32
- if (len > buf_size - 1)
33
- len = buf_size - 1;
34
- memcpy(buf, p, len);
35
- buf[len] = '\0';
36
- }
37
- *pp = p1;
38
- return 0;
39
-}
40
-
41
int parse_host_port(struct sockaddr_in *saddr, const char *str,
42
Error **errp)
43
{
44
--
45
2.5.0
46
47
diff view generated by jsdifflib
Deleted patch
1
From: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
2
1
3
Allow the caller to restrict the set of interfaces that announces are
4
sent on. The default is still to send on all interfaces.
5
6
e.g.
7
8
{ "execute": "announce-self", "arguments": { "initial": 50, "max": 550, "rounds": 5, "step": 50, "interfaces": ["vn2", "vn1"] } }
9
10
This doesn't affect the behaviour of migraiton announcments.
11
12
Note: There's still only one timer for the qmp command, so that
13
performing an 'announce-self' on one list of interfaces followed
14
by another 'announce-self' on another list will stop the announces
15
on the existing set.
16
17
Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
18
Signed-off-by: Jason Wang <jasowang@redhat.com>
19
---
20
include/net/announce.h | 2 +-
21
net/announce.c | 39 ++++++++++++++++++++++++++++++++-------
22
net/trace-events | 2 +-
23
qapi/net.json | 11 ++++++++---
24
4 files changed, 42 insertions(+), 12 deletions(-)
25
26
diff --git a/include/net/announce.h b/include/net/announce.h
27
index XXXXXXX..XXXXXXX 100644
28
--- a/include/net/announce.h
29
+++ b/include/net/announce.h
30
@@ -XXX,XX +XXX,XX @@ struct AnnounceTimer {
31
/* Returns: update the timer to the next time point */
32
int64_t qemu_announce_timer_step(AnnounceTimer *timer);
33
34
-/* Delete the underlying timer */
35
+/* Delete the underlying timer and other data */
36
void qemu_announce_timer_del(AnnounceTimer *timer);
37
38
/*
39
diff --git a/net/announce.c b/net/announce.c
40
index XXXXXXX..XXXXXXX 100644
41
--- a/net/announce.c
42
+++ b/net/announce.c
43
@@ -XXX,XX +XXX,XX @@ void qemu_announce_timer_del(AnnounceTimer *timer)
44
timer_free(timer->tm);
45
timer->tm = NULL;
46
}
47
+ qapi_free_strList(timer->params.interfaces);
48
+ timer->params.interfaces = NULL;
49
}
50
51
/*
52
@@ -XXX,XX +XXX,XX @@ static int announce_self_create(uint8_t *buf,
53
54
static void qemu_announce_self_iter(NICState *nic, void *opaque)
55
{
56
+ AnnounceTimer *timer = opaque;
57
uint8_t buf[60];
58
int len;
59
+ bool skip;
60
+
61
+ if (timer->params.has_interfaces) {
62
+ strList *entry = timer->params.interfaces;
63
+ /* Skip unless we find our name in the requested list */
64
+ skip = true;
65
+
66
+ while (entry) {
67
+ if (!strcmp(entry->value, nic->ncs->name)) {
68
+ /* Found us */
69
+ skip = false;
70
+ break;
71
+ }
72
+ entry = entry->next;
73
+ }
74
+ } else {
75
+ skip = false;
76
+ }
77
+
78
+ trace_qemu_announce_self_iter(nic->ncs->name,
79
+ qemu_ether_ntoa(&nic->conf->macaddr), skip);
80
81
- trace_qemu_announce_self_iter(qemu_ether_ntoa(&nic->conf->macaddr));
82
- len = announce_self_create(buf, nic->conf->macaddr.a);
83
+ if (!skip) {
84
+ len = announce_self_create(buf, nic->conf->macaddr.a);
85
86
- qemu_send_packet_raw(qemu_get_queue(nic), buf, len);
87
+ qemu_send_packet_raw(qemu_get_queue(nic), buf, len);
88
89
- /* if the NIC provides it's own announcement support, use it as well */
90
- if (nic->ncs->info->announce) {
91
- nic->ncs->info->announce(nic->ncs);
92
+ /* if the NIC provides it's own announcement support, use it as well */
93
+ if (nic->ncs->info->announce) {
94
+ nic->ncs->info->announce(nic->ncs);
95
+ }
96
}
97
}
98
static void qemu_announce_self_once(void *opaque)
99
{
100
AnnounceTimer *timer = (AnnounceTimer *)opaque;
101
102
- qemu_foreach_nic(qemu_announce_self_iter, NULL);
103
+ qemu_foreach_nic(qemu_announce_self_iter, timer);
104
105
if (--timer->round) {
106
qemu_announce_timer_step(timer);
107
diff --git a/net/trace-events b/net/trace-events
108
index XXXXXXX..XXXXXXX 100644
109
--- a/net/trace-events
110
+++ b/net/trace-events
111
@@ -XXX,XX +XXX,XX @@
112
# See docs/devel/tracing.txt for syntax documentation.
113
114
# announce.c
115
-qemu_announce_self_iter(const char *mac) "%s"
116
+qemu_announce_self_iter(const char *name, const char *mac, int skip) "%s:%s skip: %d"
117
118
# vhost-user.c
119
vhost_user_event(const char *chr, int event) "chr: %s got event: %d"
120
diff --git a/qapi/net.json b/qapi/net.json
121
index XXXXXXX..XXXXXXX 100644
122
--- a/qapi/net.json
123
+++ b/qapi/net.json
124
@@ -XXX,XX +XXX,XX @@
125
#
126
# @step: Delay increase (in ms) after each self-announcement attempt
127
#
128
+# @interfaces: An optional list of interface names, which restricts the
129
+# announcement to the listed interfaces. (Since 4.1)
130
+#
131
# Since: 4.0
132
##
133
134
@@ -XXX,XX +XXX,XX @@
135
'data': { 'initial': 'int',
136
'max': 'int',
137
'rounds': 'int',
138
- 'step': 'int' } }
139
+ 'step': 'int',
140
+ '*interfaces': ['str'] } }
141
142
##
143
# @announce-self:
144
@@ -XXX,XX +XXX,XX @@
145
#
146
# Example:
147
#
148
-# -> { "execute": "announce-self"
149
+# -> { "execute": "announce-self",
150
# "arguments": {
151
-# "initial": 50, "max": 550, "rounds": 10, "step": 50 } }
152
+# "initial": 50, "max": 550, "rounds": 10, "step": 50,
153
+# "interfaces": ["vn2", "vn3"] } }
154
# <- { "return": {} }
155
#
156
# Since: 4.0
157
--
158
2.5.0
159
160
diff view generated by jsdifflib
Deleted patch
1
From: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
2
1
3
Add the optional ID to the HMP command.
4
5
e.g.
6
# start an announce for a long time on eth1
7
migrate_set_parameter announce-rounds 1000
8
announce_self "eth1" e1
9
10
# start an announce on eth2
11
announce_self "eth2" e2
12
13
# Change e1 to be announcing on eth1 and eth3
14
announce_self "eth1,eth3" e1
15
16
# Cancel e1
17
migrate_set_parameter announce-rounds 0
18
announce_self "" e1
19
20
Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
21
Signed-off-by: Jason Wang <jasowang@redhat.com>
22
---
23
hmp-commands.hx | 7 ++++---
24
monitor/hmp-cmds.c | 3 +++
25
2 files changed, 7 insertions(+), 3 deletions(-)
26
27
diff --git a/hmp-commands.hx b/hmp-commands.hx
28
index XXXXXXX..XXXXXXX 100644
29
--- a/hmp-commands.hx
30
+++ b/hmp-commands.hx
31
@@ -XXX,XX +XXX,XX @@ ETEXI
32
33
{
34
.name = "announce_self",
35
- .args_type = "interfaces:s?",
36
- .params = "[interfaces]",
37
+ .args_type = "interfaces:s?,id:s?",
38
+ .params = "[interfaces] [id]",
39
.help = "Trigger GARP/RARP announcements",
40
.cmd = hmp_announce_self,
41
},
42
@@ -XXX,XX +XXX,XX @@ Trigger a round of GARP/RARP broadcasts; this is useful for explicitly updating
43
network infrastructure after a reconfiguration or some forms of migration.
44
The timings of the round are set by the migration announce parameters.
45
An optional comma separated @var{interfaces} list restricts the announce to the
46
-named set of interfaces.
47
+named set of interfaces. An optional @var{id} can be used to start a separate announce
48
+timer and to change the parameters of it later.
49
ETEXI
50
51
{
52
diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c
53
index XXXXXXX..XXXXXXX 100644
54
--- a/monitor/hmp-cmds.c
55
+++ b/monitor/hmp-cmds.c
56
@@ -XXX,XX +XXX,XX @@ void hmp_info_snapshots(Monitor *mon, const QDict *qdict)
57
void hmp_announce_self(Monitor *mon, const QDict *qdict)
58
{
59
const char *interfaces_str = qdict_get_try_str(qdict, "interfaces");
60
+ const char *id = qdict_get_try_str(qdict, "id");
61
AnnounceParameters *params = QAPI_CLONE(AnnounceParameters,
62
migrate_announce_params());
63
64
qapi_free_strList(params->interfaces);
65
params->interfaces = strList_from_comma_list(interfaces_str);
66
params->has_interfaces = params->interfaces != NULL;
67
+ params->id = g_strdup(id);
68
+ params->has_id = !!params->id;
69
qmp_announce_self(params, NULL);
70
qapi_free_AnnounceParameters(params);
71
}
72
--
73
2.5.0
74
75
diff view generated by jsdifflib
Deleted patch
1
From: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
2
1
3
Expand self-announce test to check we can stop an announce timer.
4
We set it up to send 300 packets, but after we receive
5
the first one we tell it to stop.
6
7
We error if:
8
a) We receive more than 30 of the packets
9
b) We're still receiving packets after a lot longer than the
10
30 seconds should have arrived
11
12
Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
13
Signed-off-by: Jason Wang <jasowang@redhat.com>
14
---
15
tests/virtio-net-test.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++---
16
1 file changed, 54 insertions(+), 3 deletions(-)
17
18
diff --git a/tests/virtio-net-test.c b/tests/virtio-net-test.c
19
index XXXXXXX..XXXXXXX 100644
20
--- a/tests/virtio-net-test.c
21
+++ b/tests/virtio-net-test.c
22
@@ -XXX,XX +XXX,XX @@ static void announce_self(void *obj, void *data, QGuestAllocator *t_alloc)
23
QDict *rsp;
24
int ret;
25
uint16_t *proto = (uint16_t *)&buffer[12];
26
+ size_t total_received = 0;
27
+ uint64_t start, now, last_rxt, deadline;
28
29
+ /* Send a set of packets over a few second period */
30
rsp = qmp("{ 'execute' : 'announce-self', "
31
" 'arguments': {"
32
- " 'initial': 50, 'max': 550,"
33
- " 'rounds': 10, 'step': 50 } }");
34
+ " 'initial': 20, 'max': 100,"
35
+ " 'rounds': 300, 'step': 10, 'id': 'bob' } }");
36
assert(!qdict_haskey(rsp, "error"));
37
qobject_unref(rsp);
38
39
- /* Catch the packet and make sure it's a RARP */
40
+ /* Catch the first packet and make sure it's a RARP */
41
ret = qemu_recv(sv[0], &len, sizeof(len), 0);
42
g_assert_cmpint(ret, ==, sizeof(len));
43
len = ntohl(len);
44
45
ret = qemu_recv(sv[0], buffer, len, 0);
46
g_assert_cmpint(*proto, ==, htons(ETH_P_RARP));
47
+
48
+ /*
49
+ * Stop the announcment by settings rounds to 0 on the
50
+ * existing timer.
51
+ */
52
+ rsp = qmp("{ 'execute' : 'announce-self', "
53
+ " 'arguments': {"
54
+ " 'initial': 20, 'max': 100,"
55
+ " 'rounds': 0, 'step': 10, 'id': 'bob' } }");
56
+ assert(!qdict_haskey(rsp, "error"));
57
+ qobject_unref(rsp);
58
+
59
+ /* Now make sure the packets stop */
60
+
61
+ /* Times are in us */
62
+ start = g_get_monotonic_time();
63
+ /* 30 packets, max gap 100ms, * 4 for wiggle */
64
+ deadline = start + 1000 * (100 * 30 * 4);
65
+ last_rxt = start;
66
+
67
+ while (true) {
68
+ int saved_err;
69
+ ret = qemu_recv(sv[0], buffer, 60, MSG_DONTWAIT);
70
+ saved_err = errno;
71
+ now = g_get_monotonic_time();
72
+ g_assert_cmpint(now, <, deadline);
73
+
74
+ if (ret >= 0) {
75
+ if (ret) {
76
+ last_rxt = now;
77
+ }
78
+ total_received += ret;
79
+
80
+ /* Check it's not spewing loads */
81
+ g_assert_cmpint(total_received, <, 60 * 30 * 2);
82
+ } else {
83
+ g_assert_cmpint(saved_err, ==, EAGAIN);
84
+
85
+ /* 400ms, i.e. 4 worst case gaps */
86
+ if ((now - last_rxt) > (1000 * 100 * 4)) {
87
+ /* Nothings arrived for a while - must have stopped */
88
+ break;
89
+ };
90
+
91
+ /* 100ms */
92
+ g_usleep(1000 * 100);
93
+ }
94
+ };
95
}
96
97
static void virtio_net_test_cleanup(void *sockets)
98
--
99
2.5.0
100
101
diff view generated by jsdifflib
Deleted patch
1
From: Zhang Chen <chen.zhang@intel.com>
2
1
3
We add the "notify_dev=chardevID" parameter. After that colo-compare can connect with
4
remote(currently just for Xen, KVM-COLO didn't need it.) colo-frame through chardev socket,
5
it can notify remote(Xen) colo-frame to handle checkpoint event.
6
7
Signed-off-by: Zhang Chen <chen.zhang@intel.com>
8
Signed-off-by: Jason Wang <jasowang@redhat.com>
9
---
10
net/colo-compare.c | 21 +++++++++++++++++++++
11
qemu-options.hx | 33 ++++++++++++++++++++++++++++++++-
12
2 files changed, 53 insertions(+), 1 deletion(-)
13
14
diff --git a/net/colo-compare.c b/net/colo-compare.c
15
index XXXXXXX..XXXXXXX 100644
16
--- a/net/colo-compare.c
17
+++ b/net/colo-compare.c
18
@@ -XXX,XX +XXX,XX @@ typedef struct CompareState {
19
char *pri_indev;
20
char *sec_indev;
21
char *outdev;
22
+ char *notify_dev;
23
CharBackend chr_pri_in;
24
CharBackend chr_sec_in;
25
CharBackend chr_out;
26
@@ -XXX,XX +XXX,XX @@ static void compare_set_vnet_hdr(Object *obj,
27
s->vnet_hdr = value;
28
}
29
30
+static char *compare_get_notify_dev(Object *obj, Error **errp)
31
+{
32
+ CompareState *s = COLO_COMPARE(obj);
33
+
34
+ return g_strdup(s->notify_dev);
35
+}
36
+
37
+static void compare_set_notify_dev(Object *obj, const char *value, Error **errp)
38
+{
39
+ CompareState *s = COLO_COMPARE(obj);
40
+
41
+ g_free(s->notify_dev);
42
+ s->notify_dev = g_strdup(value);
43
+}
44
+
45
static void compare_pri_rs_finalize(SocketReadState *pri_rs)
46
{
47
CompareState *s = container_of(pri_rs, CompareState, pri_rs);
48
@@ -XXX,XX +XXX,XX @@ static void colo_compare_init(Object *obj)
49
(Object **)&s->iothread,
50
object_property_allow_set_link,
51
OBJ_PROP_LINK_STRONG, NULL);
52
+ /* This parameter just for Xen COLO */
53
+ object_property_add_str(obj, "notify_dev",
54
+ compare_get_notify_dev, compare_set_notify_dev,
55
+ NULL);
56
57
s->vnet_hdr = false;
58
object_property_add_bool(obj, "vnet_hdr_support", compare_get_vnet_hdr,
59
@@ -XXX,XX +XXX,XX @@ static void colo_compare_finalize(Object *obj)
60
g_free(s->pri_indev);
61
g_free(s->sec_indev);
62
g_free(s->outdev);
63
+ g_free(s->notify_dev);
64
}
65
66
static const TypeInfo colo_compare_info = {
67
diff --git a/qemu-options.hx b/qemu-options.hx
68
index XXXXXXX..XXXXXXX 100644
69
--- a/qemu-options.hx
70
+++ b/qemu-options.hx
71
@@ -XXX,XX +XXX,XX @@ Dump the network traffic on netdev @var{dev} to the file specified by
72
The file format is libpcap, so it can be analyzed with tools such as tcpdump
73
or Wireshark.
74
75
-@item -object colo-compare,id=@var{id},primary_in=@var{chardevid},secondary_in=@var{chardevid},outdev=@var{chardevid},iothread=@var{id}[,vnet_hdr_support]
76
+@item -object colo-compare,id=@var{id},primary_in=@var{chardevid},secondary_in=@var{chardevid},outdev=@var{chardevid},iothread=@var{id}[,vnet_hdr_support][,notify_dev=@var{id}]
77
78
Colo-compare gets packet from primary_in@var{chardevid} and secondary_in@var{chardevid}, than compare primary packet with
79
secondary packet. If the packets are same, we will output primary
80
@@ -XXX,XX +XXX,XX @@ do checkpoint and send primary packet to outdev@var{chardevid}.
81
In order to improve efficiency, we need to put the task of comparison
82
in another thread. If it has the vnet_hdr_support flag, colo compare
83
will send/recv packet with vnet_hdr_len.
84
+If you want to use Xen COLO, will need the notify_dev to notify Xen
85
+colo-frame to do checkpoint.
86
87
we must use it with the help of filter-mirror and filter-redirector.
88
89
@example
90
91
+KVM COLO
92
+
93
primary:
94
-netdev tap,id=hn0,vhost=off,script=/etc/qemu-ifup,downscript=/etc/qemu-ifdown
95
-device e1000,id=e0,netdev=hn0,mac=52:a4:00:12:78:66
96
@@ -XXX,XX +XXX,XX @@ secondary:
97
-object filter-redirector,id=f1,netdev=hn0,queue=tx,indev=red0
98
-object filter-redirector,id=f2,netdev=hn0,queue=rx,outdev=red1
99
100
+
101
+Xen COLO
102
+
103
+primary:
104
+-netdev tap,id=hn0,vhost=off,script=/etc/qemu-ifup,downscript=/etc/qemu-ifdown
105
+-device e1000,id=e0,netdev=hn0,mac=52:a4:00:12:78:66
106
+-chardev socket,id=mirror0,host=3.3.3.3,port=9003,server,nowait
107
+-chardev socket,id=compare1,host=3.3.3.3,port=9004,server,nowait
108
+-chardev socket,id=compare0,host=3.3.3.3,port=9001,server,nowait
109
+-chardev socket,id=compare0-0,host=3.3.3.3,port=9001
110
+-chardev socket,id=compare_out,host=3.3.3.3,port=9005,server,nowait
111
+-chardev socket,id=compare_out0,host=3.3.3.3,port=9005
112
+-chardev socket,id=notify_way,host=3.3.3.3,port=9009,server,nowait
113
+-object filter-mirror,id=m0,netdev=hn0,queue=tx,outdev=mirror0
114
+-object filter-redirector,netdev=hn0,id=redire0,queue=rx,indev=compare_out
115
+-object filter-redirector,netdev=hn0,id=redire1,queue=rx,outdev=compare0
116
+-object iothread,id=iothread1
117
+-object colo-compare,id=comp0,primary_in=compare0-0,secondary_in=compare1,outdev=compare_out0,notify_dev=nofity_way,iothread=iothread1
118
+
119
+secondary:
120
+-netdev tap,id=hn0,vhost=off,script=/etc/qemu-ifup,down script=/etc/qemu-ifdown
121
+-device e1000,netdev=hn0,mac=52:a4:00:12:78:66
122
+-chardev socket,id=red0,host=3.3.3.3,port=9003
123
+-chardev socket,id=red1,host=3.3.3.3,port=9004
124
+-object filter-redirector,id=f1,netdev=hn0,queue=tx,indev=red0
125
+-object filter-redirector,id=f2,netdev=hn0,queue=rx,outdev=red1
126
+
127
@end example
128
129
If you want to know the detail of above command line, you can read
130
--
131
2.5.0
132
133
diff view generated by jsdifflib
Deleted patch
1
From: Zhang Chen <chen.zhang@intel.com>
2
1
3
Add chardev handler to send notification to remote(current from Xen) colo-frame.
4
5
Signed-off-by: Zhang Chen <chen.zhang@intel.com>
6
Signed-off-by: Jason Wang <jasowang@redhat.com>
7
---
8
net/colo-compare.c | 39 +++++++++++++++++++++++++++++++++++++++
9
1 file changed, 39 insertions(+)
10
11
diff --git a/net/colo-compare.c b/net/colo-compare.c
12
index XXXXXXX..XXXXXXX 100644
13
--- a/net/colo-compare.c
14
+++ b/net/colo-compare.c
15
@@ -XXX,XX +XXX,XX @@ typedef struct CompareState {
16
CharBackend chr_pri_in;
17
CharBackend chr_sec_in;
18
CharBackend chr_out;
19
+ CharBackend chr_notify_dev;
20
SocketReadState pri_rs;
21
SocketReadState sec_rs;
22
+ SocketReadState notify_rs;
23
bool vnet_hdr;
24
25
/*
26
@@ -XXX,XX +XXX,XX @@ static void compare_sec_chr_in(void *opaque, const uint8_t *buf, int size)
27
}
28
}
29
30
+static void compare_notify_chr(void *opaque, const uint8_t *buf, int size)
31
+{
32
+ CompareState *s = COLO_COMPARE(opaque);
33
+ int ret;
34
+
35
+ ret = net_fill_rstate(&s->notify_rs, buf, size);
36
+ if (ret == -1) {
37
+ qemu_chr_fe_set_handlers(&s->chr_notify_dev, NULL, NULL, NULL, NULL,
38
+ NULL, NULL, true);
39
+ error_report("colo-compare notify_dev error");
40
+ }
41
+}
42
+
43
/*
44
* Check old packet regularly so it can watch for any packets
45
* that the secondary hasn't produced equivalents of.
46
@@ -XXX,XX +XXX,XX @@ static void colo_compare_iothread(CompareState *s)
47
qemu_chr_fe_set_handlers(&s->chr_sec_in, compare_chr_can_read,
48
compare_sec_chr_in, NULL, NULL,
49
s, s->worker_context, true);
50
+ if (s->notify_dev) {
51
+ qemu_chr_fe_set_handlers(&s->chr_notify_dev, compare_chr_can_read,
52
+ compare_notify_chr, NULL, NULL,
53
+ s, s->worker_context, true);
54
+ }
55
56
colo_compare_timer_init(s);
57
s->event_bh = qemu_bh_new(colo_compare_handle_event, s);
58
@@ -XXX,XX +XXX,XX @@ static void compare_sec_rs_finalize(SocketReadState *sec_rs)
59
}
60
}
61
62
+static void compare_notify_rs_finalize(SocketReadState *notify_rs)
63
+{
64
+ /* Get Xen colo-frame's notify and handle the message */
65
+}
66
67
/*
68
* Return 0 is success.
69
@@ -XXX,XX +XXX,XX @@ static void colo_compare_complete(UserCreatable *uc, Error **errp)
70
net_socket_rs_init(&s->pri_rs, compare_pri_rs_finalize, s->vnet_hdr);
71
net_socket_rs_init(&s->sec_rs, compare_sec_rs_finalize, s->vnet_hdr);
72
73
+ /* Try to enable remote notify chardev, currently just for Xen COLO */
74
+ if (s->notify_dev) {
75
+ if (find_and_check_chardev(&chr, s->notify_dev, errp) ||
76
+ !qemu_chr_fe_init(&s->chr_notify_dev, chr, errp)) {
77
+ return;
78
+ }
79
+
80
+ net_socket_rs_init(&s->notify_rs, compare_notify_rs_finalize,
81
+ s->vnet_hdr);
82
+ }
83
+
84
QTAILQ_INSERT_TAIL(&net_compares, s, next);
85
86
g_queue_init(&s->conn_list);
87
@@ -XXX,XX +XXX,XX @@ static void colo_compare_finalize(Object *obj)
88
qemu_chr_fe_deinit(&s->chr_pri_in, false);
89
qemu_chr_fe_deinit(&s->chr_sec_in, false);
90
qemu_chr_fe_deinit(&s->chr_out, false);
91
+ if (s->notify_dev) {
92
+ qemu_chr_fe_deinit(&s->chr_notify_dev, false);
93
+ }
94
+
95
if (s->iothread) {
96
colo_compare_timer_del(s);
97
}
98
--
99
2.5.0
100
101
diff view generated by jsdifflib