1
The following changes since commit 92f8c6fef13b31ba222c4d20ad8afd2b79c4c28e:
1
The following changes since commit 99d6b11b5b44d7dd64f4cb1973184e40a4a174f8:
2
2
3
Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20210525' into staging (2021-05-25 16:17:06 +0100)
3
Merge tag 'pull-target-arm-20220922' of https://git.linaro.org/people/pmaydell/qemu-arm into staging (2022-09-26 13:38:26 -0400)
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 90322e646e87c1440661cb3ddbc0cc94309d8a4f:
9
for you to fetch changes up to bf769f742c3624952f125b303878a77ea870c156:
10
10
11
MAINTAINERS: Added eBPF maintainers information. (2021-06-04 15:25:46 +0800)
11
virtio: del net client if net_init_tap_one failed (2022-09-27 15:14:37 +0800)
12
12
13
----------------------------------------------------------------
13
----------------------------------------------------------------
14
14
15
----------------------------------------------------------------
15
----------------------------------------------------------------
16
Andrew Melnychenko (7):
16
Ding Hui (1):
17
net/tap: Added TUNSETSTEERINGEBPF code.
17
e1000e: set RX desc status with DD flag in a separate operation
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.
24
18
25
MAINTAINERS | 8 +
19
Eugenio Pérez (6):
26
configure | 8 +-
20
vdpa: Make VhostVDPAState cvq_cmd_in_buffer control ack type
27
docs/devel/ebpf_rss.rst | 125 +++++++++
21
vdpa: extract vhost_vdpa_net_load_mac from vhost_vdpa_net_load
28
docs/devel/index.rst | 1 +
22
vdpa: Add vhost_vdpa_net_load_mq
29
ebpf/ebpf_rss-stub.c | 40 +++
23
vdpa: validate MQ CVQ commands
30
ebpf/ebpf_rss.c | 165 ++++++++++++
24
virtio-net: Update virtio-net curr_queue_pairs in vdpa backends
31
ebpf/ebpf_rss.h | 44 ++++
25
vdpa: Allow MQ feature in SVQ
32
ebpf/meson.build | 1 +
26
33
ebpf/rss.bpf.skeleton.h | 431 +++++++++++++++++++++++++++++++
27
lu zhipeng (1):
34
ebpf/trace-events | 4 +
28
virtio: del net client if net_init_tap_one failed
35
ebpf/trace.h | 1 +
29
36
hw/net/vhost_net.c | 3 +
30
hw/net/e1000e_core.c | 53 ++++++++++++++++++++++-
37
hw/net/virtio-net.c | 116 ++++++++-
31
hw/net/virtio-net.c | 17 +++-----
38
include/hw/virtio/virtio-net.h | 4 +
32
net/tap.c | 18 +++++---
39
include/net/net.h | 2 +
33
net/vhost-vdpa.c | 119 +++++++++++++++++++++++++++++++++++++--------------
40
meson.build | 23 ++
34
4 files changed, 157 insertions(+), 50 deletions(-)
41
meson_options.txt | 2 +
35
42
net/tap-bsd.c | 5 +
36
Ding Hui (1):
43
net/tap-linux.c | 13 +
37
e1000e: set RX desc status with DD flag in a separate operation
44
net/tap-linux.h | 1 +
38
45
net/tap-solaris.c | 5 +
39
Eugenio Pérez (6):
46
net/tap-stub.c | 5 +
40
vdpa: Make VhostVDPAState cvq_cmd_in_buffer control ack type
47
net/tap.c | 9 +
41
vdpa: extract vhost_vdpa_net_load_mac from vhost_vdpa_net_load
48
net/tap_int.h | 1 +
42
vdpa: Add vhost_vdpa_net_load_mq
49
net/vhost-vdpa.c | 2 +
43
vdpa: validate MQ CVQ commands
50
tools/ebpf/Makefile.ebpf | 21 ++
44
virtio-net: Update virtio-net curr_queue_pairs in vdpa backends
51
tools/ebpf/rss.bpf.c | 571 +++++++++++++++++++++++++++++++++++++++++
45
vdpa: Allow MQ feature in SVQ
52
27 files changed, 1607 insertions(+), 4 deletions(-)
46
53
create mode 100644 docs/devel/ebpf_rss.rst
47
lu zhipeng (1):
54
create mode 100644 ebpf/ebpf_rss-stub.c
48
virtio: del net client if net_init_tap_one failed
55
create mode 100644 ebpf/ebpf_rss.c
49
56
create mode 100644 ebpf/ebpf_rss.h
50
hw/net/e1000e_core.c | 53 ++++++++++++++++++++++-
57
create mode 100644 ebpf/meson.build
51
hw/net/virtio-net.c | 17 +++-----
58
create mode 100644 ebpf/rss.bpf.skeleton.h
52
net/tap.c | 18 +++++---
59
create mode 100644 ebpf/trace-events
53
net/vhost-vdpa.c | 119 +++++++++++++++++++++++++++++++++++++--------------
60
create mode 100644 ebpf/trace.h
54
4 files changed, 157 insertions(+), 50 deletions(-)
61
create mode 100755 tools/ebpf/Makefile.ebpf
55
62
create mode 100644 tools/ebpf/rss.bpf.c
56
--
57
2.7.4
63
58
64
59
diff view generated by jsdifflib
1
From: Andrew Melnychenko <andrew@daynix.com>
1
From: Ding Hui <dinghui@sangfor.com.cn>
2
2
3
Added function that loads RSS eBPF program.
3
Like commit 034d00d48581 ("e1000: set RX descriptor status in
4
Added stub functions for RSS eBPF loader.
4
a separate operation"), there is also same issue in e1000e, which
5
Added meson and configuration options.
5
would cause lost packets or stop sending packets to VM with DPDK.
6
6
7
By default, eBPF feature enabled if libbpf is present in the build system.
7
Do similar fix in e1000e.
8
libbpf checked in configuration shell script and meson script.
9
8
10
Signed-off-by: Yuri Benditovich <yuri.benditovich@daynix.com>
9
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/402
11
Signed-off-by: Andrew Melnychenko <andrew@daynix.com>
10
Signed-off-by: Ding Hui <dinghui@sangfor.com.cn>
12
Signed-off-by: Jason Wang <jasowang@redhat.com>
11
Signed-off-by: Jason Wang <jasowang@redhat.com>
13
---
12
---
14
configure | 8 +-
13
hw/net/e1000e_core.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++-
15
ebpf/ebpf_rss-stub.c | 40 +++++
14
1 file changed, 52 insertions(+), 1 deletion(-)
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
32
15
33
diff --git a/configure b/configure
16
diff --git a/hw/net/e1000e_core.c b/hw/net/e1000e_core.c
34
index XXXXXXX..XXXXXXX 100755
17
index XXXXXXX..XXXXXXX 100644
35
--- a/configure
18
--- a/hw/net/e1000e_core.c
36
+++ b/configure
19
+++ b/hw/net/e1000e_core.c
37
@@ -XXX,XX +XXX,XX @@ vhost_vsock="$default_feature"
20
@@ -XXX,XX +XXX,XX @@ struct NetRxPkt *pkt, const E1000E_RSSInfo *rss_info,
38
vhost_user="no"
21
}
39
vhost_user_blk_server="auto"
22
}
40
vhost_user_fs="$default_feature"
23
41
+bpf="auto"
24
+static inline void
42
kvm="auto"
25
+e1000e_pci_dma_write_rx_desc(E1000ECore *core, dma_addr_t addr,
43
hax="auto"
26
+ uint8_t *desc, dma_addr_t len)
44
hvf="auto"
27
+{
45
@@ -XXX,XX +XXX,XX @@ for opt do
28
+ PCIDevice *dev = core->owner;
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
+
29
+
91
+#include "qemu/osdep.h"
30
+ if (e1000e_rx_use_legacy_descriptor(core)) {
92
+#include "ebpf/ebpf_rss.h"
31
+ struct e1000_rx_desc *d = (struct e1000_rx_desc *) desc;
32
+ size_t offset = offsetof(struct e1000_rx_desc, status);
33
+ uint8_t status = d->status;
93
+
34
+
94
+void ebpf_rss_init(struct EBPFRSSContext *ctx)
35
+ d->status &= ~E1000_RXD_STAT_DD;
95
+{
36
+ pci_dma_write(dev, addr, desc, len);
96
+
37
+
97
+}
38
+ if (status & E1000_RXD_STAT_DD) {
39
+ d->status = status;
40
+ pci_dma_write(dev, addr + offset, &status, sizeof(status));
41
+ }
42
+ } else {
43
+ if (core->mac[RCTL] & E1000_RCTL_DTYP_PS) {
44
+ union e1000_rx_desc_packet_split *d =
45
+ (union e1000_rx_desc_packet_split *) desc;
46
+ size_t offset = offsetof(union e1000_rx_desc_packet_split,
47
+ wb.middle.status_error);
48
+ uint32_t status = d->wb.middle.status_error;
98
+
49
+
99
+bool ebpf_rss_is_loaded(struct EBPFRSSContext *ctx)
50
+ d->wb.middle.status_error &= ~E1000_RXD_STAT_DD;
100
+{
51
+ pci_dma_write(dev, addr, desc, len);
101
+ return false;
102
+}
103
+
52
+
104
+bool ebpf_rss_load(struct EBPFRSSContext *ctx)
53
+ if (status & E1000_RXD_STAT_DD) {
105
+{
54
+ d->wb.middle.status_error = status;
106
+ return false;
55
+ pci_dma_write(dev, addr + offset, &status, sizeof(status));
107
+}
56
+ }
57
+ } else {
58
+ union e1000_rx_desc_extended *d =
59
+ (union e1000_rx_desc_extended *) desc;
60
+ size_t offset = offsetof(union e1000_rx_desc_extended,
61
+ wb.upper.status_error);
62
+ uint32_t status = d->wb.upper.status_error;
108
+
63
+
109
+bool ebpf_rss_set_all(struct EBPFRSSContext *ctx, struct EBPFRSSConfig *config,
64
+ d->wb.upper.status_error &= ~E1000_RXD_STAT_DD;
110
+ uint16_t *indirections_table, uint8_t *toeplitz_key)
65
+ pci_dma_write(dev, addr, desc, len);
111
+{
112
+ return false;
113
+}
114
+
66
+
115
+void ebpf_rss_unload(struct EBPFRSSContext *ctx)
67
+ if (status & E1000_RXD_STAT_DD) {
116
+{
68
+ d->wb.upper.status_error = status;
117
+
69
+ pci_dma_write(dev, addr + offset, &status, sizeof(status));
118
+}
70
+ }
119
diff --git a/ebpf/ebpf_rss.c b/ebpf/ebpf_rss.c
71
+ }
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
+ }
72
+ }
155
+}
73
+}
156
+
74
+
157
+bool ebpf_rss_is_loaded(struct EBPFRSSContext *ctx)
75
typedef struct e1000e_ba_state_st {
158
+{
76
uint16_t written[MAX_PS_BUFFERS];
159
+ return ctx != NULL && ctx->obj != NULL;
77
uint8_t cur_idx;
160
+}
78
@@ -XXX,XX +XXX,XX @@ e1000e_write_packet_to_guest(E1000ECore *core, struct NetRxPkt *pkt,
161
+
79
162
+bool ebpf_rss_load(struct EBPFRSSContext *ctx)
80
e1000e_write_rx_descr(core, desc, is_last ? core->rx_pkt : NULL,
163
+{
81
rss_info, do_ps ? ps_hdr_len : 0, &bastate.written);
164
+ struct rss_bpf *rss_bpf_ctx;
82
- pci_dma_write(d, base, &desc, core->rx_desc_len);
165
+
83
+ e1000e_pci_dma_write_rx_desc(core, base, desc, core->rx_desc_len);
166
+ if (ctx == NULL) {
84
167
+ return false;
85
e1000e_ring_advance(core, rxi,
168
+ }
86
core->rx_desc_len / E1000_MIN_RX_DESC_LEN);
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
802
index XXXXXXX..XXXXXXX 100644
803
--- a/meson.build
804
+++ b/meson.build
805
@@ -XXX,XX +XXX,XX @@ if not get_option('fuse_lseek').disabled()
806
endif
807
endif
808
809
+# libbpf
810
+libbpf = dependency('libbpf', required: get_option('bpf'), method: 'pkg-config')
811
+if libbpf.found() and not cc.links('''
812
+ #include <bpf/libbpf.h>
813
+ int main(void)
814
+ {
815
+ bpf_object__destroy_skeleton(NULL);
816
+ return 0;
817
+ }''', dependencies: libbpf)
818
+ libbpf = not_found
819
+ if get_option('bpf').enabled()
820
+ error('libbpf skeleton test failed')
821
+ else
822
+ warning('libbpf skeleton test failed, disabling')
823
+ endif
824
+endif
825
+
826
if get_option('cfi')
827
cfi_flags=[]
828
# Check for dependency on LTO
829
@@ -XXX,XX +XXX,XX @@ endif
830
config_host_data.set('CONFIG_GTK', gtk.found())
831
config_host_data.set('CONFIG_LIBATTR', have_old_libattr)
832
config_host_data.set('CONFIG_LIBCAP_NG', libcap_ng.found())
833
+config_host_data.set('CONFIG_EBPF', libbpf.found())
834
config_host_data.set('CONFIG_LIBISCSI', libiscsi.found())
835
config_host_data.set('CONFIG_LIBNFS', libnfs.found())
836
config_host_data.set('CONFIG_RBD', rbd.found())
837
@@ -XXX,XX +XXX,XX @@ if have_system
838
'backends',
839
'backends/tpm',
840
'chardev',
841
+ 'ebpf',
842
'hw/9pfs',
843
'hw/acpi',
844
'hw/adc',
845
@@ -XXX,XX +XXX,XX @@ subdir('accel')
846
subdir('plugins')
847
subdir('bsd-user')
848
subdir('linux-user')
849
+subdir('ebpf')
850
+
851
+common_ss.add(libbpf)
852
853
bsd_user_ss.add(files('gdbstub.c'))
854
specific_ss.add_all(when: 'CONFIG_BSD_USER', if_true: bsd_user_ss)
855
@@ -XXX,XX +XXX,XX @@ summary_info += {'RDMA support': config_host.has_key('CONFIG_RDMA')}
856
summary_info += {'PVRDMA support': config_host.has_key('CONFIG_PVRDMA')}
857
summary_info += {'fdt support': fdt_opt == 'disabled' ? false : fdt_opt}
858
summary_info += {'libcap-ng support': libcap_ng.found()}
859
+summary_info += {'bpf support': libbpf.found()}
860
# TODO: add back protocol and server version
861
summary_info += {'spice support': config_host.has_key('CONFIG_SPICE')}
862
summary_info += {'rbd support': rbd.found()}
863
diff --git a/meson_options.txt b/meson_options.txt
864
index XXXXXXX..XXXXXXX 100644
865
--- a/meson_options.txt
866
+++ b/meson_options.txt
867
@@ -XXX,XX +XXX,XX @@ option('bzip2', type : 'feature', value : 'auto',
868
description: 'bzip2 support for DMG images')
869
option('cap_ng', type : 'feature', value : 'auto',
870
description: 'cap_ng support')
871
+option('bpf', type : 'feature', value : 'auto',
872
+ description: 'eBPF support')
873
option('cocoa', type : 'feature', value : 'auto',
874
description: 'Cocoa user interface (macOS only)')
875
option('curl', type : 'feature', value : 'auto',
876
--
87
--
877
2.7.4
88
2.7.4
878
879
diff view generated by jsdifflib
1
From: Andrew Melnychenko <andrew@daynix.com>
1
From: Eugenio Pérez <eperezma@redhat.com>
2
2
3
Signed-off-by: Yuri Benditovich <yuri.benditovich@daynix.com>
3
This allows to simplify the code. Rename to status while we're at it.
4
Signed-off-by: Andrew Melnychenko <andrew@daynix.com>
4
5
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
5
Signed-off-by: Jason Wang <jasowang@redhat.com>
6
Signed-off-by: Jason Wang <jasowang@redhat.com>
6
---
7
---
7
MAINTAINERS | 8 ++++++++
8
net/vhost-vdpa.c | 23 ++++++++++++-----------
8
1 file changed, 8 insertions(+)
9
1 file changed, 12 insertions(+), 11 deletions(-)
9
10
10
diff --git a/MAINTAINERS b/MAINTAINERS
11
diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c
11
index XXXXXXX..XXXXXXX 100644
12
index XXXXXXX..XXXXXXX 100644
12
--- a/MAINTAINERS
13
--- a/net/vhost-vdpa.c
13
+++ b/MAINTAINERS
14
+++ b/net/vhost-vdpa.c
14
@@ -XXX,XX +XXX,XX @@ F: include/hw/remote/proxy-memory-listener.h
15
@@ -XXX,XX +XXX,XX @@ typedef struct VhostVDPAState {
15
F: hw/remote/iohub.c
16
VHostNetState *vhost_net;
16
F: include/hw/remote/iohub.h
17
17
18
/* Control commands shadow buffers */
18
+EBPF:
19
- void *cvq_cmd_out_buffer, *cvq_cmd_in_buffer;
19
+M: Jason Wang <jasowang@redhat.com>
20
+ void *cvq_cmd_out_buffer;
20
+R: Andrew Melnychenko <andrew@daynix.com>
21
+ virtio_net_ctrl_ack *status;
21
+R: Yuri Benditovich <yuri.benditovich@daynix.com>
22
+S: Maintained
23
+F: ebpf/*
24
+F: tools/ebpf/*
25
+
22
+
26
Build and test automation
23
bool started;
27
-------------------------
24
} VhostVDPAState;
28
Build and test automation, general continuous integration
25
26
@@ -XXX,XX +XXX,XX @@ static void vhost_vdpa_cleanup(NetClientState *nc)
27
struct vhost_dev *dev = &s->vhost_net->dev;
28
29
qemu_vfree(s->cvq_cmd_out_buffer);
30
- qemu_vfree(s->cvq_cmd_in_buffer);
31
+ qemu_vfree(s->status);
32
if (dev->vq_index + dev->nvqs == dev->vq_index_end) {
33
g_clear_pointer(&s->vhost_vdpa.iova_tree, vhost_iova_tree_delete);
34
}
35
@@ -XXX,XX +XXX,XX @@ static int vhost_vdpa_net_cvq_start(NetClientState *nc)
36
return r;
37
}
38
39
- r = vhost_vdpa_cvq_map_buf(&s->vhost_vdpa, s->cvq_cmd_in_buffer,
40
+ r = vhost_vdpa_cvq_map_buf(&s->vhost_vdpa, s->status,
41
vhost_vdpa_net_cvq_cmd_page_len(), true);
42
if (unlikely(r < 0)) {
43
vhost_vdpa_cvq_unmap_buf(&s->vhost_vdpa, s->cvq_cmd_out_buffer);
44
@@ -XXX,XX +XXX,XX @@ static void vhost_vdpa_net_cvq_stop(NetClientState *nc)
45
46
if (s->vhost_vdpa.shadow_vqs_enabled) {
47
vhost_vdpa_cvq_unmap_buf(&s->vhost_vdpa, s->cvq_cmd_out_buffer);
48
- vhost_vdpa_cvq_unmap_buf(&s->vhost_vdpa, s->cvq_cmd_in_buffer);
49
+ vhost_vdpa_cvq_unmap_buf(&s->vhost_vdpa, s->status);
50
}
51
}
52
53
@@ -XXX,XX +XXX,XX @@ static ssize_t vhost_vdpa_net_cvq_add(VhostVDPAState *s, size_t out_len,
54
.iov_len = out_len,
55
};
56
const struct iovec in = {
57
- .iov_base = s->cvq_cmd_in_buffer,
58
+ .iov_base = s->status,
59
.iov_len = sizeof(virtio_net_ctrl_ack),
60
};
61
VhostShadowVirtqueue *svq = g_ptr_array_index(s->vhost_vdpa.shadow_vqs, 0);
62
@@ -XXX,XX +XXX,XX @@ static int vhost_vdpa_net_load(NetClientState *nc)
63
return dev_written;
64
}
65
66
- return *((virtio_net_ctrl_ack *)s->cvq_cmd_in_buffer) != VIRTIO_NET_OK;
67
+ return *s->status != VIRTIO_NET_OK;
68
}
69
70
return 0;
71
@@ -XXX,XX +XXX,XX @@ static int vhost_vdpa_net_handle_ctrl_avail(VhostShadowVirtqueue *svq,
72
goto out;
73
}
74
75
- memcpy(&status, s->cvq_cmd_in_buffer, sizeof(status));
76
- if (status != VIRTIO_NET_OK) {
77
+ if (*s->status != VIRTIO_NET_OK) {
78
return VIRTIO_NET_ERR;
79
}
80
81
@@ -XXX,XX +XXX,XX @@ static NetClientState *net_vhost_vdpa_init(NetClientState *peer,
82
s->cvq_cmd_out_buffer = qemu_memalign(qemu_real_host_page_size(),
83
vhost_vdpa_net_cvq_cmd_page_len());
84
memset(s->cvq_cmd_out_buffer, 0, vhost_vdpa_net_cvq_cmd_page_len());
85
- s->cvq_cmd_in_buffer = qemu_memalign(qemu_real_host_page_size(),
86
- vhost_vdpa_net_cvq_cmd_page_len());
87
- memset(s->cvq_cmd_in_buffer, 0, vhost_vdpa_net_cvq_cmd_page_len());
88
+ s->status = qemu_memalign(qemu_real_host_page_size(),
89
+ vhost_vdpa_net_cvq_cmd_page_len());
90
+ memset(s->status, 0, vhost_vdpa_net_cvq_cmd_page_len());
91
92
s->vhost_vdpa.shadow_vq_ops = &vhost_vdpa_net_svq_ops;
93
s->vhost_vdpa.shadow_vq_ops_opaque = s;
29
--
94
--
30
2.7.4
95
2.7.4
31
96
32
97
diff view generated by jsdifflib
1
From: Andrew Melnychenko <andrew@daynix.com>
1
From: Eugenio Pérez <eperezma@redhat.com>
2
2
3
RSS program and Makefile to build it.
3
Since there may be many commands we need to issue to load the NIC
4
The bpftool used to generate '.h' file.
4
state, let's split them in individual functions
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
5
9
Signed-off-by: Yuri Benditovich <yuri.benditovich@daynix.com>
6
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
10
Signed-off-by: Andrew Melnychenko <andrew@daynix.com>
11
Signed-off-by: Jason Wang <jasowang@redhat.com>
7
Signed-off-by: Jason Wang <jasowang@redhat.com>
12
---
8
---
13
tools/ebpf/Makefile.ebpf | 21 ++
9
net/vhost-vdpa.c | 62 ++++++++++++++++++++++++++++++++++++--------------------
14
tools/ebpf/rss.bpf.c | 571 +++++++++++++++++++++++++++++++++++++++++++++++
10
1 file changed, 40 insertions(+), 22 deletions(-)
15
2 files changed, 592 insertions(+)
16
create mode 100755 tools/ebpf/Makefile.ebpf
17
create mode 100644 tools/ebpf/rss.bpf.c
18
11
19
diff --git a/tools/ebpf/Makefile.ebpf b/tools/ebpf/Makefile.ebpf
12
diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c
20
new file mode 100755
13
index XXXXXXX..XXXXXXX 100644
21
index XXXXXXX..XXXXXXX
14
--- a/net/vhost-vdpa.c
22
--- /dev/null
15
+++ b/net/vhost-vdpa.c
23
+++ b/tools/ebpf/Makefile.ebpf
16
@@ -XXX,XX +XXX,XX @@ static ssize_t vhost_vdpa_net_cvq_add(VhostVDPAState *s, size_t out_len,
24
@@ -XXX,XX +XXX,XX @@
17
return vhost_svq_poll(svq);
25
+OBJS = rss.bpf.o
18
}
19
20
+static ssize_t vhost_vdpa_net_load_cmd(VhostVDPAState *s, uint8_t class,
21
+ uint8_t cmd, const void *data,
22
+ size_t data_size)
23
+{
24
+ const struct virtio_net_ctrl_hdr ctrl = {
25
+ .class = class,
26
+ .cmd = cmd,
27
+ };
26
+
28
+
27
+LLC ?= llc
29
+ assert(data_size < vhost_vdpa_net_cvq_cmd_page_len() - sizeof(ctrl));
28
+CLANG ?= clang
29
+INC_FLAGS = `$(CLANG) -print-file-name=include`
30
+EXTRA_CFLAGS ?= -O2 -emit-llvm -fno-stack-protector
31
+
30
+
32
+all: $(OBJS)
31
+ memcpy(s->cvq_cmd_out_buffer, &ctrl, sizeof(ctrl));
32
+ memcpy(s->cvq_cmd_out_buffer + sizeof(ctrl), data, data_size);
33
+
33
+
34
+.PHONY: clean
34
+ return vhost_vdpa_net_cvq_add(s, sizeof(ctrl) + data_size,
35
+
35
+ sizeof(virtio_net_ctrl_ack));
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
69
+ */
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
+}
36
+}
159
+
37
+
160
+static inline
38
+static int vhost_vdpa_net_load_mac(VhostVDPAState *s, const VirtIONet *n)
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
+{
39
+{
194
+ switch (hdr_type) {
40
+ uint64_t features = n->parent_obj.guest_features;
195
+ case IPPROTO_HOPOPTS:
41
+ if (features & BIT_ULL(VIRTIO_NET_F_CTRL_MAC_ADDR)) {
196
+ case IPPROTO_ROUTING:
42
+ ssize_t dev_written = vhost_vdpa_net_load_cmd(s, VIRTIO_NET_CTRL_MAC,
197
+ case IPPROTO_FRAGMENT:
43
+ VIRTIO_NET_CTRL_MAC_ADDR_SET,
198
+ case IPPROTO_ICMPV6:
44
+ n->mac, sizeof(n->mac));
199
+ case IPPROTO_NONE:
45
+ if (unlikely(dev_written < 0)) {
200
+ case IPPROTO_DSTOPTS:
46
+ return dev_written;
201
+ case IPPROTO_MH:
202
+ return 1;
203
+ default:
204
+ return 0;
205
+ }
206
+}
207
+/*
208
+ * According to
209
+ * https://www.iana.org/assignments/ipv6-parameters/ipv6-parameters.xhtml
210
+ * we expect that there are would be no more than 11 extensions in IPv6 header,
211
+ * also there is 27 TLV options for Destination and Hop-by-hop extensions.
212
+ * Need to choose reasonable amount of maximum extensions/options we may
213
+ * check to find ext src/dst.
214
+ */
215
+#define IP6_EXTENSIONS_COUNT 11
216
+#define IP6_OPTIONS_COUNT 30
217
+
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
+ }
47
+ }
237
+
48
+
238
+ if (*l4_protocol == IPPROTO_ROUTING) {
49
+ return *s->status != VIRTIO_NET_OK;
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
+ }
50
+ }
309
+
51
+
310
+ return 0;
52
+ return 0;
311
+error:
312
+ return err;
313
+}
53
+}
314
+
54
+
315
+static __be16 parse_eth_type(struct __sk_buff *skb)
55
static int vhost_vdpa_net_load(NetClientState *nc)
316
+{
56
{
317
+ unsigned int offset = 12;
57
VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc);
318
+ __be16 ret = 0;
58
- const struct vhost_vdpa *v = &s->vhost_vdpa;
319
+ int err = 0;
59
+ struct vhost_vdpa *v = &s->vhost_vdpa;
320
+
60
const VirtIONet *n;
321
+ err = bpf_skb_load_bytes_relative(skb, offset, &ret, sizeof(ret),
61
- uint64_t features;
322
+ BPF_HDR_START_MAC);
62
+ int r;
323
+ if (err) {
63
324
+ return 0;
64
assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA);
325
+ }
65
326
+
66
@@ -XXX,XX +XXX,XX @@ static int vhost_vdpa_net_load(NetClientState *nc)
327
+ switch (bpf_ntohs(ret)) {
67
}
328
+ case ETH_P_8021AD:
68
329
+ offset += 4;
69
n = VIRTIO_NET(v->dev->vdev);
330
+ case ETH_P_8021Q:
70
- features = n->parent_obj.guest_features;
331
+ offset += 4;
71
- if (features & BIT_ULL(VIRTIO_NET_F_CTRL_MAC_ADDR)) {
332
+ err = bpf_skb_load_bytes_relative(skb, offset, &ret, sizeof(ret),
72
- const struct virtio_net_ctrl_hdr ctrl = {
333
+ BPF_HDR_START_MAC);
73
- .class = VIRTIO_NET_CTRL_MAC,
334
+ default:
74
- .cmd = VIRTIO_NET_CTRL_MAC_ADDR_SET,
335
+ break;
75
- };
336
+ }
76
- char *cursor = s->cvq_cmd_out_buffer;
337
+
77
- ssize_t dev_written;
338
+ if (err) {
78
-
339
+ return 0;
79
- memcpy(cursor, &ctrl, sizeof(ctrl));
340
+ }
80
- cursor += sizeof(ctrl);
341
+
81
- memcpy(cursor, n->mac, sizeof(n->mac));
342
+ return ret;
82
-
343
+}
83
- dev_written = vhost_vdpa_net_cvq_add(s, sizeof(ctrl) + sizeof(n->mac),
344
+
84
- sizeof(virtio_net_ctrl_ack));
345
+static inline int parse_packet(struct __sk_buff *skb,
85
- if (unlikely(dev_written < 0)) {
346
+ struct packet_hash_info_t *info)
86
- return dev_written;
347
+{
87
- }
348
+ int err = 0;
88
-
349
+
89
- return *s->status != VIRTIO_NET_OK;
350
+ if (!info || !skb) {
90
+ r = vhost_vdpa_net_load_mac(s, n);
351
+ return -1;
91
+ if (unlikely(r < 0)) {
352
+ }
92
+ return r;
353
+
93
}
354
+ size_t l4_offset = 0;
94
355
+ __u8 l4_protocol = 0;
95
return 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";
623
--
96
--
624
2.7.4
97
2.7.4
625
98
626
99
diff view generated by jsdifflib
1
From: Andrew Melnychenko <andrew@daynix.com>
1
From: Eugenio Pérez <eperezma@redhat.com>
2
2
3
For now, that method supported only by Linux TAP.
3
Same way as with the MAC, restore the expected number of queues at
4
Linux TAP uses TUNSETSTEERINGEBPF ioctl.
4
device's start.
5
5
6
Signed-off-by: Andrew Melnychenko <andrew@daynix.com>
6
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
7
Signed-off-by: Jason Wang <jasowang@redhat.com>
7
Signed-off-by: Jason Wang <jasowang@redhat.com>
8
---
8
---
9
include/net/net.h | 2 ++
9
net/vhost-vdpa.c | 26 ++++++++++++++++++++++++++
10
net/tap-bsd.c | 5 +++++
10
1 file changed, 26 insertions(+)
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(+)
17
11
18
diff --git a/include/net/net.h b/include/net/net.h
12
diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c
19
index XXXXXXX..XXXXXXX 100644
13
index XXXXXXX..XXXXXXX 100644
20
--- a/include/net/net.h
14
--- a/net/vhost-vdpa.c
21
+++ b/include/net/net.h
15
+++ b/net/vhost-vdpa.c
22
@@ -XXX,XX +XXX,XX @@ typedef int (SetVnetBE)(NetClientState *, bool);
16
@@ -XXX,XX +XXX,XX @@ static int vhost_vdpa_net_load_mac(VhostVDPAState *s, const VirtIONet *n)
23
typedef struct SocketReadState SocketReadState;
24
typedef void (SocketReadStateFinalize)(SocketReadState *rs);
25
typedef void (NetAnnounce)(NetClientState *);
26
+typedef bool (SetSteeringEBPF)(NetClientState *, int);
27
28
typedef struct NetClientInfo {
29
NetClientDriver type;
30
@@ -XXX,XX +XXX,XX @@ typedef struct NetClientInfo {
31
SetVnetLE *set_vnet_le;
32
SetVnetBE *set_vnet_be;
33
NetAnnounce *announce;
34
+ SetSteeringEBPF *set_steering_ebpf;
35
} NetClientInfo;
36
37
struct NetClientState {
38
diff --git a/net/tap-bsd.c b/net/tap-bsd.c
39
index XXXXXXX..XXXXXXX 100644
40
--- a/net/tap-bsd.c
41
+++ b/net/tap-bsd.c
42
@@ -XXX,XX +XXX,XX @@ int tap_fd_get_ifname(int fd, char *ifname)
43
{
44
return -1;
45
}
46
+
47
+int tap_fd_set_steering_ebpf(int fd, int prog_fd)
48
+{
49
+ return -1;
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;
17
return 0;
58
}
18
}
19
20
+static int vhost_vdpa_net_load_mq(VhostVDPAState *s,
21
+ const VirtIONet *n)
22
+{
23
+ struct virtio_net_ctrl_mq mq;
24
+ uint64_t features = n->parent_obj.guest_features;
25
+ ssize_t dev_written;
59
+
26
+
60
+int tap_fd_set_steering_ebpf(int fd, int prog_fd)
27
+ if (!(features & BIT_ULL(VIRTIO_NET_F_MQ))) {
61
+{
28
+ return 0;
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);
66
+
67
+ return -1;
68
+ }
29
+ }
69
+
30
+
70
+ return 0;
31
+ mq.virtqueue_pairs = cpu_to_le16(n->curr_queue_pairs);
71
+}
32
+ dev_written = vhost_vdpa_net_load_cmd(s, VIRTIO_NET_CTRL_MQ,
72
diff --git a/net/tap-solaris.c b/net/tap-solaris.c
33
+ VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, &mq,
73
index XXXXXXX..XXXXXXX 100644
34
+ sizeof(mq));
74
--- a/net/tap-solaris.c
35
+ if (unlikely(dev_written < 0)) {
75
+++ b/net/tap-solaris.c
36
+ return dev_written;
76
@@ -XXX,XX +XXX,XX @@ int tap_fd_get_ifname(int fd, char *ifname)
37
+ }
77
{
78
return -1;
79
}
80
+
38
+
81
+int tap_fd_set_steering_ebpf(int fd, int prog_fd)
39
+ return *s->status != VIRTIO_NET_OK;
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;
112
+}
40
+}
113
+
41
+
114
int tap_get_fd(NetClientState *nc)
42
static int vhost_vdpa_net_load(NetClientState *nc)
115
{
43
{
116
TAPState *s = DO_UPCAST(TAPState, nc, nc);
44
VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc);
117
@@ -XXX,XX +XXX,XX @@ static NetClientInfo net_tap_info = {
45
@@ -XXX,XX +XXX,XX @@ static int vhost_vdpa_net_load(NetClientState *nc)
118
.set_vnet_hdr_len = tap_set_vnet_hdr_len,
46
if (unlikely(r < 0)) {
119
.set_vnet_le = tap_set_vnet_le,
47
return r;
120
.set_vnet_be = tap_set_vnet_be,
48
}
121
+ .set_steering_ebpf = tap_set_steering_ebpf,
49
+ r = vhost_vdpa_net_load_mq(s, n);
122
};
50
+ if (unlikely(r)) {
123
51
+ return r;
124
static TAPState *net_tap_fd_init(NetClientState *peer,
52
+ }
125
diff --git a/net/tap_int.h b/net/tap_int.h
53
126
index XXXXXXX..XXXXXXX 100644
54
return 0;
127
--- a/net/tap_int.h
55
}
128
+++ b/net/tap_int.h
129
@@ -XXX,XX +XXX,XX @@ int tap_fd_set_vnet_be(int fd, int vnet_is_be);
130
int tap_fd_enable(int fd);
131
int tap_fd_disable(int fd);
132
int tap_fd_get_ifname(int fd, char *ifname);
133
+int tap_fd_set_steering_ebpf(int fd, int prog_fd);
134
135
#endif /* NET_TAP_INT_H */
136
--
56
--
137
2.7.4
57
2.7.4
138
58
139
59
diff view generated by jsdifflib
1
From: Andrew Melnychenko <andrew@daynix.com>
1
From: Eugenio Pérez <eperezma@redhat.com>
2
2
3
Signed-off-by: Yuri Benditovich <yuri.benditovich@daynix.com>
3
So we are sure we can update the device model properly before sending to
4
Signed-off-by: Andrew Melnychenko <andrew@daynix.com>
4
the device.
5
6
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
5
Signed-off-by: Jason Wang <jasowang@redhat.com>
7
Signed-off-by: Jason Wang <jasowang@redhat.com>
6
---
8
---
7
docs/devel/ebpf_rss.rst | 125 ++++++++++++++++++++++++++++++++++++++++++++++++
9
net/vhost-vdpa.c | 9 +++++++++
8
docs/devel/index.rst | 1 +
10
1 file changed, 9 insertions(+)
9
2 files changed, 126 insertions(+)
10
create mode 100644 docs/devel/ebpf_rss.rst
11
11
12
diff --git a/docs/devel/ebpf_rss.rst b/docs/devel/ebpf_rss.rst
12
diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c
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
144
index XXXXXXX..XXXXXXX 100644
13
index XXXXXXX..XXXXXXX 100644
145
--- a/docs/devel/index.rst
14
--- a/net/vhost-vdpa.c
146
+++ b/docs/devel/index.rst
15
+++ b/net/vhost-vdpa.c
147
@@ -XXX,XX +XXX,XX @@ Contents:
16
@@ -XXX,XX +XXX,XX @@ static bool vhost_vdpa_net_cvq_validate_cmd(const void *out_buf, size_t len)
148
qom
17
__func__, ctrl.cmd);
149
block-coroutine-wrapper
18
};
150
multi-process
19
break;
151
+ ebpf_rss
20
+ case VIRTIO_NET_CTRL_MQ:
21
+ switch (ctrl.cmd) {
22
+ case VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET:
23
+ return true;
24
+ default:
25
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid mq cmd %u\n",
26
+ __func__, ctrl.cmd);
27
+ };
28
+ break;
29
default:
30
qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid control class %u\n",
31
__func__, ctrl.class);
152
--
32
--
153
2.7.4
33
2.7.4
154
34
155
35
diff view generated by jsdifflib
1
From: Andrew Melnychenko <andrew@daynix.com>
1
From: Eugenio Pérez <eperezma@redhat.com>
2
2
3
When RSS is enabled the device tries to load the eBPF program
3
It was returned as error before. Instead of it, simply update the
4
to select RX virtqueue in the TUN. If eBPF can be loaded
4
corresponding field so qemu can send it in the migration data.
5
the RSS will function also with vhost (works with kernel 5.8 and later).
6
Software RSS is used as a fallback with vhost=off when eBPF can't be loaded
7
or when hash population requested by the guest.
8
5
9
Signed-off-by: Yuri Benditovich <yuri.benditovich@daynix.com>
6
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
10
Signed-off-by: Andrew Melnychenko <andrew@daynix.com>
7
Acked-by: Si-Wei Liu <si-wei.liu@oracle.com>
11
Signed-off-by: Jason Wang <jasowang@redhat.com>
8
Signed-off-by: Jason Wang <jasowang@redhat.com>
12
---
9
---
13
hw/net/vhost_net.c | 3 ++
10
hw/net/virtio-net.c | 17 ++++++-----------
14
hw/net/virtio-net.c | 116 +++++++++++++++++++++++++++++++++++++++--
11
1 file changed, 6 insertions(+), 11 deletions(-)
15
include/hw/virtio/virtio-net.h | 4 ++
16
net/vhost-vdpa.c | 2 +
17
4 files changed, 122 insertions(+), 3 deletions(-)
18
12
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,
40
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
13
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
41
index XXXXXXX..XXXXXXX 100644
14
index XXXXXXX..XXXXXXX 100644
42
--- a/hw/net/virtio-net.c
15
--- a/hw/net/virtio-net.c
43
+++ b/hw/net/virtio-net.c
16
+++ b/hw/net/virtio-net.c
44
@@ -XXX,XX +XXX,XX @@ static uint64_t virtio_net_get_features(VirtIODevice *vdev, uint64_t features,
17
@@ -XXX,XX +XXX,XX @@ static int virtio_net_handle_mq(VirtIONet *n, uint8_t cmd,
45
return features;
18
return VIRTIO_NET_ERR;
46
}
19
}
47
20
48
- virtio_clear_feature(&features, VIRTIO_NET_F_RSS);
21
- /* Avoid changing the number of queue_pairs for vdpa device in
49
- virtio_clear_feature(&features, VIRTIO_NET_F_HASH_REPORT);
22
- * userspace handler. A future fix is needed to handle the mq
50
+ if (!ebpf_rss_is_loaded(&n->ebpf_rss)) {
23
- * change in userspace handler with vhost-vdpa. Let's disable
51
+ virtio_clear_feature(&features, VIRTIO_NET_F_RSS);
24
- * the mq handling from userspace for now and only allow get
52
+ }
25
- * done through the kernel. Ripples may be seen when falling
53
features = vhost_net_get_features(get_vhost_net(nc->peer), features);
26
- * back to userspace, but without doing it qemu process would
54
vdev->backend_features = features;
27
- * crash on a recursive entry to virtio_net_set_status().
55
28
- */
56
@@ -XXX,XX +XXX,XX @@ static int virtio_net_handle_announce(VirtIONet *n, uint8_t cmd,
29
+ n->curr_queue_pairs = queue_pairs;
30
if (nc->peer && nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_VDPA) {
31
- return VIRTIO_NET_ERR;
32
+ /*
33
+ * Avoid updating the backend for a vdpa device: We're only interested
34
+ * in updating the device model queues.
35
+ */
36
+ return VIRTIO_NET_OK;
57
}
37
}
58
}
38
-
59
39
- n->curr_queue_pairs = queue_pairs;
60
+static void virtio_net_detach_epbf_rss(VirtIONet *n);
40
/* stop the backend before changing the number of queue_pairs to avoid handling a
61
+
41
* disabled queue */
62
static void virtio_net_disable_rss(VirtIONet *n)
42
virtio_net_set_status(vdev, vdev->status);
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);
171
@@ -XXX,XX +XXX,XX @@ static int virtio_net_post_load_device(void *opaque, int version_id)
172
}
173
174
if (n->rss_data.enabled) {
175
+ n->rss_data.enabled_software_rss = n->rss_data.populate_hash;
176
+ if (!n->rss_data.populate_hash) {
177
+ if (!virtio_net_attach_epbf_rss(n)) {
178
+ if (get_vhost_net(qemu_get_queue(n->nic)->peer)) {
179
+ warn_report("Can't post-load eBPF RSS for vhost");
180
+ } else {
181
+ warn_report("Can't post-load eBPF RSS - "
182
+ "fallback to software RSS");
183
+ n->rss_data.enabled_software_rss = true;
184
+ }
185
+ }
186
+ }
187
+
188
trace_virtio_net_rss_enable(n->rss_data.hash_types,
189
n->rss_data.indirections_len,
190
sizeof(n->rss_data.key));
191
@@ -XXX,XX +XXX,XX @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp)
192
n->qdev = dev;
193
194
net_rx_pkt_init(&n->rx_pkt, false);
195
+
196
+ if (virtio_has_feature(n->host_features, VIRTIO_NET_F_RSS)) {
197
+ virtio_net_load_ebpf(n);
198
+ }
199
}
200
201
static void virtio_net_device_unrealize(DeviceState *dev)
202
@@ -XXX,XX +XXX,XX @@ static void virtio_net_device_unrealize(DeviceState *dev)
203
VirtIONet *n = VIRTIO_NET(dev);
204
int i, max_queues;
205
206
+ if (virtio_has_feature(n->host_features, VIRTIO_NET_F_RSS)) {
207
+ virtio_net_unload_ebpf(n);
208
+ }
209
+
210
/* This will stop vhost backend if appropriate. */
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
226
@@ -XXX,XX +XXX,XX @@
227
#include "qemu/option_int.h"
228
#include "qom/object.h"
229
230
+#include "ebpf/ebpf_rss.h"
231
+
232
#define TYPE_VIRTIO_NET "virtio-net-device"
233
OBJECT_DECLARE_SIMPLE_TYPE(VirtIONet, VIRTIO_NET)
234
235
@@ -XXX,XX +XXX,XX @@ typedef struct VirtioNetRscChain {
236
237
typedef struct VirtioNetRssData {
238
bool enabled;
239
+ bool enabled_software_rss;
240
bool redirect;
241
bool populate_hash;
242
uint32_t hash_types;
243
@@ -XXX,XX +XXX,XX @@ struct VirtIONet {
244
Notifier migration_state;
245
VirtioNetRssData rss_data;
246
struct NetRxPkt *rx_pkt;
247
+ struct EBPFRSSContext ebpf_rss;
248
};
249
250
void virtio_net_set_netclient_name(VirtIONet *n, const char *name,
251
diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c
252
index XXXXXXX..XXXXXXX 100644
253
--- a/net/vhost-vdpa.c
254
+++ b/net/vhost-vdpa.c
255
@@ -XXX,XX +XXX,XX @@ const int vdpa_feature_bits[] = {
256
VIRTIO_NET_F_MTU,
257
VIRTIO_F_IOMMU_PLATFORM,
258
VIRTIO_F_RING_PACKED,
259
+ VIRTIO_NET_F_RSS,
260
+ VIRTIO_NET_F_HASH_REPORT,
261
VIRTIO_NET_F_GUEST_ANNOUNCE,
262
VIRTIO_NET_F_STATUS,
263
VHOST_INVALID_FEATURE_BIT
264
--
43
--
265
2.7.4
44
2.7.4
266
45
267
46
diff view generated by jsdifflib
1
From: Andrew Melnychenko <andrew@daynix.com>
1
From: Eugenio Pérez <eperezma@redhat.com>
2
2
3
Additional code that will be used for eBPF setting steering routine.
3
Finally enable SVQ with MQ feature.
4
4
5
Signed-off-by: Andrew Melnychenko <andrew@daynix.com>
5
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
6
Signed-off-by: Jason Wang <jasowang@redhat.com>
6
Signed-off-by: Jason Wang <jasowang@redhat.com>
7
---
7
---
8
net/tap-linux.h | 1 +
8
net/vhost-vdpa.c | 1 +
9
1 file changed, 1 insertion(+)
9
1 file changed, 1 insertion(+)
10
10
11
diff --git a/net/tap-linux.h b/net/tap-linux.h
11
diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c
12
index XXXXXXX..XXXXXXX 100644
12
index XXXXXXX..XXXXXXX 100644
13
--- a/net/tap-linux.h
13
--- a/net/vhost-vdpa.c
14
+++ b/net/tap-linux.h
14
+++ b/net/vhost-vdpa.c
15
@@ -XXX,XX +XXX,XX @@
15
@@ -XXX,XX +XXX,XX @@ static const uint64_t vdpa_svq_device_features =
16
#define TUNSETQUEUE _IOW('T', 217, int)
16
BIT_ULL(VIRTIO_NET_F_MRG_RXBUF) |
17
#define TUNSETVNETLE _IOW('T', 220, int)
17
BIT_ULL(VIRTIO_NET_F_STATUS) |
18
#define TUNSETVNETBE _IOW('T', 222, int)
18
BIT_ULL(VIRTIO_NET_F_CTRL_VQ) |
19
+#define TUNSETSTEERINGEBPF _IOR('T', 224, int)
19
+ BIT_ULL(VIRTIO_NET_F_MQ) |
20
20
BIT_ULL(VIRTIO_F_ANY_LAYOUT) |
21
#endif
21
BIT_ULL(VIRTIO_NET_F_CTRL_MAC_ADDR) |
22
22
BIT_ULL(VIRTIO_NET_F_RSC_EXT) |
23
--
23
--
24
2.7.4
24
2.7.4
25
25
26
26
diff view generated by jsdifflib
New patch
1
From: lu zhipeng <luzhipeng@cestc.cn>
1
2
3
If the net tap initializes successful, but failed during
4
network card hot-plugging, the net-tap will remains,
5
so cleanup.
6
7
Signed-off-by: lu zhipeng <luzhipeng@cestc.cn>
8
Signed-off-by: Jason Wang <jasowang@redhat.com>
9
---
10
net/tap.c | 18 ++++++++++++------
11
1 file changed, 12 insertions(+), 6 deletions(-)
12
13
diff --git a/net/tap.c b/net/tap.c
14
index XXXXXXX..XXXXXXX 100644
15
--- a/net/tap.c
16
+++ b/net/tap.c
17
@@ -XXX,XX +XXX,XX @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer,
18
tap_set_sndbuf(s->fd, tap, &err);
19
if (err) {
20
error_propagate(errp, err);
21
- return;
22
+ goto failed;
23
}
24
25
if (tap->has_fd || tap->has_fds) {
26
@@ -XXX,XX +XXX,XX @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer,
27
} else {
28
warn_report_err(err);
29
}
30
- return;
31
+ goto failed;
32
}
33
if (!g_unix_set_fd_nonblocking(vhostfd, true, NULL)) {
34
error_setg_errno(errp, errno, "%s: Can't use file descriptor %d",
35
name, fd);
36
- return;
37
+ goto failed;
38
}
39
} else {
40
vhostfd = open("/dev/vhost-net", O_RDWR);
41
@@ -XXX,XX +XXX,XX @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer,
42
warn_report("tap: open vhost char device failed: %s",
43
strerror(errno));
44
}
45
- return;
46
+ goto failed;
47
}
48
if (!g_unix_set_fd_nonblocking(vhostfd, true, NULL)) {
49
error_setg_errno(errp, errno, "Failed to set FD nonblocking");
50
- return;
51
+ goto failed;
52
}
53
}
54
options.opaque = (void *)(uintptr_t)vhostfd;
55
@@ -XXX,XX +XXX,XX @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer,
56
} else {
57
warn_report(VHOST_NET_INIT_FAILED);
58
}
59
- return;
60
+ goto failed;
61
}
62
} else if (vhostfdname) {
63
error_setg(errp, "vhostfd(s)= is not valid without vhost");
64
+ goto failed;
65
}
66
+
67
+ return;
68
+
69
+failed:
70
+ qemu_del_net_client(&s->nc);
71
}
72
73
static int get_fds(char *str, char *fds[], int max)
74
--
75
2.7.4
diff view generated by jsdifflib