1
The following changes since commit 92f8c6fef13b31ba222c4d20ad8afd2b79c4c28e:
1
The following changes since commit 187f35512106501fe9a11057f4d8705431e0026d:
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 remote-tracking branch 'remotes/stsquad/tags/pull-testing-next-251019-3' into staging (2019-10-26 10:13:48 +0100)
4
4
5
are available in the git repository at:
5
are available in the git repository at:
6
6
7
https://github.com/jasowang/qemu.git tags/net-pull-request
7
https://github.com/jasowang/qemu.git tags/net-pull-request
8
8
9
for you to fetch changes up to 90322e646e87c1440661cb3ddbc0cc94309d8a4f:
9
for you to fetch changes up to 1e907a32b77e5d418538453df5945242e43224fa:
10
10
11
MAINTAINERS: Added eBPF maintainers information. (2021-06-04 15:25:46 +0800)
11
COLO-compare: Fix incorrect `if` logic (2019-10-29 10:28:07 +0800)
12
12
13
----------------------------------------------------------------
13
----------------------------------------------------------------
14
14
15
Changes from V1:
16
17
- Fix compling issue
18
15
----------------------------------------------------------------
19
----------------------------------------------------------------
16
Andrew Melnychenko (7):
20
Fan Yang (1):
17
net/tap: Added TUNSETSTEERINGEBPF code.
21
COLO-compare: Fix incorrect `if` logic
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
22
25
MAINTAINERS | 8 +
23
Michael S. Tsirkin (1):
26
configure | 8 +-
24
virtio: new post_load hook
27
docs/devel/ebpf_rss.rst | 125 +++++++++
25
28
docs/devel/index.rst | 1 +
26
Mikhail Sennikovsky (1):
29
ebpf/ebpf_rss-stub.c | 40 +++
27
virtio-net: prevent offloads reset on migration
30
ebpf/ebpf_rss.c | 165 ++++++++++++
28
31
ebpf/ebpf_rss.h | 44 ++++
29
Sven Schnelle (1):
32
ebpf/meson.build | 1 +
30
net: add tulip (dec21143) driver
33
ebpf/rss.bpf.skeleton.h | 431 +++++++++++++++++++++++++++++++
31
34
ebpf/trace-events | 4 +
32
MAINTAINERS | 6 +
35
ebpf/trace.h | 1 +
33
hw/net/Kconfig | 5 +
36
hw/net/vhost_net.c | 3 +
34
hw/net/Makefile.objs | 1 +
37
hw/net/virtio-net.c | 116 ++++++++-
35
hw/net/trace-events | 14 +
38
include/hw/virtio/virtio-net.h | 4 +
36
hw/net/tulip.c | 1029 ++++++++++++++++++++++++++++++++++++++++
39
include/net/net.h | 2 +
37
hw/net/tulip.h | 267 +++++++++++
40
meson.build | 23 ++
38
hw/net/virtio-net.c | 27 +-
41
meson_options.txt | 2 +
39
hw/virtio/virtio.c | 7 +
42
net/tap-bsd.c | 5 +
40
include/hw/pci/pci_ids.h | 1 +
43
net/tap-linux.c | 13 +
41
include/hw/virtio/virtio-net.h | 2 +
44
net/tap-linux.h | 1 +
42
include/hw/virtio/virtio.h | 6 +
45
net/tap-solaris.c | 5 +
43
net/colo-compare.c | 6 +-
46
net/tap-stub.c | 5 +
44
12 files changed, 1365 insertions(+), 6 deletions(-)
47
net/tap.c | 9 +
45
create mode 100644 hw/net/tulip.c
48
net/tap_int.h | 1 +
46
create mode 100644 hw/net/tulip.h
49
net/vhost-vdpa.c | 2 +
50
tools/ebpf/Makefile.ebpf | 21 ++
51
tools/ebpf/rss.bpf.c | 571 +++++++++++++++++++++++++++++++++++++++++
52
27 files changed, 1607 insertions(+), 4 deletions(-)
53
create mode 100644 docs/devel/ebpf_rss.rst
54
create mode 100644 ebpf/ebpf_rss-stub.c
55
create mode 100644 ebpf/ebpf_rss.c
56
create mode 100644 ebpf/ebpf_rss.h
57
create mode 100644 ebpf/meson.build
58
create mode 100644 ebpf/rss.bpf.skeleton.h
59
create mode 100644 ebpf/trace-events
60
create mode 100644 ebpf/trace.h
61
create mode 100755 tools/ebpf/Makefile.ebpf
62
create mode 100644 tools/ebpf/rss.bpf.c
63
47
64
48
49
diff view generated by jsdifflib
Deleted patch
1
From: Andrew Melnychenko <andrew@daynix.com>
2
1
3
Additional code that will be used for eBPF setting steering routine.
4
5
Signed-off-by: Andrew Melnychenko <andrew@daynix.com>
6
Signed-off-by: Jason Wang <jasowang@redhat.com>
7
---
8
net/tap-linux.h | 1 +
9
1 file changed, 1 insertion(+)
10
11
diff --git a/net/tap-linux.h b/net/tap-linux.h
12
index XXXXXXX..XXXXXXX 100644
13
--- a/net/tap-linux.h
14
+++ b/net/tap-linux.h
15
@@ -XXX,XX +XXX,XX @@
16
#define TUNSETQUEUE _IOW('T', 217, int)
17
#define TUNSETVNETLE _IOW('T', 220, int)
18
#define TUNSETVNETBE _IOW('T', 222, int)
19
+#define TUNSETSTEERINGEBPF _IOR('T', 224, int)
20
21
#endif
22
23
--
24
2.7.4
25
26
diff view generated by jsdifflib
Deleted patch
1
From: Andrew Melnychenko <andrew@daynix.com>
2
1
3
For now, that method supported only by Linux TAP.
4
Linux TAP uses TUNSETSTEERINGEBPF ioctl.
5
6
Signed-off-by: Andrew Melnychenko <andrew@daynix.com>
7
Signed-off-by: Jason Wang <jasowang@redhat.com>
8
---
9
include/net/net.h | 2 ++
10
net/tap-bsd.c | 5 +++++
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
18
diff --git a/include/net/net.h b/include/net/net.h
19
index XXXXXXX..XXXXXXX 100644
20
--- a/include/net/net.h
21
+++ b/include/net/net.h
22
@@ -XXX,XX +XXX,XX @@ typedef int (SetVnetBE)(NetClientState *, bool);
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;
58
}
59
+
60
+int tap_fd_set_steering_ebpf(int fd, int prog_fd)
61
+{
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
+ }
69
+
70
+ return 0;
71
+}
72
diff --git a/net/tap-solaris.c b/net/tap-solaris.c
73
index XXXXXXX..XXXXXXX 100644
74
--- a/net/tap-solaris.c
75
+++ b/net/tap-solaris.c
76
@@ -XXX,XX +XXX,XX @@ int tap_fd_get_ifname(int fd, char *ifname)
77
{
78
return -1;
79
}
80
+
81
+int tap_fd_set_steering_ebpf(int fd, int prog_fd)
82
+{
83
+ return -1;
84
+}
85
diff --git a/net/tap-stub.c b/net/tap-stub.c
86
index XXXXXXX..XXXXXXX 100644
87
--- a/net/tap-stub.c
88
+++ b/net/tap-stub.c
89
@@ -XXX,XX +XXX,XX @@ int tap_fd_get_ifname(int fd, char *ifname)
90
{
91
return -1;
92
}
93
+
94
+int tap_fd_set_steering_ebpf(int fd, int prog_fd)
95
+{
96
+ return -1;
97
+}
98
diff --git a/net/tap.c b/net/tap.c
99
index XXXXXXX..XXXXXXX 100644
100
--- a/net/tap.c
101
+++ b/net/tap.c
102
@@ -XXX,XX +XXX,XX @@ static void tap_poll(NetClientState *nc, bool enable)
103
tap_write_poll(s, enable);
104
}
105
106
+static bool tap_set_steering_ebpf(NetClientState *nc, int prog_fd)
107
+{
108
+ TAPState *s = DO_UPCAST(TAPState, nc, nc);
109
+ assert(nc->info->type == NET_CLIENT_DRIVER_TAP);
110
+
111
+ return tap_fd_set_steering_ebpf(s->fd, prog_fd) == 0;
112
+}
113
+
114
int tap_get_fd(NetClientState *nc)
115
{
116
TAPState *s = DO_UPCAST(TAPState, nc, nc);
117
@@ -XXX,XX +XXX,XX @@ static NetClientInfo net_tap_info = {
118
.set_vnet_hdr_len = tap_set_vnet_hdr_len,
119
.set_vnet_le = tap_set_vnet_le,
120
.set_vnet_be = tap_set_vnet_be,
121
+ .set_steering_ebpf = tap_set_steering_ebpf,
122
};
123
124
static TAPState *net_tap_fd_init(NetClientState *peer,
125
diff --git a/net/tap_int.h b/net/tap_int.h
126
index XXXXXXX..XXXXXXX 100644
127
--- a/net/tap_int.h
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
--
137
2.7.4
138
139
diff view generated by jsdifflib
1
From: Andrew Melnychenko <andrew@daynix.com>
1
From: Sven Schnelle <svens@stackframe.org>
2
2
3
RSS program and Makefile to build it.
3
This adds the basic functionality to emulate a Tulip NIC.
4
The bpftool used to generate '.h' file.
5
The data in that file may be loaded by libbpf.
6
EBPF compilation is not required for building qemu.
7
You can use Makefile if you need to regenerate rss.bpf.skeleton.h.
8
4
9
Signed-off-by: Yuri Benditovich <yuri.benditovich@daynix.com>
5
Implemented are:
10
Signed-off-by: Andrew Melnychenko <andrew@daynix.com>
6
7
- RX and TX functionality
8
- Perfect Frame Filtering
9
- Big/Little Endian descriptor support
10
- 93C46 EEPROM support
11
- LXT970 PHY
12
13
Not implemented, mostly because i had no OS using these functions:
14
15
- Imperfect frame filtering
16
- General Purpose Timer
17
- Transmit automatic polling
18
- Boot ROM support
19
- SIA interface
20
- Big/Little Endian data buffer conversion
21
22
Successfully tested with the following Operating Systems:
23
24
- MSDOS with Microsoft Network Client 3.0 and DEC ODI drivers
25
- HPPA Linux
26
- Windows XP
27
- HP-UX
28
29
Signed-off-by: Sven Schnelle <svens@stackframe.org>
30
Message-Id: <20191022155413.4619-1-svens@stackframe.org>
31
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
11
Signed-off-by: Jason Wang <jasowang@redhat.com>
32
Signed-off-by: Jason Wang <jasowang@redhat.com>
12
---
33
---
13
tools/ebpf/Makefile.ebpf | 21 ++
34
MAINTAINERS | 6 +
14
tools/ebpf/rss.bpf.c | 571 +++++++++++++++++++++++++++++++++++++++++++++++
35
hw/net/Kconfig | 5 +
15
2 files changed, 592 insertions(+)
36
hw/net/Makefile.objs | 1 +
16
create mode 100755 tools/ebpf/Makefile.ebpf
37
hw/net/trace-events | 14 +
17
create mode 100644 tools/ebpf/rss.bpf.c
38
hw/net/tulip.c | 1029 ++++++++++++++++++++++++++++++++++++++++++++++
39
hw/net/tulip.h | 267 ++++++++++++
40
include/hw/pci/pci_ids.h | 1 +
41
7 files changed, 1323 insertions(+)
42
create mode 100644 hw/net/tulip.c
43
create mode 100644 hw/net/tulip.h
18
44
19
diff --git a/tools/ebpf/Makefile.ebpf b/tools/ebpf/Makefile.ebpf
45
diff --git a/MAINTAINERS b/MAINTAINERS
20
new file mode 100755
46
index XXXXXXX..XXXXXXX 100644
21
index XXXXXXX..XXXXXXX
47
--- a/MAINTAINERS
22
--- /dev/null
48
+++ b/MAINTAINERS
23
+++ b/tools/ebpf/Makefile.ebpf
49
@@ -XXX,XX +XXX,XX @@ M: Stefan Weil <sw@weilnetz.de>
24
@@ -XXX,XX +XXX,XX @@
50
S: Maintained
25
+OBJS = rss.bpf.o
51
F: hw/net/eepro100.c
26
+
52
27
+LLC ?= llc
53
+tulip
28
+CLANG ?= clang
54
+M: Sven Schnelle <svens@stackframe.org>
29
+INC_FLAGS = `$(CLANG) -print-file-name=include`
55
+S: Maintained
30
+EXTRA_CFLAGS ?= -O2 -emit-llvm -fno-stack-protector
56
+F: hw/net/tulip.c
31
+
57
+F: hw/net/tulip.h
32
+all: $(OBJS)
58
+
33
+
59
Generic Loader
34
+.PHONY: clean
60
M: Alistair Francis <alistair@alistair23.me>
35
+
61
S: Maintained
36
+clean:
62
diff --git a/hw/net/Kconfig b/hw/net/Kconfig
37
+    rm -f $(OBJS)
63
index XXXXXXX..XXXXXXX 100644
38
+
64
--- a/hw/net/Kconfig
39
+$(OBJS): %.o:%.c
65
+++ b/hw/net/Kconfig
40
+    $(CLANG) $(INC_FLAGS) \
66
@@ -XXX,XX +XXX,XX @@ config PCNET_PCI
41
+ -D__KERNEL__ -D__ASM_SYSREG_H \
67
config PCNET_COMMON
42
+ -I../include $(LINUXINCLUDE) \
68
bool
43
+ $(EXTRA_CFLAGS) -c $< -o -| $(LLC) -march=bpf -filetype=obj -o $@
69
44
+    bpftool gen skeleton rss.bpf.o > rss.bpf.skeleton.h
70
+config TULIP
45
+    cp rss.bpf.skeleton.h ../../ebpf/
71
+ bool
46
diff --git a/tools/ebpf/rss.bpf.c b/tools/ebpf/rss.bpf.c
72
+ default y if PCI_DEVICES
73
+ depends on PCI
74
+
75
config E1000_PCI
76
bool
77
default y if PCI_DEVICES
78
diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs
79
index XXXXXXX..XXXXXXX 100644
80
--- a/hw/net/Makefile.objs
81
+++ b/hw/net/Makefile.objs
82
@@ -XXX,XX +XXX,XX @@ common-obj-$(CONFIG_E1000E_PCI_EXPRESS) += e1000e.o e1000e_core.o e1000x_common.
83
common-obj-$(CONFIG_RTL8139_PCI) += rtl8139.o
84
common-obj-$(CONFIG_VMXNET3_PCI) += net_tx_pkt.o net_rx_pkt.o
85
common-obj-$(CONFIG_VMXNET3_PCI) += vmxnet3.o
86
+common-obj-$(CONFIG_TULIP) += tulip.o
87
88
common-obj-$(CONFIG_SMC91C111) += smc91c111.o
89
common-obj-$(CONFIG_LAN9118) += lan9118.o
90
diff --git a/hw/net/trace-events b/hw/net/trace-events
91
index XXXXXXX..XXXXXXX 100644
92
--- a/hw/net/trace-events
93
+++ b/hw/net/trace-events
94
@@ -XXX,XX +XXX,XX @@ virtio_net_announce_notify(void) ""
95
virtio_net_announce_timer(int round) "%d"
96
virtio_net_handle_announce(int round) "%d"
97
virtio_net_post_load_device(void)
98
+
99
+# tulip.c
100
+tulip_reg_write(uint64_t addr, const char *name, int size, uint64_t val) "addr 0x%02"PRIx64" (%s) size %d value 0x%08"PRIx64
101
+tulip_reg_read(uint64_t addr, const char *name, int size, uint64_t val) "addr 0x%02"PRIx64" (%s) size %d value 0x%08"PRIx64
102
+tulip_receive(const uint8_t *buf, size_t len) "buf %p size %zu"
103
+tulip_descriptor(const char *prefix, uint32_t addr, uint32_t status, uint32_t control, uint32_t len1, uint32_t len2, uint32_t buf1, uint32_t buf2) "%s 0x%08x: status 0x%08x control 0x%03x len1 %4d len2 %4d buf1 0x%08x buf2 0x%08x"
104
+tulip_rx_state(const char *state) "RX %s"
105
+tulip_tx_state(const char *state) "TX %s"
106
+tulip_irq(uint32_t mask, uint32_t en, const char *state) "mask 0x%08x ie 0x%08x %s"
107
+tulip_mii_write(int phy, int reg, uint16_t data) "phy 0x%x reg 0x%x data 0x%04x"
108
+tulip_mii_read(int phy, int reg, uint16_t data) "phy 0x%x, reg 0x%x data 0x%04x"
109
+tulip_reset(void) ""
110
+tulip_setup_frame(void) ""
111
+tulip_setup_filter(int n, uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint8_t e, uint8_t f) "%d: %02x:%02x:%02x:%02x:%02x:%02x"
112
diff --git a/hw/net/tulip.c b/hw/net/tulip.c
47
new file mode 100644
113
new file mode 100644
48
index XXXXXXX..XXXXXXX
114
index XXXXXXX..XXXXXXX
49
--- /dev/null
115
--- /dev/null
50
+++ b/tools/ebpf/rss.bpf.c
116
+++ b/hw/net/tulip.c
51
@@ -XXX,XX +XXX,XX @@
117
@@ -XXX,XX +XXX,XX @@
52
+/*
118
+/*
53
+ * eBPF RSS program
119
+ * QEMU TULIP Emulation
54
+ *
120
+ *
55
+ * Developed by Daynix Computing LTD (http://www.daynix.com)
121
+ * Copyright (c) 2019 Sven Schnelle <svens@stackframe.org>
56
+ *
122
+ *
57
+ * Authors:
123
+ * This work is licensed under the GNU GPL license version 2 or later.
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
+ */
124
+ */
70
+
125
+
71
+#include <stddef.h>
126
+#include "qemu/osdep.h"
72
+#include <stdbool.h>
127
+#include "qemu/log.h"
73
+#include <linux/bpf.h>
128
+#include "hw/irq.h"
74
+
129
+#include "hw/pci/pci.h"
75
+#include <linux/in.h>
130
+#include "hw/qdev-properties.h"
76
+#include <linux/if_ether.h>
131
+#include "hw/nvram/eeprom93xx.h"
77
+#include <linux/ip.h>
132
+#include "migration/vmstate.h"
78
+#include <linux/ipv6.h>
133
+#include "sysemu/sysemu.h"
79
+
134
+#include "tulip.h"
80
+#include <linux/udp.h>
135
+#include "trace.h"
81
+#include <linux/tcp.h>
136
+#include "net/eth.h"
82
+
137
+
83
+#include <bpf/bpf_helpers.h>
138
+typedef struct TULIPState {
84
+#include <bpf/bpf_endian.h>
139
+ PCIDevice dev;
85
+#include <linux/virtio_net.h>
140
+ MemoryRegion io;
86
+
141
+ MemoryRegion memory;
87
+#define INDIRECTION_TABLE_SIZE 128
142
+ NICConf c;
88
+#define HASH_CALCULATION_BUFFER_SIZE 36
143
+ qemu_irq irq;
89
+
144
+ NICState *nic;
90
+struct rss_config_t {
145
+ eeprom_t *eeprom;
91
+ __u8 redirect;
146
+ uint32_t csr[16];
92
+ __u8 populate_hash;
147
+
93
+ __u32 hash_types;
148
+ /* state for MII */
94
+ __u16 indirections_len;
149
+ uint32_t old_csr9;
95
+ __u16 default_queue;
150
+ uint32_t mii_word;
96
+} __attribute__((packed));
151
+ uint32_t mii_bitcnt;
97
+
152
+
98
+struct toeplitz_key_data_t {
153
+ hwaddr current_rx_desc;
99
+ __u32 leftmost_32_bits;
154
+ hwaddr current_tx_desc;
100
+ __u8 next_byte[HASH_CALCULATION_BUFFER_SIZE];
155
+
156
+ uint8_t rx_frame[2048];
157
+ uint8_t tx_frame[2048];
158
+ uint16_t tx_frame_len;
159
+ uint16_t rx_frame_len;
160
+ uint16_t rx_frame_size;
161
+
162
+ uint32_t rx_status;
163
+ uint8_t filter[16][6];
164
+} TULIPState;
165
+
166
+static const VMStateDescription vmstate_pci_tulip = {
167
+ .name = "tulip",
168
+ .fields = (VMStateField[]) {
169
+ VMSTATE_PCI_DEVICE(dev, TULIPState),
170
+ VMSTATE_UINT32_ARRAY(csr, TULIPState, 16),
171
+ VMSTATE_UINT32(old_csr9, TULIPState),
172
+ VMSTATE_UINT32(mii_word, TULIPState),
173
+ VMSTATE_UINT32(mii_bitcnt, TULIPState),
174
+ VMSTATE_UINT64(current_rx_desc, TULIPState),
175
+ VMSTATE_UINT64(current_tx_desc, TULIPState),
176
+ VMSTATE_BUFFER(rx_frame, TULIPState),
177
+ VMSTATE_BUFFER(tx_frame, TULIPState),
178
+ VMSTATE_UINT16(rx_frame_len, TULIPState),
179
+ VMSTATE_UINT16(tx_frame_len, TULIPState),
180
+ VMSTATE_UINT16(rx_frame_size, TULIPState),
181
+ VMSTATE_UINT32(rx_status, TULIPState),
182
+ VMSTATE_UINT8_2DARRAY(filter, TULIPState, 16, 6),
183
+ VMSTATE_END_OF_LIST()
184
+ }
101
+};
185
+};
102
+
186
+
103
+struct packet_hash_info_t {
187
+static void tulip_desc_read(TULIPState *s, hwaddr p,
104
+ __u8 is_ipv4;
188
+ struct tulip_descriptor *desc)
105
+ __u8 is_ipv6;
189
+{
106
+ __u8 is_udp;
190
+ if (s->csr[0] & CSR0_DBO) {
107
+ __u8 is_tcp;
191
+ desc->status = ldl_be_pci_dma(&s->dev, p);
108
+ __u8 is_ipv6_ext_src;
192
+ desc->control = ldl_be_pci_dma(&s->dev, p + 4);
109
+ __u8 is_ipv6_ext_dst;
193
+ desc->buf_addr1 = ldl_be_pci_dma(&s->dev, p + 8);
110
+ __u8 is_fragmented;
194
+ desc->buf_addr2 = ldl_be_pci_dma(&s->dev, p + 12);
111
+
195
+ } else {
112
+ __u16 src_port;
196
+ desc->status = ldl_le_pci_dma(&s->dev, p);
113
+ __u16 dst_port;
197
+ desc->control = ldl_le_pci_dma(&s->dev, p + 4);
114
+
198
+ desc->buf_addr1 = ldl_le_pci_dma(&s->dev, p + 8);
115
+ union {
199
+ desc->buf_addr2 = ldl_le_pci_dma(&s->dev, p + 12);
116
+ struct {
200
+ }
117
+ __be32 in_src;
201
+}
118
+ __be32 in_dst;
202
+
119
+ };
203
+static void tulip_desc_write(TULIPState *s, hwaddr p,
120
+
204
+ struct tulip_descriptor *desc)
121
+ struct {
205
+{
122
+ struct in6_addr in6_src;
206
+ if (s->csr[0] & CSR0_DBO) {
123
+ struct in6_addr in6_dst;
207
+ stl_be_pci_dma(&s->dev, p, desc->status);
124
+ struct in6_addr in6_ext_src;
208
+ stl_be_pci_dma(&s->dev, p + 4, desc->control);
125
+ struct in6_addr in6_ext_dst;
209
+ stl_be_pci_dma(&s->dev, p + 8, desc->buf_addr1);
126
+ };
210
+ stl_be_pci_dma(&s->dev, p + 12, desc->buf_addr2);
127
+ };
211
+ } else {
212
+ stl_le_pci_dma(&s->dev, p, desc->status);
213
+ stl_le_pci_dma(&s->dev, p + 4, desc->control);
214
+ stl_le_pci_dma(&s->dev, p + 8, desc->buf_addr1);
215
+ stl_le_pci_dma(&s->dev, p + 12, desc->buf_addr2);
216
+ }
217
+}
218
+
219
+static void tulip_update_int(TULIPState *s)
220
+{
221
+ uint32_t ie = s->csr[5] & s->csr[7];
222
+ bool assert = false;
223
+
224
+ s->csr[5] &= ~(CSR5_AIS | CSR5_NIS);
225
+
226
+ if (ie & (CSR5_TI | CSR5_TU | CSR5_RI | CSR5_GTE | CSR5_ERI)) {
227
+ s->csr[5] |= CSR5_NIS;
228
+ }
229
+
230
+ if (ie & (CSR5_LC | CSR5_GPI | CSR5_FBE | CSR5_LNF | CSR5_ETI | CSR5_RWT |
231
+ CSR5_RPS | CSR5_RU | CSR5_UNF | CSR5_LNP_ANC | CSR5_TJT |
232
+ CSR5_TPS)) {
233
+ s->csr[5] |= CSR5_AIS;
234
+ }
235
+
236
+ assert = s->csr[5] & s->csr[7] & (CSR5_AIS | CSR5_NIS);
237
+ trace_tulip_irq(s->csr[5], s->csr[7], assert ? "assert" : "deassert");
238
+ qemu_set_irq(s->irq, assert);
239
+}
240
+
241
+static bool tulip_rx_stopped(TULIPState *s)
242
+{
243
+ return ((s->csr[5] >> CSR5_RS_SHIFT) & CSR5_RS_MASK) == CSR5_RS_STOPPED;
244
+}
245
+
246
+static void tulip_dump_tx_descriptor(TULIPState *s,
247
+ struct tulip_descriptor *desc)
248
+{
249
+ trace_tulip_descriptor("TX ", s->current_tx_desc,
250
+ desc->status, desc->control >> 22,
251
+ desc->control & 0x7ff, (desc->control >> 11) & 0x7ff,
252
+ desc->buf_addr1, desc->buf_addr2);
253
+}
254
+
255
+static void tulip_dump_rx_descriptor(TULIPState *s,
256
+ struct tulip_descriptor *desc)
257
+{
258
+ trace_tulip_descriptor("RX ", s->current_rx_desc,
259
+ desc->status, desc->control >> 22,
260
+ desc->control & 0x7ff, (desc->control >> 11) & 0x7ff,
261
+ desc->buf_addr1, desc->buf_addr2);
262
+}
263
+
264
+static void tulip_next_rx_descriptor(TULIPState *s,
265
+ struct tulip_descriptor *desc)
266
+{
267
+ if (desc->control & RDES1_RER) {
268
+ s->current_rx_desc = s->csr[3];
269
+ } else if (desc->control & RDES1_RCH) {
270
+ s->current_rx_desc = desc->buf_addr2;
271
+ } else {
272
+ s->current_rx_desc += sizeof(struct tulip_descriptor) +
273
+ (((s->csr[0] >> CSR0_DSL_SHIFT) & CSR0_DSL_MASK) << 2);
274
+ }
275
+ s->current_rx_desc &= ~3ULL;
276
+}
277
+
278
+static void tulip_copy_rx_bytes(TULIPState *s, struct tulip_descriptor *desc)
279
+{
280
+ int len1 = (desc->control >> RDES1_BUF1_SIZE_SHIFT) & RDES1_BUF1_SIZE_MASK;
281
+ int len2 = (desc->control >> RDES1_BUF2_SIZE_SHIFT) & RDES1_BUF2_SIZE_MASK;
282
+ int len;
283
+
284
+ if (s->rx_frame_len && len1) {
285
+ if (s->rx_frame_len > len1) {
286
+ len = len1;
287
+ } else {
288
+ len = s->rx_frame_len;
289
+ }
290
+ pci_dma_write(&s->dev, desc->buf_addr1, s->rx_frame +
291
+ (s->rx_frame_size - s->rx_frame_len), len);
292
+ s->rx_frame_len -= len;
293
+ }
294
+
295
+ if (s->rx_frame_len && len2) {
296
+ if (s->rx_frame_len > len2) {
297
+ len = len2;
298
+ } else {
299
+ len = s->rx_frame_len;
300
+ }
301
+ pci_dma_write(&s->dev, desc->buf_addr2, s->rx_frame +
302
+ (s->rx_frame_size - s->rx_frame_len), len);
303
+ s->rx_frame_len -= len;
304
+ }
305
+}
306
+
307
+static bool tulip_filter_address(TULIPState *s, const uint8_t *addr)
308
+{
309
+ static const char broadcast[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
310
+ bool ret = false;
311
+ int i;
312
+
313
+ for (i = 0; i < 16 && ret == false; i++) {
314
+ if (!memcmp(&s->filter[i], addr, ETH_ALEN)) {
315
+ ret = true;
316
+ }
317
+ }
318
+
319
+ if (!memcmp(addr, broadcast, ETH_ALEN)) {
320
+ return true;
321
+ }
322
+
323
+ if (s->csr[6] & (CSR6_PR | CSR6_RA)) {
324
+ /* Promiscuous mode enabled */
325
+ s->rx_status |= RDES0_FF;
326
+ return true;
327
+ }
328
+
329
+ if ((s->csr[6] & CSR6_PM) && (addr[0] & 1)) {
330
+ /* Pass all Multicast enabled */
331
+ s->rx_status |= RDES0_MF;
332
+ return true;
333
+ }
334
+
335
+ if (s->csr[6] & CSR6_IF) {
336
+ ret ^= true;
337
+ }
338
+ return ret;
339
+}
340
+
341
+static ssize_t tulip_receive(TULIPState *s, const uint8_t *buf, size_t size)
342
+{
343
+ struct tulip_descriptor desc;
344
+
345
+ trace_tulip_receive(buf, size);
346
+
347
+ if (size < 14 || size > 2048 || s->rx_frame_len || tulip_rx_stopped(s)) {
348
+ return 0;
349
+ }
350
+
351
+ if (!tulip_filter_address(s, buf)) {
352
+ return size;
353
+ }
354
+
355
+ do {
356
+ tulip_desc_read(s, s->current_rx_desc, &desc);
357
+ tulip_dump_rx_descriptor(s, &desc);
358
+
359
+ if (!(desc.status & RDES0_OWN)) {
360
+ s->csr[5] |= CSR5_RU;
361
+ tulip_update_int(s);
362
+ return s->rx_frame_size - s->rx_frame_len;
363
+ }
364
+ desc.status = 0;
365
+
366
+ if (!s->rx_frame_len) {
367
+ s->rx_frame_size = size + 4;
368
+ s->rx_status = RDES0_LS |
369
+ ((s->rx_frame_size & RDES0_FL_MASK) << RDES0_FL_SHIFT);
370
+ desc.status |= RDES0_FS;
371
+ memcpy(s->rx_frame, buf, size);
372
+ s->rx_frame_len = s->rx_frame_size;
373
+ }
374
+
375
+ tulip_copy_rx_bytes(s, &desc);
376
+
377
+ if (!s->rx_frame_len) {
378
+ desc.status |= s->rx_status;
379
+ s->csr[5] |= CSR5_RI;
380
+ tulip_update_int(s);
381
+ }
382
+ tulip_dump_rx_descriptor(s, &desc);
383
+ tulip_desc_write(s, s->current_rx_desc, &desc);
384
+ tulip_next_rx_descriptor(s, &desc);
385
+ } while (s->rx_frame_len);
386
+ return size;
387
+}
388
+
389
+static ssize_t tulip_receive_nc(NetClientState *nc,
390
+ const uint8_t *buf, size_t size)
391
+{
392
+ return tulip_receive(qemu_get_nic_opaque(nc), buf, size);
393
+}
394
+
395
+
396
+static NetClientInfo net_tulip_info = {
397
+ .type = NET_CLIENT_DRIVER_NIC,
398
+ .size = sizeof(NICState),
399
+ .receive = tulip_receive_nc,
128
+};
400
+};
129
+
401
+
130
+struct bpf_map_def SEC("maps")
402
+static const char *tulip_reg_name(const hwaddr addr)
131
+tap_rss_map_configurations = {
403
+{
132
+ .type = BPF_MAP_TYPE_ARRAY,
404
+ switch (addr) {
133
+ .key_size = sizeof(__u32),
405
+ case CSR(0):
134
+ .value_size = sizeof(struct rss_config_t),
406
+ return "CSR0";
135
+ .max_entries = 1,
407
+
408
+ case CSR(1):
409
+ return "CSR1";
410
+
411
+ case CSR(2):
412
+ return "CSR2";
413
+
414
+ case CSR(3):
415
+ return "CSR3";
416
+
417
+ case CSR(4):
418
+ return "CSR4";
419
+
420
+ case CSR(5):
421
+ return "CSR5";
422
+
423
+ case CSR(6):
424
+ return "CSR6";
425
+
426
+ case CSR(7):
427
+ return "CSR7";
428
+
429
+ case CSR(8):
430
+ return "CSR8";
431
+
432
+ case CSR(9):
433
+ return "CSR9";
434
+
435
+ case CSR(10):
436
+ return "CSR10";
437
+
438
+ case CSR(11):
439
+ return "CSR11";
440
+
441
+ case CSR(12):
442
+ return "CSR12";
443
+
444
+ case CSR(13):
445
+ return "CSR13";
446
+
447
+ case CSR(14):
448
+ return "CSR14";
449
+
450
+ case CSR(15):
451
+ return "CSR15";
452
+
453
+ default:
454
+ break;
455
+ }
456
+ return "";
457
+}
458
+
459
+static const char *tulip_rx_state_name(int state)
460
+{
461
+ switch (state) {
462
+ case CSR5_RS_STOPPED:
463
+ return "STOPPED";
464
+
465
+ case CSR5_RS_RUNNING_FETCH:
466
+ return "RUNNING/FETCH";
467
+
468
+ case CSR5_RS_RUNNING_CHECK_EOR:
469
+ return "RUNNING/CHECK EOR";
470
+
471
+ case CSR5_RS_RUNNING_WAIT_RECEIVE:
472
+ return "WAIT RECEIVE";
473
+
474
+ case CSR5_RS_SUSPENDED:
475
+ return "SUSPENDED";
476
+
477
+ case CSR5_RS_RUNNING_CLOSE:
478
+ return "RUNNING/CLOSE";
479
+
480
+ case CSR5_RS_RUNNING_FLUSH:
481
+ return "RUNNING/FLUSH";
482
+
483
+ case CSR5_RS_RUNNING_QUEUE:
484
+ return "RUNNING/QUEUE";
485
+
486
+ default:
487
+ break;
488
+ }
489
+ return "";
490
+}
491
+
492
+static const char *tulip_tx_state_name(int state)
493
+{
494
+ switch (state) {
495
+ case CSR5_TS_STOPPED:
496
+ return "STOPPED";
497
+
498
+ case CSR5_TS_RUNNING_FETCH:
499
+ return "RUNNING/FETCH";
500
+
501
+ case CSR5_TS_RUNNING_WAIT_EOT:
502
+ return "RUNNING/WAIT EOT";
503
+
504
+ case CSR5_TS_RUNNING_READ_BUF:
505
+ return "RUNNING/READ BUF";
506
+
507
+ case CSR5_TS_RUNNING_SETUP:
508
+ return "RUNNING/SETUP";
509
+
510
+ case CSR5_TS_SUSPENDED:
511
+ return "SUSPENDED";
512
+
513
+ case CSR5_TS_RUNNING_CLOSE:
514
+ return "RUNNING/CLOSE";
515
+
516
+ default:
517
+ break;
518
+ }
519
+ return "";
520
+}
521
+
522
+static void tulip_update_rs(TULIPState *s, int state)
523
+{
524
+ s->csr[5] &= ~(CSR5_RS_MASK << CSR5_RS_SHIFT);
525
+ s->csr[5] |= (state & CSR5_RS_MASK) << CSR5_RS_SHIFT;
526
+ trace_tulip_rx_state(tulip_rx_state_name(state));
527
+}
528
+
529
+static uint16_t tulip_mdi_default[] = {
530
+ /* MDI Registers 0 - 6, 7 */
531
+ 0x3100, 0xf02c, 0x7810, 0x0000, 0x0501, 0x4181, 0x0000, 0x0000,
532
+ /* MDI Registers 8 - 15 */
533
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
534
+ /* MDI Registers 16 - 31 */
535
+ 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
536
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
136
+};
537
+};
137
+
538
+
138
+struct bpf_map_def SEC("maps")
539
+/* Readonly mask for MDI (PHY) registers */
139
+tap_rss_map_toeplitz_key = {
540
+static const uint16_t tulip_mdi_mask[] = {
140
+ .type = BPF_MAP_TYPE_ARRAY,
541
+ 0x0000, 0xffff, 0xffff, 0xffff, 0xc01f, 0xffff, 0xffff, 0x0000,
141
+ .key_size = sizeof(__u32),
542
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
142
+ .value_size = sizeof(struct toeplitz_key_data_t),
543
+ 0x0fff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
143
+ .max_entries = 1,
544
+ 0xffff, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
144
+};
545
+};
145
+
546
+
146
+struct bpf_map_def SEC("maps")
547
+static uint16_t tulip_mii_read(TULIPState *s, int phy, int reg)
147
+tap_rss_map_indirection_table = {
548
+{
148
+ .type = BPF_MAP_TYPE_ARRAY,
549
+ uint16_t ret = 0;
149
+ .key_size = sizeof(__u32),
550
+ if (phy == 1) {
150
+ .value_size = sizeof(__u16),
551
+ ret = tulip_mdi_default[reg];
151
+ .max_entries = INDIRECTION_TABLE_SIZE,
552
+ }
553
+ trace_tulip_mii_read(phy, reg, ret);
554
+ return ret;
555
+}
556
+
557
+static void tulip_mii_write(TULIPState *s, int phy, int reg, uint16_t data)
558
+{
559
+ trace_tulip_mii_write(phy, reg, data);
560
+
561
+ if (phy != 1) {
562
+ return;
563
+ }
564
+
565
+ tulip_mdi_default[reg] &= ~tulip_mdi_mask[reg];
566
+ tulip_mdi_default[reg] |= (data & tulip_mdi_mask[reg]);
567
+}
568
+
569
+static void tulip_mii(TULIPState *s)
570
+{
571
+ uint32_t changed = s->old_csr9 ^ s->csr[9];
572
+ uint16_t data;
573
+ int op, phy, reg;
574
+
575
+ if (!(changed & CSR9_MDC)) {
576
+ return;
577
+ }
578
+
579
+ if (!(s->csr[9] & CSR9_MDC)) {
580
+ return;
581
+ }
582
+
583
+ s->mii_bitcnt++;
584
+ s->mii_word <<= 1;
585
+
586
+ if (s->csr[9] & CSR9_MDO && (s->mii_bitcnt < 16 ||
587
+ !(s->csr[9] & CSR9_MII))) {
588
+ /* write op or address bits */
589
+ s->mii_word |= 1;
590
+ }
591
+
592
+ if (s->mii_bitcnt >= 16 && (s->csr[9] & CSR9_MII)) {
593
+ if (s->mii_word & 0x8000) {
594
+ s->csr[9] |= CSR9_MDI;
595
+ } else {
596
+ s->csr[9] &= ~CSR9_MDI;
597
+ }
598
+ }
599
+
600
+ if (s->mii_word == 0xffffffff) {
601
+ s->mii_bitcnt = 0;
602
+ } else if (s->mii_bitcnt == 16) {
603
+ op = (s->mii_word >> 12) & 0x0f;
604
+ phy = (s->mii_word >> 7) & 0x1f;
605
+ reg = (s->mii_word >> 2) & 0x1f;
606
+
607
+ if (op == 6) {
608
+ s->mii_word = tulip_mii_read(s, phy, reg);
609
+ }
610
+ } else if (s->mii_bitcnt == 32) {
611
+ op = (s->mii_word >> 28) & 0x0f;
612
+ phy = (s->mii_word >> 23) & 0x1f;
613
+ reg = (s->mii_word >> 18) & 0x1f;
614
+ data = s->mii_word & 0xffff;
615
+
616
+ if (op == 5) {
617
+ tulip_mii_write(s, phy, reg, data);
618
+ }
619
+ }
620
+}
621
+
622
+static uint32_t tulip_csr9_read(TULIPState *s)
623
+{
624
+ if (s->csr[9] & CSR9_SR) {
625
+ if (eeprom93xx_read(s->eeprom)) {
626
+ s->csr[9] |= CSR9_SR_DO;
627
+ } else {
628
+ s->csr[9] &= ~CSR9_SR_DO;
629
+ }
630
+ }
631
+
632
+ tulip_mii(s);
633
+ return s->csr[9];
634
+}
635
+
636
+static void tulip_update_ts(TULIPState *s, int state)
637
+{
638
+ s->csr[5] &= ~(CSR5_TS_MASK << CSR5_TS_SHIFT);
639
+ s->csr[5] |= (state & CSR5_TS_MASK) << CSR5_TS_SHIFT;
640
+ trace_tulip_tx_state(tulip_tx_state_name(state));
641
+}
642
+
643
+static uint64_t tulip_read(void *opaque, hwaddr addr,
644
+ unsigned size)
645
+{
646
+ TULIPState *s = opaque;
647
+ uint64_t data = 0;
648
+
649
+ switch (addr) {
650
+ case CSR(9):
651
+ data = tulip_csr9_read(s);
652
+ break;
653
+
654
+ case CSR(12):
655
+ /* Fake autocompletion complete until we have PHY emulation */
656
+ data = 5 << CSR12_ANS_SHIFT;
657
+ break;
658
+
659
+ default:
660
+ if (addr & 7) {
661
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: read access at unknown address"
662
+ " 0x%"PRIx64"\n", __func__, addr);
663
+ } else {
664
+ data = s->csr[addr >> 3];
665
+ }
666
+ break;
667
+ }
668
+ trace_tulip_reg_read(addr, tulip_reg_name(addr), size, data);
669
+ return data;
670
+}
671
+
672
+static void tulip_tx(TULIPState *s, struct tulip_descriptor *desc)
673
+{
674
+ if (s->tx_frame_len) {
675
+ if ((s->csr[6] >> CSR6_OM_SHIFT) & CSR6_OM_MASK) {
676
+ /* Internal or external Loopback */
677
+ tulip_receive(s, s->tx_frame, s->tx_frame_len);
678
+ } else {
679
+ qemu_send_packet(qemu_get_queue(s->nic),
680
+ s->tx_frame, s->tx_frame_len);
681
+ }
682
+ }
683
+
684
+ if (desc->control & TDES1_IC) {
685
+ s->csr[5] |= CSR5_TI;
686
+ tulip_update_int(s);
687
+ }
688
+}
689
+
690
+static void tulip_copy_tx_buffers(TULIPState *s, struct tulip_descriptor *desc)
691
+{
692
+ int len1 = (desc->control >> TDES1_BUF1_SIZE_SHIFT) & TDES1_BUF1_SIZE_MASK;
693
+ int len2 = (desc->control >> TDES1_BUF2_SIZE_SHIFT) & TDES1_BUF2_SIZE_MASK;
694
+
695
+ if (len1) {
696
+ pci_dma_read(&s->dev, desc->buf_addr1,
697
+ s->tx_frame + s->tx_frame_len, len1);
698
+ s->tx_frame_len += len1;
699
+ }
700
+
701
+ if (len2) {
702
+ pci_dma_read(&s->dev, desc->buf_addr2,
703
+ s->tx_frame + s->tx_frame_len, len2);
704
+ s->tx_frame_len += len2;
705
+ }
706
+ desc->status = (len1 + len2) ? 0 : 0x7fffffff;
707
+}
708
+
709
+static void tulip_setup_filter_addr(TULIPState *s, uint8_t *buf, int n)
710
+{
711
+ int offset = n * 12;
712
+
713
+ s->filter[n][0] = buf[offset];
714
+ s->filter[n][1] = buf[offset + 1];
715
+
716
+ s->filter[n][2] = buf[offset + 4];
717
+ s->filter[n][3] = buf[offset + 5];
718
+
719
+ s->filter[n][4] = buf[offset + 8];
720
+ s->filter[n][5] = buf[offset + 9];
721
+
722
+ trace_tulip_setup_filter(n, s->filter[n][5], s->filter[n][4],
723
+ s->filter[n][3], s->filter[n][2], s->filter[n][1], s->filter[n][0]);
724
+}
725
+
726
+static void tulip_setup_frame(TULIPState *s,
727
+ struct tulip_descriptor *desc)
728
+{
729
+ uint8_t buf[4096];
730
+ int len = (desc->control >> TDES1_BUF1_SIZE_SHIFT) & TDES1_BUF1_SIZE_MASK;
731
+ int i;
732
+
733
+ trace_tulip_setup_frame();
734
+
735
+ if (len == 192) {
736
+ pci_dma_read(&s->dev, desc->buf_addr1, buf, len);
737
+ for (i = 0; i < 16; i++) {
738
+ tulip_setup_filter_addr(s, buf, i);
739
+ }
740
+ }
741
+
742
+ desc->status = 0x7fffffff;
743
+
744
+ if (desc->control & TDES1_IC) {
745
+ s->csr[5] |= CSR5_TI;
746
+ tulip_update_int(s);
747
+ }
748
+}
749
+
750
+static void tulip_next_tx_descriptor(TULIPState *s,
751
+ struct tulip_descriptor *desc)
752
+{
753
+ if (desc->control & TDES1_TER) {
754
+ s->current_tx_desc = s->csr[4];
755
+ } else if (desc->control & TDES1_TCH) {
756
+ s->current_tx_desc = desc->buf_addr2;
757
+ } else {
758
+ s->current_tx_desc += sizeof(struct tulip_descriptor) +
759
+ (((s->csr[0] >> CSR0_DSL_SHIFT) & CSR0_DSL_MASK) << 2);
760
+ }
761
+ s->current_tx_desc &= ~3ULL;
762
+}
763
+
764
+static uint32_t tulip_ts(TULIPState *s)
765
+{
766
+ return (s->csr[5] >> CSR5_TS_SHIFT) & CSR5_TS_MASK;
767
+}
768
+
769
+static void tulip_xmit_list_update(TULIPState *s)
770
+{
771
+ struct tulip_descriptor desc;
772
+
773
+ if (tulip_ts(s) != CSR5_TS_SUSPENDED) {
774
+ return;
775
+ }
776
+
777
+ for (;;) {
778
+ tulip_desc_read(s, s->current_tx_desc, &desc);
779
+ tulip_dump_tx_descriptor(s, &desc);
780
+
781
+ if (!(desc.status & TDES0_OWN)) {
782
+ tulip_update_ts(s, CSR5_TS_SUSPENDED);
783
+ s->csr[5] |= CSR5_TU;
784
+ tulip_update_int(s);
785
+ return;
786
+ }
787
+
788
+ if (desc.control & TDES1_SET) {
789
+ tulip_setup_frame(s, &desc);
790
+ } else {
791
+ if (desc.control & TDES1_FS) {
792
+ s->tx_frame_len = 0;
793
+ }
794
+
795
+ tulip_copy_tx_buffers(s, &desc);
796
+
797
+ if (desc.control & TDES1_LS) {
798
+ tulip_tx(s, &desc);
799
+ }
800
+ }
801
+ tulip_desc_write(s, s->current_tx_desc, &desc);
802
+ tulip_next_tx_descriptor(s, &desc);
803
+ }
804
+}
805
+
806
+static void tulip_csr9_write(TULIPState *s, uint32_t old_val,
807
+ uint32_t new_val)
808
+{
809
+ if (new_val & CSR9_SR) {
810
+ eeprom93xx_write(s->eeprom,
811
+ !!(new_val & CSR9_SR_CS),
812
+ !!(new_val & CSR9_SR_SK),
813
+ !!(new_val & CSR9_SR_DI));
814
+ }
815
+}
816
+
817
+static void tulip_reset(TULIPState *s)
818
+{
819
+ trace_tulip_reset();
820
+
821
+ s->csr[0] = 0xfe000000;
822
+ s->csr[1] = 0xffffffff;
823
+ s->csr[2] = 0xffffffff;
824
+ s->csr[5] = 0xf0000000;
825
+ s->csr[6] = 0x32000040;
826
+ s->csr[7] = 0xf3fe0000;
827
+ s->csr[8] = 0xe0000000;
828
+ s->csr[9] = 0xfff483ff;
829
+ s->csr[11] = 0xfffe0000;
830
+ s->csr[12] = 0x000000c6;
831
+ s->csr[13] = 0xffff0000;
832
+ s->csr[14] = 0xffffffff;
833
+ s->csr[15] = 0x8ff00000;
834
+}
835
+
836
+static void tulip_qdev_reset(DeviceState *dev)
837
+{
838
+ PCIDevice *d = PCI_DEVICE(dev);
839
+ TULIPState *s = TULIP(d);
840
+
841
+ tulip_reset(s);
842
+}
843
+
844
+static void tulip_write(void *opaque, hwaddr addr,
845
+ uint64_t data, unsigned size)
846
+{
847
+ TULIPState *s = opaque;
848
+ trace_tulip_reg_write(addr, tulip_reg_name(addr), size, data);
849
+
850
+ switch (addr) {
851
+ case CSR(0):
852
+ s->csr[0] = data;
853
+ if (data & CSR0_SWR) {
854
+ tulip_reset(s);
855
+ tulip_update_int(s);
856
+ }
857
+ break;
858
+
859
+ case CSR(1):
860
+ tulip_xmit_list_update(s);
861
+ break;
862
+
863
+ case CSR(2):
864
+ qemu_flush_queued_packets(qemu_get_queue(s->nic));
865
+ break;
866
+
867
+ case CSR(3):
868
+ s->csr[3] = data & ~3ULL;
869
+ s->current_rx_desc = s->csr[3];
870
+ qemu_flush_queued_packets(qemu_get_queue(s->nic));
871
+ break;
872
+
873
+ case CSR(4):
874
+ s->csr[4] = data & ~3ULL;
875
+ s->current_tx_desc = s->csr[4];
876
+ tulip_xmit_list_update(s);
877
+ break;
878
+
879
+ case CSR(5):
880
+ /* Status register, write clears bit */
881
+ s->csr[5] &= ~(data & (CSR5_TI | CSR5_TPS | CSR5_TU | CSR5_TJT |
882
+ CSR5_LNP_ANC | CSR5_UNF | CSR5_RI | CSR5_RU |
883
+ CSR5_RPS | CSR5_RWT | CSR5_ETI | CSR5_GTE |
884
+ CSR5_LNF | CSR5_FBE | CSR5_ERI | CSR5_AIS |
885
+ CSR5_NIS | CSR5_GPI | CSR5_LC));
886
+ tulip_update_int(s);
887
+ break;
888
+
889
+ case CSR(6):
890
+ s->csr[6] = data;
891
+ if (s->csr[6] & CSR6_SR) {
892
+ tulip_update_rs(s, CSR5_RS_RUNNING_WAIT_RECEIVE);
893
+ qemu_flush_queued_packets(qemu_get_queue(s->nic));
894
+ } else {
895
+ tulip_update_rs(s, CSR5_RS_STOPPED);
896
+ }
897
+
898
+ if (s->csr[6] & CSR6_ST) {
899
+ tulip_update_ts(s, CSR5_TS_SUSPENDED);
900
+ tulip_xmit_list_update(s);
901
+ } else {
902
+ tulip_update_ts(s, CSR5_TS_STOPPED);
903
+ }
904
+ break;
905
+
906
+ case CSR(7):
907
+ s->csr[7] = data;
908
+ tulip_update_int(s);
909
+ break;
910
+
911
+ case CSR(8):
912
+ s->csr[9] = data;
913
+ break;
914
+
915
+ case CSR(9):
916
+ tulip_csr9_write(s, s->csr[9], data);
917
+ /* don't clear MII read data */
918
+ s->csr[9] &= CSR9_MDI;
919
+ s->csr[9] |= (data & ~CSR9_MDI);
920
+ tulip_mii(s);
921
+ s->old_csr9 = s->csr[9];
922
+ break;
923
+
924
+ case CSR(10):
925
+ s->csr[10] = data;
926
+ break;
927
+
928
+ case CSR(11):
929
+ s->csr[11] = data;
930
+ break;
931
+
932
+ case CSR(12):
933
+ /* SIA Status register, some bits are cleared by writing 1 */
934
+ s->csr[12] &= ~(data & (CSR12_MRA | CSR12_TRA | CSR12_ARA));
935
+ break;
936
+
937
+ case CSR(13):
938
+ s->csr[13] = data;
939
+ break;
940
+
941
+ case CSR(14):
942
+ s->csr[14] = data;
943
+ break;
944
+
945
+ case CSR(15):
946
+ s->csr[15] = data;
947
+ break;
948
+
949
+ default:
950
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: write to CSR at unknown address "
951
+ "0x%"PRIx64"\n", __func__, addr);
952
+ break;
953
+ }
954
+}
955
+
956
+static const MemoryRegionOps tulip_ops = {
957
+ .read = tulip_read,
958
+ .write = tulip_write,
959
+ .endianness = DEVICE_LITTLE_ENDIAN,
960
+ .impl = {
961
+ .min_access_size = 4,
962
+ .max_access_size = 4,
963
+ },
152
+};
964
+};
153
+
965
+
154
+static inline void net_rx_rss_add_chunk(__u8 *rss_input, size_t *bytes_written,
966
+static void tulip_idblock_crc(TULIPState *s, uint16_t *srom)
155
+ const void *ptr, size_t size) {
967
+{
156
+ __builtin_memcpy(&rss_input[*bytes_written], ptr, size);
968
+ int word, n;
157
+ *bytes_written += size;
969
+ int bit;
158
+}
970
+ unsigned char bitval, crc;
159
+
971
+ const int len = 9;
160
+static inline
972
+ n = 0;
161
+void net_toeplitz_add(__u32 *result,
973
+ crc = -1;
162
+ __u8 *input,
974
+
163
+ __u32 len
975
+ for (word = 0; word < len; word++) {
164
+ , struct toeplitz_key_data_t *key) {
976
+ for (bit = 15; bit >= 0; bit--) {
165
+
977
+ if ((word == (len - 1)) && (bit == 7)) {
166
+ __u32 accumulator = *result;
978
+ /*
167
+ __u32 leftmost_32_bits = key->leftmost_32_bits;
979
+ * Insert the correct CRC result into input data stream
168
+ __u32 byte;
980
+ * in place.
169
+
981
+ */
170
+ for (byte = 0; byte < HASH_CALCULATION_BUFFER_SIZE; byte++) {
982
+ srom[len - 1] = (srom[len - 1] & 0xff00) | (unsigned short)crc;
171
+ __u8 input_byte = input[byte];
983
+ break;
172
+ __u8 key_byte = key->next_byte[byte];
984
+ }
173
+ __u8 bit;
985
+ n++;
174
+
986
+ bitval = ((srom[word] >> bit) & 1) ^ ((crc >> 7) & 1);
987
+ crc = crc << 1;
988
+ if (bitval == 1) {
989
+ crc ^= 6;
990
+ crc |= 0x01;
991
+ }
992
+ }
993
+ }
994
+}
995
+
996
+static uint16_t tulip_srom_crc(TULIPState *s, uint8_t *eeprom, size_t len)
997
+{
998
+ unsigned long crc = 0xffffffff;
999
+ unsigned long flippedcrc = 0;
1000
+ unsigned char currentbyte;
1001
+ unsigned int msb, bit, i;
1002
+
1003
+ for (i = 0; i < len; i++) {
1004
+ currentbyte = eeprom[i];
175
+ for (bit = 0; bit < 8; bit++) {
1005
+ for (bit = 0; bit < 8; bit++) {
176
+ if (input_byte & (1 << 7)) {
1006
+ msb = (crc >> 31) & 1;
177
+ accumulator ^= leftmost_32_bits;
1007
+ crc <<= 1;
1008
+ if (msb ^ (currentbyte & 1)) {
1009
+ crc ^= 0x04c11db6;
1010
+ crc |= 0x00000001;
178
+ }
1011
+ }
179
+
1012
+ currentbyte >>= 1;
180
+ leftmost_32_bits =
1013
+ }
181
+ (leftmost_32_bits << 1) | ((key_byte & (1 << 7)) >> 7);
1014
+ }
182
+
1015
+
183
+ input_byte <<= 1;
1016
+ for (i = 0; i < 32; i++) {
184
+ key_byte <<= 1;
1017
+ flippedcrc <<= 1;
185
+ }
1018
+ bit = crc & 1;
186
+ }
1019
+ crc >>= 1;
187
+
1020
+ flippedcrc += bit;
188
+ *result = accumulator;
1021
+ }
189
+}
1022
+ return (flippedcrc ^ 0xffffffff) & 0xffff;
190
+
1023
+}
191
+
1024
+
192
+static inline int ip6_extension_header_type(__u8 hdr_type)
1025
+static const uint8_t eeprom_default[128] = {
193
+{
1026
+ 0x3c, 0x10, 0x4f, 0x10, 0x00, 0x00, 0x00, 0x00,
194
+ switch (hdr_type) {
1027
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
195
+ case IPPROTO_HOPOPTS:
1028
+ 0x56, 0x08, 0x04, 0x01, 0x00, 0x80, 0x48, 0xb3,
196
+ case IPPROTO_ROUTING:
1029
+ 0x0e, 0xa7, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x08,
197
+ case IPPROTO_FRAGMENT:
1030
+ 0x01, 0x8d, 0x03, 0x00, 0x00, 0x00, 0x00, 0x78,
198
+ case IPPROTO_ICMPV6:
1031
+ 0xe0, 0x01, 0x00, 0x50, 0x00, 0x18, 0x00, 0x00,
199
+ case IPPROTO_NONE:
1032
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
200
+ case IPPROTO_DSTOPTS:
1033
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
201
+ case IPPROTO_MH:
1034
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
202
+ return 1;
1035
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
203
+ default:
1036
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
204
+ return 0;
1037
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe8, 0x6b,
205
+ }
1038
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
206
+}
1039
+ 0x48, 0xb3, 0x0e, 0xa7, 0x40, 0x00, 0x00, 0x00,
207
+/*
1040
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
208
+ * According to
1041
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
209
+ * https://www.iana.org/assignments/ipv6-parameters/ipv6-parameters.xhtml
1042
+};
210
+ * we expect that there are would be no more than 11 extensions in IPv6 header,
1043
+
211
+ * also there is 27 TLV options for Destination and Hop-by-hop extensions.
1044
+static void tulip_fill_eeprom(TULIPState *s)
212
+ * Need to choose reasonable amount of maximum extensions/options we may
1045
+{
213
+ * check to find ext src/dst.
1046
+ uint16_t *eeprom = eeprom93xx_data(s->eeprom);
214
+ */
1047
+ memcpy(eeprom, eeprom_default, 128);
215
+#define IP6_EXTENSIONS_COUNT 11
1048
+
216
+#define IP6_OPTIONS_COUNT 30
1049
+ /* patch in our mac address */
217
+
1050
+ eeprom[10] = cpu_to_le16(s->c.macaddr.a[0] | (s->c.macaddr.a[1] << 8));
218
+static inline int parse_ipv6_ext(struct __sk_buff *skb,
1051
+ eeprom[11] = cpu_to_le16(s->c.macaddr.a[2] | (s->c.macaddr.a[3] << 8));
219
+ struct packet_hash_info_t *info,
1052
+ eeprom[12] = cpu_to_le16(s->c.macaddr.a[4] | (s->c.macaddr.a[5] << 8));
220
+ __u8 *l4_protocol, size_t *l4_offset)
1053
+ tulip_idblock_crc(s, eeprom);
221
+{
1054
+ eeprom[63] = cpu_to_le16(tulip_srom_crc(s, (uint8_t *)eeprom, 126));
222
+ int err = 0;
1055
+}
223
+
1056
+
224
+ if (!ip6_extension_header_type(*l4_protocol)) {
1057
+static void pci_tulip_realize(PCIDevice *pci_dev, Error **errp)
225
+ return 0;
1058
+{
226
+ }
1059
+ TULIPState *s = DO_UPCAST(TULIPState, dev, pci_dev);
227
+
1060
+ uint8_t *pci_conf;
228
+ struct ipv6_opt_hdr ext_hdr = {};
1061
+
229
+
1062
+ pci_conf = s->dev.config;
230
+ for (unsigned int i = 0; i < IP6_EXTENSIONS_COUNT; ++i) {
1063
+ pci_conf[PCI_INTERRUPT_PIN] = 1; /* interrupt pin A */
231
+
1064
+
232
+ err = bpf_skb_load_bytes_relative(skb, *l4_offset, &ext_hdr,
1065
+ s->eeprom = eeprom93xx_new(&pci_dev->qdev, 64);
233
+ sizeof(ext_hdr), BPF_HDR_START_NET);
1066
+ tulip_fill_eeprom(s);
234
+ if (err) {
1067
+
235
+ goto error;
1068
+ memory_region_init_io(&s->io, OBJECT(&s->dev), &tulip_ops, s,
236
+ }
1069
+ "tulip-io", 128);
237
+
1070
+
238
+ if (*l4_protocol == IPPROTO_ROUTING) {
1071
+ memory_region_init_io(&s->memory, OBJECT(&s->dev), &tulip_ops, s,
239
+ struct ipv6_rt_hdr ext_rt = {};
1072
+ "tulip-mem", 128);
240
+
1073
+
241
+ err = bpf_skb_load_bytes_relative(skb, *l4_offset, &ext_rt,
1074
+ pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io);
242
+ sizeof(ext_rt), BPF_HDR_START_NET);
1075
+ pci_register_bar(&s->dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->memory);
243
+ if (err) {
1076
+
244
+ goto error;
1077
+ s->irq = pci_allocate_irq(&s->dev);
245
+ }
1078
+
246
+
1079
+ qemu_macaddr_default_if_unset(&s->c.macaddr);
247
+ if ((ext_rt.type == IPV6_SRCRT_TYPE_2) &&
1080
+
248
+ (ext_rt.hdrlen == sizeof(struct in6_addr) / 8) &&
1081
+ s->nic = qemu_new_nic(&net_tulip_info, &s->c,
249
+ (ext_rt.segments_left == 1)) {
1082
+ object_get_typename(OBJECT(pci_dev)),
250
+
1083
+ pci_dev->qdev.id, s);
251
+ err = bpf_skb_load_bytes_relative(skb,
1084
+ qemu_format_nic_info_str(qemu_get_queue(s->nic), s->c.macaddr.a);
252
+ *l4_offset + offsetof(struct rt2_hdr, addr),
1085
+}
253
+ &info->in6_ext_dst, sizeof(info->in6_ext_dst),
1086
+
254
+ BPF_HDR_START_NET);
1087
+static void pci_tulip_exit(PCIDevice *pci_dev)
255
+ if (err) {
1088
+{
256
+ goto error;
1089
+ TULIPState *s = DO_UPCAST(TULIPState, dev, pci_dev);
257
+ }
1090
+
258
+
1091
+ qemu_del_nic(s->nic);
259
+ info->is_ipv6_ext_dst = 1;
1092
+ qemu_free_irq(s->irq);
260
+ }
1093
+ eeprom93xx_free(&pci_dev->qdev, s->eeprom);
261
+
1094
+}
262
+ } else if (*l4_protocol == IPPROTO_DSTOPTS) {
1095
+
263
+ struct ipv6_opt_t {
1096
+static void tulip_instance_init(Object *obj)
264
+ __u8 type;
1097
+{
265
+ __u8 length;
1098
+ PCIDevice *pci_dev = PCI_DEVICE(obj);
266
+ } __attribute__((packed)) opt = {};
1099
+ TULIPState *d = DO_UPCAST(TULIPState, dev, pci_dev);
267
+
1100
+
268
+ size_t opt_offset = sizeof(ext_hdr);
1101
+ device_add_bootindex_property(obj, &d->c.bootindex,
269
+
1102
+ "bootindex", "/ethernet-phy@0",
270
+ for (unsigned int j = 0; j < IP6_OPTIONS_COUNT; ++j) {
1103
+ &pci_dev->qdev, NULL);
271
+ err = bpf_skb_load_bytes_relative(skb, *l4_offset + opt_offset,
1104
+}
272
+ &opt, sizeof(opt), BPF_HDR_START_NET);
1105
+
273
+ if (err) {
1106
+static Property tulip_properties[] = {
274
+ goto error;
1107
+ DEFINE_NIC_PROPERTIES(TULIPState, c),
275
+ }
1108
+ DEFINE_PROP_END_OF_LIST(),
276
+
1109
+};
277
+ if (opt.type == IPV6_TLV_HAO) {
1110
+
278
+ err = bpf_skb_load_bytes_relative(skb,
1111
+static void tulip_class_init(ObjectClass *klass, void *data)
279
+ *l4_offset + opt_offset
1112
+{
280
+ + offsetof(struct ipv6_destopt_hao, addr),
1113
+ DeviceClass *dc = DEVICE_CLASS(klass);
281
+ &info->in6_ext_src, sizeof(info->in6_ext_src),
1114
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
282
+ BPF_HDR_START_NET);
1115
+
283
+ if (err) {
1116
+ k->realize = pci_tulip_realize;
284
+ goto error;
1117
+ k->exit = pci_tulip_exit;
285
+ }
1118
+ k->vendor_id = PCI_VENDOR_ID_DEC;
286
+
1119
+ k->device_id = PCI_DEVICE_ID_DEC_21143;
287
+ info->is_ipv6_ext_src = 1;
1120
+ k->subsystem_vendor_id = 0x103c;
288
+ break;
1121
+ k->subsystem_id = 0x104f;
289
+ }
1122
+ k->class_id = PCI_CLASS_NETWORK_ETHERNET;
290
+
1123
+ dc->vmsd = &vmstate_pci_tulip;
291
+ opt_offset += (opt.type == IPV6_TLV_PAD1) ?
1124
+ dc->props = tulip_properties;
292
+ 1 : opt.length + sizeof(opt);
1125
+ dc->reset = tulip_qdev_reset;
293
+
1126
+ set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
294
+ if (opt_offset + 1 >= ext_hdr.hdrlen * 8) {
1127
+}
295
+ break;
1128
+
296
+ }
1129
+static const TypeInfo tulip_info = {
297
+ }
1130
+ .name = TYPE_TULIP,
298
+ } else if (*l4_protocol == IPPROTO_FRAGMENT) {
1131
+ .parent = TYPE_PCI_DEVICE,
299
+ info->is_fragmented = true;
1132
+ .instance_size = sizeof(TULIPState),
300
+ }
1133
+ .class_init = tulip_class_init,
301
+
1134
+ .instance_init = tulip_instance_init,
302
+ *l4_protocol = ext_hdr.nexthdr;
1135
+ .interfaces = (InterfaceInfo[]) {
303
+ *l4_offset += (ext_hdr.hdrlen + 1) * 8;
1136
+ { INTERFACE_CONVENTIONAL_PCI_DEVICE },
304
+
1137
+ { },
305
+ if (!ip6_extension_header_type(ext_hdr.nexthdr)) {
1138
+ },
306
+ return 0;
1139
+};
307
+ }
1140
+
308
+ }
1141
+static void tulip_register_types(void)
309
+
1142
+{
310
+ return 0;
1143
+ type_register_static(&tulip_info);
311
+error:
1144
+}
312
+ return err;
1145
+
313
+}
1146
+type_init(tulip_register_types)
314
+
1147
diff --git a/hw/net/tulip.h b/hw/net/tulip.h
315
+static __be16 parse_eth_type(struct __sk_buff *skb)
1148
new file mode 100644
316
+{
1149
index XXXXXXX..XXXXXXX
317
+ unsigned int offset = 12;
1150
--- /dev/null
318
+ __be16 ret = 0;
1151
+++ b/hw/net/tulip.h
319
+ int err = 0;
1152
@@ -XXX,XX +XXX,XX @@
320
+
1153
+#ifndef HW_TULIP_H
321
+ err = bpf_skb_load_bytes_relative(skb, offset, &ret, sizeof(ret),
1154
+#define HW_TULIP_H
322
+ BPF_HDR_START_MAC);
1155
+
323
+ if (err) {
1156
+#include "qemu/units.h"
324
+ return 0;
1157
+#include "net/net.h"
325
+ }
1158
+
326
+
1159
+#define TYPE_TULIP "tulip"
327
+ switch (bpf_ntohs(ret)) {
1160
+#define TULIP(obj) OBJECT_CHECK(TULIPState, (obj), TYPE_TULIP)
328
+ case ETH_P_8021AD:
1161
+
329
+ offset += 4;
1162
+#define CSR(_x) ((_x) << 3)
330
+ case ETH_P_8021Q:
1163
+
331
+ offset += 4;
1164
+#define CSR0_SWR BIT(0)
332
+ err = bpf_skb_load_bytes_relative(skb, offset, &ret, sizeof(ret),
1165
+#define CSR0_BAR BIT(1)
333
+ BPF_HDR_START_MAC);
1166
+#define CSR0_DSL_SHIFT 2
334
+ default:
1167
+#define CSR0_DSL_MASK 0x1f
335
+ break;
1168
+#define CSR0_BLE BIT(7)
336
+ }
1169
+#define CSR0_PBL_SHIFT 8
337
+
1170
+#define CSR0_PBL_MASK 0x3f
338
+ if (err) {
1171
+#define CSR0_CAC_SHIFT 14
339
+ return 0;
1172
+#define CSR0_CAC_MASK 0x3
340
+ }
1173
+#define CSR0_DAS 0x10000
341
+
1174
+#define CSR0_TAP_SHIFT 17
342
+ return ret;
1175
+#define CSR0_TAP_MASK 0x7
343
+}
1176
+#define CSR0_DBO 0x100000
344
+
1177
+#define CSR1_TPD 0x01
345
+static inline int parse_packet(struct __sk_buff *skb,
1178
+#define CSR0_RLE BIT(23)
346
+ struct packet_hash_info_t *info)
1179
+#define CSR0_WIE BIT(24)
347
+{
1180
+
348
+ int err = 0;
1181
+#define CSR2_RPD 0x01
349
+
1182
+
350
+ if (!info || !skb) {
1183
+#define CSR5_TI BIT(0)
351
+ return -1;
1184
+#define CSR5_TPS BIT(1)
352
+ }
1185
+#define CSR5_TU BIT(2)
353
+
1186
+#define CSR5_TJT BIT(3)
354
+ size_t l4_offset = 0;
1187
+#define CSR5_LNP_ANC BIT(4)
355
+ __u8 l4_protocol = 0;
1188
+#define CSR5_UNF BIT(5)
356
+ __u16 l3_protocol = bpf_ntohs(parse_eth_type(skb));
1189
+#define CSR5_RI BIT(6)
357
+ if (l3_protocol == 0) {
1190
+#define CSR5_RU BIT(7)
358
+ err = -1;
1191
+#define CSR5_RPS BIT(8)
359
+ goto error;
1192
+#define CSR5_RWT BIT(9)
360
+ }
1193
+#define CSR5_ETI BIT(10)
361
+
1194
+#define CSR5_GTE BIT(11)
362
+ if (l3_protocol == ETH_P_IP) {
1195
+#define CSR5_LNF BIT(12)
363
+ info->is_ipv4 = 1;
1196
+#define CSR5_FBE BIT(13)
364
+
1197
+#define CSR5_ERI BIT(14)
365
+ struct iphdr ip = {};
1198
+#define CSR5_AIS BIT(15)
366
+ err = bpf_skb_load_bytes_relative(skb, 0, &ip, sizeof(ip),
1199
+#define CSR5_NIS BIT(16)
367
+ BPF_HDR_START_NET);
1200
+#define CSR5_RS_SHIFT 17
368
+ if (err) {
1201
+#define CSR5_RS_MASK 7
369
+ goto error;
1202
+#define CSR5_TS_SHIFT 20
370
+ }
1203
+#define CSR5_TS_MASK 7
371
+
1204
+
372
+ info->in_src = ip.saddr;
1205
+#define CSR5_TS_STOPPED 0
373
+ info->in_dst = ip.daddr;
1206
+#define CSR5_TS_RUNNING_FETCH 1
374
+ info->is_fragmented = !!ip.frag_off;
1207
+#define CSR5_TS_RUNNING_WAIT_EOT 2
375
+
1208
+#define CSR5_TS_RUNNING_READ_BUF 3
376
+ l4_protocol = ip.protocol;
1209
+#define CSR5_TS_RUNNING_SETUP 5
377
+ l4_offset = ip.ihl * 4;
1210
+#define CSR5_TS_SUSPENDED 6
378
+ } else if (l3_protocol == ETH_P_IPV6) {
1211
+#define CSR5_TS_RUNNING_CLOSE 7
379
+ info->is_ipv6 = 1;
1212
+
380
+
1213
+#define CSR5_RS_STOPPED 0
381
+ struct ipv6hdr ip6 = {};
1214
+#define CSR5_RS_RUNNING_FETCH 1
382
+ err = bpf_skb_load_bytes_relative(skb, 0, &ip6, sizeof(ip6),
1215
+#define CSR5_RS_RUNNING_CHECK_EOR 2
383
+ BPF_HDR_START_NET);
1216
+#define CSR5_RS_RUNNING_WAIT_RECEIVE 3
384
+ if (err) {
1217
+#define CSR5_RS_SUSPENDED 4
385
+ goto error;
1218
+#define CSR5_RS_RUNNING_CLOSE 5
386
+ }
1219
+#define CSR5_RS_RUNNING_FLUSH 6
387
+
1220
+#define CSR5_RS_RUNNING_QUEUE 7
388
+ info->in6_src = ip6.saddr;
1221
+
389
+ info->in6_dst = ip6.daddr;
1222
+#define CSR5_EB_SHIFT 23
390
+
1223
+#define CSR5_EB_MASK 7
391
+ l4_protocol = ip6.nexthdr;
1224
+
392
+ l4_offset = sizeof(ip6);
1225
+#define CSR5_GPI BIT(26)
393
+
1226
+#define CSR5_LC BIT(27)
394
+ err = parse_ipv6_ext(skb, info, &l4_protocol, &l4_offset);
1227
+
395
+ if (err) {
1228
+#define CSR6_HP BIT(0)
396
+ goto error;
1229
+#define CSR6_SR BIT(1)
397
+ }
1230
+#define CSR6_HO BIT(2)
398
+ }
1231
+#define CSR6_PB BIT(3)
399
+
1232
+#define CSR6_IF BIT(4)
400
+ if (l4_protocol != 0 && !info->is_fragmented) {
1233
+#define CSR6_SB BIT(5)
401
+ if (l4_protocol == IPPROTO_TCP) {
1234
+#define CSR6_PR BIT(6)
402
+ info->is_tcp = 1;
1235
+#define CSR6_PM BIT(7)
403
+
1236
+#define CSR6_FKD BIT(8)
404
+ struct tcphdr tcp = {};
1237
+#define CSR6_FD BIT(9)
405
+ err = bpf_skb_load_bytes_relative(skb, l4_offset, &tcp, sizeof(tcp),
1238
+
406
+ BPF_HDR_START_NET);
1239
+#define CSR6_OM_SHIFT 10
407
+ if (err) {
1240
+#define CSR6_OM_MASK 3
408
+ goto error;
1241
+#define CSR6_OM_NORMAL 0
409
+ }
1242
+#define CSR6_OM_INT_LOOPBACK 1
410
+
1243
+#define CSR6_OM_EXT_LOOPBACK 2
411
+ info->src_port = tcp.source;
1244
+
412
+ info->dst_port = tcp.dest;
1245
+#define CSR6_FC BIT(12)
413
+ } else if (l4_protocol == IPPROTO_UDP) { /* TODO: add udplite? */
1246
+#define CSR6_ST BIT(13)
414
+ info->is_udp = 1;
1247
+
415
+
1248
+
416
+ struct udphdr udp = {};
1249
+#define CSR6_TR_SHIFT 14
417
+ err = bpf_skb_load_bytes_relative(skb, l4_offset, &udp, sizeof(udp),
1250
+#define CSR6_TR_MASK 3
418
+ BPF_HDR_START_NET);
1251
+#define CSR6_TR_72 0
419
+ if (err) {
1252
+#define CSR6_TR_96 1
420
+ goto error;
1253
+#define CSR6_TR_128 2
421
+ }
1254
+#define CSR6_TR_160 3
422
+
1255
+
423
+ info->src_port = udp.source;
1256
+#define CSR6_CA BIT(17)
424
+ info->dst_port = udp.dest;
1257
+#define CSR6_RA BIT(30)
425
+ }
1258
+#define CSR6_SC BIT(31)
426
+ }
1259
+
427
+
1260
+#define CSR7_TIM BIT(0)
428
+ return 0;
1261
+#define CSR7_TSM BIT(1)
429
+
1262
+#define CSR7_TUM BIT(2)
430
+error:
1263
+#define CSR7_TJM BIT(3)
431
+ return err;
1264
+#define CSR7_LPM BIT(4)
432
+}
1265
+#define CSR7_UNM BIT(5)
433
+
1266
+#define CSR7_RIM BIT(6)
434
+static inline __u32 calculate_rss_hash(struct __sk_buff *skb,
1267
+#define CSR7_RUM BIT(7)
435
+ struct rss_config_t *config, struct toeplitz_key_data_t *toe)
1268
+#define CSR7_RSM BIT(8)
436
+{
1269
+#define CSR7_RWM BIT(9)
437
+ __u8 rss_input[HASH_CALCULATION_BUFFER_SIZE] = {};
1270
+#define CSR7_TMM BIT(11)
438
+ size_t bytes_written = 0;
1271
+#define CSR7_LFM BIT(12)
439
+ __u32 result = 0;
1272
+#define CSR7_SEM BIT(13)
440
+ int err = 0;
1273
+#define CSR7_ERM BIT(14)
441
+ struct packet_hash_info_t packet_info = {};
1274
+#define CSR7_AIM BIT(15)
442
+
1275
+#define CSR7_NIM BIT(16)
443
+ err = parse_packet(skb, &packet_info);
1276
+
444
+ if (err) {
1277
+#define CSR8_MISSED_FRAME_OVL BIT(16)
445
+ return 0;
1278
+#define CSR8_MISSED_FRAME_CNT_MASK 0xffff
446
+ }
1279
+
447
+
1280
+#define CSR9_DATA_MASK 0xff
448
+ if (packet_info.is_ipv4) {
1281
+#define CSR9_SR_CS BIT(0)
449
+ if (packet_info.is_tcp &&
1282
+#define CSR9_SR_SK BIT(1)
450
+ config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_TCPv4) {
1283
+#define CSR9_SR_DI BIT(2)
451
+
1284
+#define CSR9_SR_DO BIT(3)
452
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
1285
+#define CSR9_REG BIT(10)
453
+ &packet_info.in_src,
1286
+#define CSR9_SR BIT(11)
454
+ sizeof(packet_info.in_src));
1287
+#define CSR9_BR BIT(12)
455
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
1288
+#define CSR9_WR BIT(13)
456
+ &packet_info.in_dst,
1289
+#define CSR9_RD BIT(14)
457
+ sizeof(packet_info.in_dst));
1290
+#define CSR9_MOD BIT(15)
458
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
1291
+#define CSR9_MDC BIT(16)
459
+ &packet_info.src_port,
1292
+#define CSR9_MDO BIT(17)
460
+ sizeof(packet_info.src_port));
1293
+#define CSR9_MII BIT(18)
461
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
1294
+#define CSR9_MDI BIT(19)
462
+ &packet_info.dst_port,
1295
+
463
+ sizeof(packet_info.dst_port));
1296
+#define CSR11_CON BIT(16)
464
+ } else if (packet_info.is_udp &&
1297
+#define CSR11_TIMER_MASK 0xffff
465
+ config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_UDPv4) {
1298
+
466
+
1299
+#define CSR12_MRA BIT(0)
467
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
1300
+#define CSR12_LS100 BIT(1)
468
+ &packet_info.in_src,
1301
+#define CSR12_LS10 BIT(2)
469
+ sizeof(packet_info.in_src));
1302
+#define CSR12_APS BIT(3)
470
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
1303
+#define CSR12_ARA BIT(8)
471
+ &packet_info.in_dst,
1304
+#define CSR12_TRA BIT(9)
472
+ sizeof(packet_info.in_dst));
1305
+#define CSR12_NSN BIT(10)
473
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
1306
+#define CSR12_TRF BIT(11)
474
+ &packet_info.src_port,
1307
+#define CSR12_ANS_SHIFT 12
475
+ sizeof(packet_info.src_port));
1308
+#define CSR12_ANS_MASK 7
476
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
1309
+#define CSR12_LPN BIT(15)
477
+ &packet_info.dst_port,
1310
+#define CSR12_LPC_SHIFT 16
478
+ sizeof(packet_info.dst_port));
1311
+#define CSR12_LPC_MASK 0xffff
479
+ } else if (config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_IPv4) {
1312
+
480
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
1313
+#define CSR13_SRL BIT(0)
481
+ &packet_info.in_src,
1314
+#define CSR13_CAC BIT(2)
482
+ sizeof(packet_info.in_src));
1315
+#define CSR13_AUI BIT(3)
483
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
1316
+#define CSR13_SDM_SHIFT 4
484
+ &packet_info.in_dst,
1317
+#define CSR13_SDM_MASK 0xfff
485
+ sizeof(packet_info.in_dst));
1318
+
486
+ }
1319
+#define CSR14_ECEN BIT(0)
487
+ } else if (packet_info.is_ipv6) {
1320
+#define CSR14_LBK BIT(1)
488
+ if (packet_info.is_tcp &&
1321
+#define CSR14_DREN BIT(2)
489
+ config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_TCPv6) {
1322
+#define CSR14_LSE BIT(3)
490
+
1323
+#define CSR14_CPEN_SHIFT 4
491
+ if (packet_info.is_ipv6_ext_src &&
1324
+#define CSR14_CPEN_MASK 3
492
+ config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_TCP_EX) {
1325
+#define CSR14_MBO BIT(6)
493
+
1326
+#define CSR14_ANE BIT(7)
494
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
1327
+#define CSR14_RSQ BIT(8)
495
+ &packet_info.in6_ext_src,
1328
+#define CSR14_CSQ BIT(9)
496
+ sizeof(packet_info.in6_ext_src));
1329
+#define CSR14_CLD BIT(10)
497
+ } else {
1330
+#define CSR14_SQE BIT(11)
498
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
1331
+#define CSR14_LTE BIT(12)
499
+ &packet_info.in6_src,
1332
+#define CSR14_APE BIT(13)
500
+ sizeof(packet_info.in6_src));
1333
+#define CSR14_SPP BIT(14)
501
+ }
1334
+#define CSR14_TAS BIT(15)
502
+ if (packet_info.is_ipv6_ext_dst &&
1335
+
503
+ config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_TCP_EX) {
1336
+#define CSR15_JBD BIT(0)
504
+
1337
+#define CSR15_HUJ BIT(1)
505
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
1338
+#define CSR15_JCK BIT(2)
506
+ &packet_info.in6_ext_dst,
1339
+#define CSR15_ABM BIT(3)
507
+ sizeof(packet_info.in6_ext_dst));
1340
+#define CSR15_RWD BIT(4)
508
+ } else {
1341
+#define CSR15_RWR BIT(5)
509
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
1342
+#define CSR15_LE1 BIT(6)
510
+ &packet_info.in6_dst,
1343
+#define CSR15_LV1 BIT(7)
511
+ sizeof(packet_info.in6_dst));
1344
+#define CSR15_TSCK BIT(8)
512
+ }
1345
+#define CSR15_FUSQ BIT(9)
513
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
1346
+#define CSR15_FLF BIT(10)
514
+ &packet_info.src_port,
1347
+#define CSR15_LSD BIT(11)
515
+ sizeof(packet_info.src_port));
1348
+#define CSR15_DPST BIT(12)
516
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
1349
+#define CSR15_FRL BIT(13)
517
+ &packet_info.dst_port,
1350
+#define CSR15_LE2 BIT(14)
518
+ sizeof(packet_info.dst_port));
1351
+#define CSR15_LV2 BIT(15)
519
+ } else if (packet_info.is_udp &&
1352
+
520
+ config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_UDPv6) {
1353
+#define RDES0_OF BIT(0)
521
+
1354
+#define RDES0_CE BIT(1)
522
+ if (packet_info.is_ipv6_ext_src &&
1355
+#define RDES0_DB BIT(2)
523
+ config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_UDP_EX) {
1356
+#define RDES0_RJ BIT(4)
524
+
1357
+#define RDES0_FT BIT(5)
525
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
1358
+#define RDES0_CS BIT(6)
526
+ &packet_info.in6_ext_src,
1359
+#define RDES0_TL BIT(7)
527
+ sizeof(packet_info.in6_ext_src));
1360
+#define RDES0_LS BIT(8)
528
+ } else {
1361
+#define RDES0_FS BIT(9)
529
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
1362
+#define RDES0_MF BIT(10)
530
+ &packet_info.in6_src,
1363
+#define RDES0_RF BIT(11)
531
+ sizeof(packet_info.in6_src));
1364
+#define RDES0_DT_SHIFT 12
532
+ }
1365
+#define RDES0_DT_MASK 3
533
+ if (packet_info.is_ipv6_ext_dst &&
1366
+#define RDES0_LE BIT(14)
534
+ config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_UDP_EX) {
1367
+#define RDES0_ES BIT(15)
535
+
1368
+#define RDES0_FL_SHIFT 16
536
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
1369
+#define RDES0_FL_MASK 0x3fff
537
+ &packet_info.in6_ext_dst,
1370
+#define RDES0_FF BIT(30)
538
+ sizeof(packet_info.in6_ext_dst));
1371
+#define RDES0_OWN BIT(31)
539
+ } else {
1372
+
540
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
1373
+#define RDES1_BUF1_SIZE_SHIFT 0
541
+ &packet_info.in6_dst,
1374
+#define RDES1_BUF1_SIZE_MASK 0x7ff
542
+ sizeof(packet_info.in6_dst));
1375
+
543
+ }
1376
+#define RDES1_BUF2_SIZE_SHIFT 11
544
+
1377
+#define RDES1_BUF2_SIZE_MASK 0x7ff
545
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
1378
+#define RDES1_RCH BIT(24)
546
+ &packet_info.src_port,
1379
+#define RDES1_RER BIT(25)
547
+ sizeof(packet_info.src_port));
1380
+
548
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
1381
+#define TDES0_DE BIT(0)
549
+ &packet_info.dst_port,
1382
+#define TDES0_UF BIT(1)
550
+ sizeof(packet_info.dst_port));
1383
+#define TDES0_LF BIT(2)
551
+
1384
+#define TDES0_CC_SHIFT 3
552
+ } else if (config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_IPv6) {
1385
+#define TDES0_CC_MASK 0xf
553
+ if (packet_info.is_ipv6_ext_src &&
1386
+#define TDES0_HF BIT(7)
554
+ config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_IP_EX) {
1387
+#define TDES0_EC BIT(8)
555
+
1388
+#define TDES0_LC BIT(9)
556
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
1389
+#define TDES0_NC BIT(10)
557
+ &packet_info.in6_ext_src,
1390
+#define TDES0_LO BIT(11)
558
+ sizeof(packet_info.in6_ext_src));
1391
+#define TDES0_TO BIT(14)
559
+ } else {
1392
+#define TDES0_ES BIT(15)
560
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
1393
+#define TDES0_OWN BIT(31)
561
+ &packet_info.in6_src,
1394
+
562
+ sizeof(packet_info.in6_src));
1395
+#define TDES1_BUF1_SIZE_SHIFT 0
563
+ }
1396
+#define TDES1_BUF1_SIZE_MASK 0x7ff
564
+ if (packet_info.is_ipv6_ext_dst &&
1397
+
565
+ config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_IP_EX) {
1398
+#define TDES1_BUF2_SIZE_SHIFT 11
566
+
1399
+#define TDES1_BUF2_SIZE_MASK 0x7ff
567
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
1400
+
568
+ &packet_info.in6_ext_dst,
1401
+#define TDES1_FT0 BIT(22)
569
+ sizeof(packet_info.in6_ext_dst));
1402
+#define TDES1_DPD BIT(23)
570
+ } else {
1403
+#define TDES1_TCH BIT(24)
571
+ net_rx_rss_add_chunk(rss_input, &bytes_written,
1404
+#define TDES1_TER BIT(25)
572
+ &packet_info.in6_dst,
1405
+#define TDES1_AC BIT(26)
573
+ sizeof(packet_info.in6_dst));
1406
+#define TDES1_SET BIT(27)
574
+ }
1407
+#define TDES1_FT1 BIT(28)
575
+ }
1408
+#define TDES1_FS BIT(29)
576
+ }
1409
+#define TDES1_LS BIT(30)
577
+
1410
+#define TDES1_IC BIT(31)
578
+ if (bytes_written) {
1411
+
579
+ net_toeplitz_add(&result, rss_input, bytes_written, toe);
1412
+struct tulip_descriptor {
580
+ }
1413
+ uint32_t status;
581
+
1414
+ uint32_t control;
582
+ return result;
1415
+ uint32_t buf_addr1;
583
+}
1416
+ uint32_t buf_addr2;
584
+
1417
+};
585
+SEC("tun_rss_steering")
1418
+
586
+int tun_rss_steering_prog(struct __sk_buff *skb)
1419
+#endif
587
+{
1420
diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h
588
+
1421
index XXXXXXX..XXXXXXX 100644
589
+ struct rss_config_t *config;
1422
--- a/include/hw/pci/pci_ids.h
590
+ struct toeplitz_key_data_t *toe;
1423
+++ b/include/hw/pci/pci_ids.h
591
+
1424
@@ -XXX,XX +XXX,XX @@
592
+ __u32 key = 0;
1425
#define PCI_DEVICE_ID_LSI_SAS0079 0x0079
593
+ __u32 hash = 0;
1426
594
+
1427
#define PCI_VENDOR_ID_DEC 0x1011
595
+ config = bpf_map_lookup_elem(&tap_rss_map_configurations, &key);
1428
+#define PCI_DEVICE_ID_DEC_21143 0x0019
596
+ toe = bpf_map_lookup_elem(&tap_rss_map_toeplitz_key, &key);
1429
#define PCI_DEVICE_ID_DEC_21154 0x0026
597
+
1430
598
+ if (config && toe) {
1431
#define PCI_VENDOR_ID_CIRRUS 0x1013
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
--
1432
--
624
2.7.4
1433
2.5.0
625
1434
626
1435
diff view generated by jsdifflib
1
From: Andrew Melnychenko <andrew@daynix.com>
1
From: "Michael S. Tsirkin" <mst@redhat.com>
2
2
3
Added function that loads RSS eBPF program.
3
Post load hook in virtio vmsd is called early while device is processed,
4
Added stub functions for RSS eBPF loader.
4
and when VirtIODevice core isn't fully initialized. Most device
5
Added meson and configuration options.
5
specific code isn't ready to deal with a device in such state, and
6
behaves weirdly.
6
7
7
By default, eBPF feature enabled if libbpf is present in the build system.
8
Add a new post_load hook in a device class instead. Devices should use
8
libbpf checked in configuration shell script and meson script.
9
this unless they specifically want to verify the migration stream as
10
it's processed, e.g. for bounds checking.
9
11
10
Signed-off-by: Yuri Benditovich <yuri.benditovich@daynix.com>
12
Cc: qemu-stable@nongnu.org
11
Signed-off-by: Andrew Melnychenko <andrew@daynix.com>
13
Suggested-by: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
14
Cc: Mikhail Sennikovsky <mikhail.sennikovskii@cloud.ionos.com>
15
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
12
Signed-off-by: Jason Wang <jasowang@redhat.com>
16
Signed-off-by: Jason Wang <jasowang@redhat.com>
13
---
17
---
14
configure | 8 +-
18
hw/virtio/virtio.c | 7 +++++++
15
ebpf/ebpf_rss-stub.c | 40 +++++
19
include/hw/virtio/virtio.h | 6 ++++++
16
ebpf/ebpf_rss.c | 165 ++++++++++++++++++
20
2 files changed, 13 insertions(+)
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
21
33
diff --git a/configure b/configure
22
diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
34
index XXXXXXX..XXXXXXX 100755
23
index XXXXXXX..XXXXXXX 100644
35
--- a/configure
24
--- a/hw/virtio/virtio.c
36
+++ b/configure
25
+++ b/hw/virtio/virtio.c
37
@@ -XXX,XX +XXX,XX @@ vhost_vsock="$default_feature"
26
@@ -XXX,XX +XXX,XX @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id)
38
vhost_user="no"
27
}
39
vhost_user_blk_server="auto"
28
rcu_read_unlock();
40
vhost_user_fs="$default_feature"
29
41
+bpf="auto"
30
+ if (vdc->post_load) {
42
kvm="auto"
31
+ ret = vdc->post_load(vdev);
43
hax="auto"
32
+ if (ret) {
44
hvf="auto"
33
+ return ret;
45
@@ -XXX,XX +XXX,XX @@ for opt do
34
+ }
46
;;
47
--enable-membarrier) membarrier="yes"
48
;;
49
+ --disable-bpf) bpf="disabled"
50
+ ;;
51
+ --enable-bpf) bpf="enabled"
52
+ ;;
53
--disable-blobs) blobs="false"
54
;;
55
--with-pkgversion=*) pkgversion="$optarg"
56
@@ -XXX,XX +XXX,XX @@ disabled with --disable-FEATURE, default is enabled if available
57
vhost-user vhost-user backend support
58
vhost-user-blk-server vhost-user-blk server support
59
vhost-vdpa vhost-vdpa kernel backend support
60
+ bpf BPF kernel support
61
spice spice
62
spice-protocol spice-protocol
63
rbd rados block device (rbd)
64
@@ -XXX,XX +XXX,XX @@ if test "$skip_meson" = no; then
65
-Dattr=$attr -Ddefault_devices=$default_devices \
66
-Ddocs=$docs -Dsphinx_build=$sphinx_build -Dinstall_blobs=$blobs \
67
-Dvhost_user_blk_server=$vhost_user_blk_server -Dmultiprocess=$multiprocess \
68
- -Dfuse=$fuse -Dfuse_lseek=$fuse_lseek -Dguest_agent_msi=$guest_agent_msi \
69
+ -Dfuse=$fuse -Dfuse_lseek=$fuse_lseek -Dguest_agent_msi=$guest_agent_msi -Dbpf=$bpf\
70
$(if test "$default_features" = no; then echo "-Dauto_features=disabled"; fi) \
71
    -Dtcg_interpreter=$tcg_interpreter \
72
$cross_arg \
73
diff --git a/ebpf/ebpf_rss-stub.c b/ebpf/ebpf_rss-stub.c
74
new file mode 100644
75
index XXXXXXX..XXXXXXX
76
--- /dev/null
77
+++ b/ebpf/ebpf_rss-stub.c
78
@@ -XXX,XX +XXX,XX @@
79
+/*
80
+ * eBPF RSS stub file
81
+ *
82
+ * Developed by Daynix Computing LTD (http://www.daynix.com)
83
+ *
84
+ * Authors:
85
+ * Yuri Benditovich <yuri.benditovich@daynix.com>
86
+ *
87
+ * This work is licensed under the terms of the GNU GPL, version 2. See
88
+ * the COPYING file in the top-level directory.
89
+ */
90
+
91
+#include "qemu/osdep.h"
92
+#include "ebpf/ebpf_rss.h"
93
+
94
+void ebpf_rss_init(struct EBPFRSSContext *ctx)
95
+{
96
+
97
+}
98
+
99
+bool ebpf_rss_is_loaded(struct EBPFRSSContext *ctx)
100
+{
101
+ return false;
102
+}
103
+
104
+bool ebpf_rss_load(struct EBPFRSSContext *ctx)
105
+{
106
+ return false;
107
+}
108
+
109
+bool ebpf_rss_set_all(struct EBPFRSSContext *ctx, struct EBPFRSSConfig *config,
110
+ uint16_t *indirections_table, uint8_t *toeplitz_key)
111
+{
112
+ return false;
113
+}
114
+
115
+void ebpf_rss_unload(struct EBPFRSSContext *ctx)
116
+{
117
+
118
+}
119
diff --git a/ebpf/ebpf_rss.c b/ebpf/ebpf_rss.c
120
new file mode 100644
121
index XXXXXXX..XXXXXXX
122
--- /dev/null
123
+++ b/ebpf/ebpf_rss.c
124
@@ -XXX,XX +XXX,XX @@
125
+/*
126
+ * eBPF RSS loader
127
+ *
128
+ * Developed by Daynix Computing LTD (http://www.daynix.com)
129
+ *
130
+ * Authors:
131
+ * Andrew Melnychenko <andrew@daynix.com>
132
+ * Yuri Benditovich <yuri.benditovich@daynix.com>
133
+ *
134
+ * This work is licensed under the terms of the GNU GPL, version 2. See
135
+ * the COPYING file in the top-level directory.
136
+ */
137
+
138
+#include "qemu/osdep.h"
139
+#include "qemu/error-report.h"
140
+
141
+#include <bpf/libbpf.h>
142
+#include <bpf/bpf.h>
143
+
144
+#include "hw/virtio/virtio-net.h" /* VIRTIO_NET_RSS_MAX_TABLE_LEN */
145
+
146
+#include "ebpf/ebpf_rss.h"
147
+#include "ebpf/rss.bpf.skeleton.h"
148
+#include "trace.h"
149
+
150
+void ebpf_rss_init(struct EBPFRSSContext *ctx)
151
+{
152
+ if (ctx != NULL) {
153
+ ctx->obj = NULL;
154
+ }
155
+}
156
+
157
+bool ebpf_rss_is_loaded(struct EBPFRSSContext *ctx)
158
+{
159
+ return ctx != NULL && ctx->obj != NULL;
160
+}
161
+
162
+bool ebpf_rss_load(struct EBPFRSSContext *ctx)
163
+{
164
+ struct rss_bpf *rss_bpf_ctx;
165
+
166
+ if (ctx == NULL) {
167
+ return false;
168
+ }
35
+ }
169
+
36
+
170
+ rss_bpf_ctx = rss_bpf__open();
37
return 0;
171
+ if (rss_bpf_ctx == NULL) {
38
}
172
+ trace_ebpf_error("eBPF RSS", "can not open eBPF RSS object");
39
173
+ goto error;
40
diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h
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
41
index XXXXXXX..XXXXXXX 100644
803
--- a/meson.build
42
--- a/include/hw/virtio/virtio.h
804
+++ b/meson.build
43
+++ b/include/hw/virtio/virtio.h
805
@@ -XXX,XX +XXX,XX @@ if not get_option('fuse_lseek').disabled()
44
@@ -XXX,XX +XXX,XX @@ typedef struct VirtioDeviceClass {
806
endif
45
*/
807
endif
46
void (*save)(VirtIODevice *vdev, QEMUFile *f);
808
47
int (*load)(VirtIODevice *vdev, QEMUFile *f, int version_id);
809
+# libbpf
48
+ /* Post load hook in vmsd is called early while device is processed, and
810
+libbpf = dependency('libbpf', required: get_option('bpf'), method: 'pkg-config')
49
+ * when VirtIODevice isn't fully initialized. Devices should use this instead,
811
+if libbpf.found() and not cc.links('''
50
+ * unless they specifically want to verify the migration stream as it's
812
+ #include <bpf/libbpf.h>
51
+ * processed, e.g. for bounds checking.
813
+ int main(void)
52
+ */
814
+ {
53
+ int (*post_load)(VirtIODevice *vdev);
815
+ bpf_object__destroy_skeleton(NULL);
54
const VMStateDescription *vmsd;
816
+ return 0;
55
} VirtioDeviceClass;
817
+ }''', dependencies: libbpf)
56
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
--
57
--
877
2.7.4
58
2.5.0
878
59
879
60
diff view generated by jsdifflib
1
From: Andrew Melnychenko <andrew@daynix.com>
1
From: Mikhail Sennikovsky <mikhail.sennikovskii@cloud.ionos.com>
2
2
3
When RSS is enabled the device tries to load the eBPF program
3
Currently offloads disabled by guest via the VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET
4
to select RX virtqueue in the TUN. If eBPF can be loaded
4
command are not preserved on VM migration.
5
the RSS will function also with vhost (works with kernel 5.8 and later).
5
Instead all offloads reported by guest features (via VIRTIO_PCI_GUEST_FEATURES)
6
Software RSS is used as a fallback with vhost=off when eBPF can't be loaded
6
get enabled.
7
or when hash population requested by the guest.
7
What happens is: first the VirtIONet::curr_guest_offloads gets restored and offloads
8
are getting set correctly:
8
9
9
Signed-off-by: Yuri Benditovich <yuri.benditovich@daynix.com>
10
#0 qemu_set_offload (nc=0x555556a11400, csum=1, tso4=0, tso6=0, ecn=0, ufo=0) at net/net.c:474
10
Signed-off-by: Andrew Melnychenko <andrew@daynix.com>
11
#1 virtio_net_apply_guest_offloads (n=0x555557701ca0) at hw/net/virtio-net.c:720
12
#2 virtio_net_post_load_device (opaque=0x555557701ca0, version_id=11) at hw/net/virtio-net.c:2334
13
#3 vmstate_load_state (f=0x5555569dc010, vmsd=0x555556577c80 <vmstate_virtio_net_device>, opaque=0x555557701ca0, version_id=11)
14
at migration/vmstate.c:168
15
#4 virtio_load (vdev=0x555557701ca0, f=0x5555569dc010, version_id=11) at hw/virtio/virtio.c:2197
16
#5 virtio_device_get (f=0x5555569dc010, opaque=0x555557701ca0, size=0, field=0x55555668cd00 <__compound_literal.5>) at hw/virtio/virtio.c:2036
17
#6 vmstate_load_state (f=0x5555569dc010, vmsd=0x555556577ce0 <vmstate_virtio_net>, opaque=0x555557701ca0, version_id=11) at migration/vmstate.c:143
18
#7 vmstate_load (f=0x5555569dc010, se=0x5555578189e0) at migration/savevm.c:829
19
#8 qemu_loadvm_section_start_full (f=0x5555569dc010, mis=0x5555569eee20) at migration/savevm.c:2211
20
#9 qemu_loadvm_state_main (f=0x5555569dc010, mis=0x5555569eee20) at migration/savevm.c:2395
21
#10 qemu_loadvm_state (f=0x5555569dc010) at migration/savevm.c:2467
22
#11 process_incoming_migration_co (opaque=0x0) at migration/migration.c:449
23
24
However later on the features are getting restored, and offloads get reset to
25
everything supported by features:
26
27
#0 qemu_set_offload (nc=0x555556a11400, csum=1, tso4=1, tso6=1, ecn=0, ufo=0) at net/net.c:474
28
#1 virtio_net_apply_guest_offloads (n=0x555557701ca0) at hw/net/virtio-net.c:720
29
#2 virtio_net_set_features (vdev=0x555557701ca0, features=5104441767) at hw/net/virtio-net.c:773
30
#3 virtio_set_features_nocheck (vdev=0x555557701ca0, val=5104441767) at hw/virtio/virtio.c:2052
31
#4 virtio_load (vdev=0x555557701ca0, f=0x5555569dc010, version_id=11) at hw/virtio/virtio.c:2220
32
#5 virtio_device_get (f=0x5555569dc010, opaque=0x555557701ca0, size=0, field=0x55555668cd00 <__compound_literal.5>) at hw/virtio/virtio.c:2036
33
#6 vmstate_load_state (f=0x5555569dc010, vmsd=0x555556577ce0 <vmstate_virtio_net>, opaque=0x555557701ca0, version_id=11) at migration/vmstate.c:143
34
#7 vmstate_load (f=0x5555569dc010, se=0x5555578189e0) at migration/savevm.c:829
35
#8 qemu_loadvm_section_start_full (f=0x5555569dc010, mis=0x5555569eee20) at migration/savevm.c:2211
36
#9 qemu_loadvm_state_main (f=0x5555569dc010, mis=0x5555569eee20) at migration/savevm.c:2395
37
#10 qemu_loadvm_state (f=0x5555569dc010) at migration/savevm.c:2467
38
#11 process_incoming_migration_co (opaque=0x0) at migration/migration.c:449
39
40
Fix this by preserving the state in saved_guest_offloads field and
41
pushing out offload initialization to the new post load hook.
42
43
Cc: qemu-stable@nongnu.org
44
Signed-off-by: Mikhail Sennikovsky <mikhail.sennikovskii@cloud.ionos.com>
11
Signed-off-by: Jason Wang <jasowang@redhat.com>
45
Signed-off-by: Jason Wang <jasowang@redhat.com>
12
---
46
---
13
hw/net/vhost_net.c | 3 ++
47
hw/net/virtio-net.c | 27 ++++++++++++++++++++++++---
14
hw/net/virtio-net.c | 116 +++++++++++++++++++++++++++++++++++++++--
48
include/hw/virtio/virtio-net.h | 2 ++
15
include/hw/virtio/virtio-net.h | 4 ++
49
2 files changed, 26 insertions(+), 3 deletions(-)
16
net/vhost-vdpa.c | 2 +
17
4 files changed, 122 insertions(+), 3 deletions(-)
18
50
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
51
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
41
index XXXXXXX..XXXXXXX 100644
52
index XXXXXXX..XXXXXXX 100644
42
--- a/hw/net/virtio-net.c
53
--- a/hw/net/virtio-net.c
43
+++ b/hw/net/virtio-net.c
54
+++ b/hw/net/virtio-net.c
44
@@ -XXX,XX +XXX,XX @@ static uint64_t virtio_net_get_features(VirtIODevice *vdev, uint64_t features,
55
@@ -XXX,XX +XXX,XX @@ static int virtio_net_post_load_device(void *opaque, int version_id)
45
return features;
56
n->curr_guest_offloads = virtio_net_supported_guest_offloads(n);
46
}
57
}
47
58
48
- virtio_clear_feature(&features, VIRTIO_NET_F_RSS);
59
- if (peer_has_vnet_hdr(n)) {
49
- virtio_clear_feature(&features, VIRTIO_NET_F_HASH_REPORT);
60
- virtio_net_apply_guest_offloads(n);
50
+ if (!ebpf_rss_is_loaded(&n->ebpf_rss)) {
61
- }
51
+ virtio_clear_feature(&features, VIRTIO_NET_F_RSS);
62
+ /*
63
+ * curr_guest_offloads will be later overwritten by the
64
+ * virtio_set_features_nocheck call done from the virtio_load.
65
+ * Here we make sure it is preserved and restored accordingly
66
+ * in the virtio_net_post_load_virtio callback.
67
+ */
68
+ n->saved_guest_offloads = n->curr_guest_offloads;
69
70
virtio_net_set_queues(n);
71
72
@@ -XXX,XX +XXX,XX @@ static int virtio_net_post_load_device(void *opaque, int version_id)
73
return 0;
74
}
75
76
+static int virtio_net_post_load_virtio(VirtIODevice *vdev)
77
+{
78
+ VirtIONet *n = VIRTIO_NET(vdev);
79
+ /*
80
+ * The actual needed state is now in saved_guest_offloads,
81
+ * see virtio_net_post_load_device for detail.
82
+ * Restore it back and apply the desired offloads.
83
+ */
84
+ n->curr_guest_offloads = n->saved_guest_offloads;
85
+ if (peer_has_vnet_hdr(n)) {
86
+ virtio_net_apply_guest_offloads(n);
52
+ }
87
+ }
53
features = vhost_net_get_features(get_vhost_net(nc->peer), features);
54
vdev->backend_features = features;
55
56
@@ -XXX,XX +XXX,XX @@ static int virtio_net_handle_announce(VirtIONet *n, uint8_t cmd,
57
}
58
}
59
60
+static void virtio_net_detach_epbf_rss(VirtIONet *n);
61
+
88
+
62
static void virtio_net_disable_rss(VirtIONet *n)
89
+ return 0;
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
+}
90
+}
71
+
91
+
72
+static bool virtio_net_attach_ebpf_to_backend(NICState *nic, int prog_fd)
92
/* tx_waiting field of a VirtIONetQueue */
73
+{
93
static const VMStateDescription vmstate_virtio_net_queue_tx_waiting = {
74
+ NetClientState *nc = qemu_get_peer(qemu_get_queue(nic), 0);
94
.name = "virtio-net-queue-tx_waiting",
75
+ if (nc == NULL || nc->info->set_steering_ebpf == NULL) {
95
@@ -XXX,XX +XXX,XX @@ static void virtio_net_class_init(ObjectClass *klass, void *data)
76
+ return false;
96
vdc->guest_notifier_mask = virtio_net_guest_notifier_mask;
77
+ }
97
vdc->guest_notifier_pending = virtio_net_guest_notifier_pending;
78
+
98
vdc->legacy_features |= (0x1 << VIRTIO_NET_F_GSO);
79
+ return nc->info->set_steering_ebpf(nc, prog_fd);
99
+ vdc->post_load = virtio_net_post_load_virtio;
80
+}
100
vdc->vmsd = &vmstate_virtio_net_device;
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
}
101
}
134
102
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
103
diff --git a/include/hw/virtio/virtio-net.h b/include/hw/virtio/virtio-net.h
223
index XXXXXXX..XXXXXXX 100644
104
index XXXXXXX..XXXXXXX 100644
224
--- a/include/hw/virtio/virtio-net.h
105
--- a/include/hw/virtio/virtio-net.h
225
+++ b/include/hw/virtio/virtio-net.h
106
+++ 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 {
107
@@ -XXX,XX +XXX,XX @@ struct VirtIONet {
244
Notifier migration_state;
108
char *netclient_name;
245
VirtioNetRssData rss_data;
109
char *netclient_type;
246
struct NetRxPkt *rx_pkt;
110
uint64_t curr_guest_offloads;
247
+ struct EBPFRSSContext ebpf_rss;
111
+ /* used on saved state restore phase to preserve the curr_guest_offloads */
248
};
112
+ uint64_t saved_guest_offloads;
249
113
AnnounceTimer announce_timer;
250
void virtio_net_set_netclient_name(VirtIONet *n, const char *name,
114
bool needs_vnet_hdr_swap;
251
diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c
115
bool mtu_bypass_backend;
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
--
116
--
265
2.7.4
117
2.5.0
266
118
267
119
diff view generated by jsdifflib
Deleted patch
1
From: Andrew Melnychenko <andrew@daynix.com>
2
1
3
Signed-off-by: Yuri Benditovich <yuri.benditovich@daynix.com>
4
Signed-off-by: Andrew Melnychenko <andrew@daynix.com>
5
Signed-off-by: Jason Wang <jasowang@redhat.com>
6
---
7
docs/devel/ebpf_rss.rst | 125 ++++++++++++++++++++++++++++++++++++++++++++++++
8
docs/devel/index.rst | 1 +
9
2 files changed, 126 insertions(+)
10
create mode 100644 docs/devel/ebpf_rss.rst
11
12
diff --git a/docs/devel/ebpf_rss.rst b/docs/devel/ebpf_rss.rst
13
new file mode 100644
14
index XXXXXXX..XXXXXXX
15
--- /dev/null
16
+++ b/docs/devel/ebpf_rss.rst
17
@@ -XXX,XX +XXX,XX @@
18
+===========================
19
+eBPF RSS virtio-net support
20
+===========================
21
+
22
+RSS(Receive Side Scaling) is used to distribute network packets to guest virtqueues
23
+by calculating packet hash. Usually every queue is processed then by a specific guest CPU core.
24
+
25
+For now there are 2 RSS implementations in qemu:
26
+- 'in-qemu' RSS (functions if qemu receives network packets, i.e. vhost=off)
27
+- eBPF RSS (can function with also with vhost=on)
28
+
29
+eBPF support (CONFIG_EBPF) is enabled by 'configure' script.
30
+To enable eBPF RSS support use './configure --enable-bpf'.
31
+
32
+If steering BPF is not set for kernel's TUN module, the TUN uses automatic selection
33
+of rx virtqueue based on lookup table built according to calculated symmetric hash
34
+of transmitted packets.
35
+If steering BPF is set for TUN the BPF code calculates the hash of packet header and
36
+returns the virtqueue number to place the packet to.
37
+
38
+Simplified decision formula:
39
+
40
+.. code:: C
41
+
42
+ queue_index = indirection_table[hash(<packet data>)%<indirection_table size>]
43
+
44
+
45
+Not for all packets, the hash can/should be calculated.
46
+
47
+Note: currently, eBPF RSS does not support hash reporting.
48
+
49
+eBPF RSS turned on by different combinations of vhost-net, vitrio-net and tap configurations:
50
+
51
+- eBPF is used:
52
+
53
+ tap,vhost=off & virtio-net-pci,rss=on,hash=off
54
+
55
+- eBPF is used:
56
+
57
+ tap,vhost=on & virtio-net-pci,rss=on,hash=off
58
+
59
+- 'in-qemu' RSS is used:
60
+
61
+ tap,vhost=off & virtio-net-pci,rss=on,hash=on
62
+
63
+- eBPF is used, hash population feature is not reported to the guest:
64
+
65
+ tap,vhost=on & virtio-net-pci,rss=on,hash=on
66
+
67
+If CONFIG_EBPF is not set then only 'in-qemu' RSS is supported.
68
+Also 'in-qemu' RSS, as a fallback, is used if the eBPF program failed to load or set to TUN.
69
+
70
+RSS eBPF program
71
+----------------
72
+
73
+RSS program located in ebpf/rss.bpf.skeleton.h generated by bpftool.
74
+So the program is part of the qemu binary.
75
+Initially, the eBPF program was compiled by clang and source code located at tools/ebpf/rss.bpf.c.
76
+Prerequisites to recompile the eBPF program (regenerate ebpf/rss.bpf.skeleton.h):
77
+
78
+ llvm, clang, kernel source tree, bpftool
79
+ Adjust Makefile.ebpf to reflect the location of the kernel source tree
80
+
81
+ $ cd tools/ebpf
82
+ $ make -f Makefile.ebpf
83
+
84
+Current eBPF RSS implementation uses 'bounded loops' with 'backward jump instructions' which present in the last kernels.
85
+Overall eBPF RSS works on kernels 5.8+.
86
+
87
+eBPF RSS implementation
88
+-----------------------
89
+
90
+eBPF RSS loading functionality located in ebpf/ebpf_rss.c and ebpf/ebpf_rss.h.
91
+
92
+The `struct EBPFRSSContext` structure that holds 4 file descriptors:
93
+
94
+- ctx - pointer of the libbpf context.
95
+- program_fd - file descriptor of the eBPF RSS program.
96
+- map_configuration - file descriptor of the 'configuration' map. This map contains one element of 'struct EBPFRSSConfig'. This configuration determines eBPF program behavior.
97
+- map_toeplitz_key - file descriptor of the 'Toeplitz key' map. One element of the 40byte key prepared for the hashing algorithm.
98
+- map_indirections_table - 128 elements of queue indexes.
99
+
100
+`struct EBPFRSSConfig` fields:
101
+
102
+- redirect - "boolean" value, should the hash be calculated, on false - `default_queue` would be used as the final decision.
103
+- populate_hash - for now, not used. eBPF RSS doesn't support hash reporting.
104
+- hash_types - binary mask of different hash types. See `VIRTIO_NET_RSS_HASH_TYPE_*` defines. If for packet hash should not be calculated - `default_queue` would be used.
105
+- indirections_len - length of the indirections table, maximum 128.
106
+- default_queue - the queue index that used for packet that shouldn't be hashed. For some packets, the hash can't be calculated(g.e ARP).
107
+
108
+Functions:
109
+
110
+- `ebpf_rss_init()` - sets ctx to NULL, which indicates that EBPFRSSContext is not loaded.
111
+- `ebpf_rss_load()` - creates 3 maps and loads eBPF program from the rss.bpf.skeleton.h. Returns 'true' on success. After that, program_fd can be used to set steering for TAP.
112
+- `ebpf_rss_set_all()` - sets values for eBPF maps. `indirections_table` length is in EBPFRSSConfig. `toeplitz_key` is VIRTIO_NET_RSS_MAX_KEY_SIZE aka 40 bytes array.
113
+- `ebpf_rss_unload()` - close all file descriptors and set ctx to NULL.
114
+
115
+Simplified eBPF RSS workflow:
116
+
117
+.. code:: C
118
+
119
+ struct EBPFRSSConfig config;
120
+ config.redirect = 1;
121
+ config.hash_types = VIRTIO_NET_RSS_HASH_TYPE_UDPv4 | VIRTIO_NET_RSS_HASH_TYPE_TCPv4;
122
+ config.indirections_len = VIRTIO_NET_RSS_MAX_TABLE_LEN;
123
+ config.default_queue = 0;
124
+
125
+ uint16_t table[VIRTIO_NET_RSS_MAX_TABLE_LEN] = {...};
126
+ uint8_t key[VIRTIO_NET_RSS_MAX_KEY_SIZE] = {...};
127
+
128
+ struct EBPFRSSContext ctx;
129
+ ebpf_rss_init(&ctx);
130
+ ebpf_rss_load(&ctx);
131
+ ebpf_rss_set_all(&ctx, &config, table, key);
132
+ if (net_client->info->set_steering_ebpf != NULL) {
133
+ net_client->info->set_steering_ebpf(net_client, ctx->program_fd);
134
+ }
135
+ ...
136
+ ebpf_unload(&ctx);
137
+
138
+
139
+NetClientState SetSteeringEBPF()
140
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
141
+
142
+For now, `set_steering_ebpf()` method supported by Linux TAP NetClientState. The method requires an eBPF program file descriptor as an argument.
143
diff --git a/docs/devel/index.rst b/docs/devel/index.rst
144
index XXXXXXX..XXXXXXX 100644
145
--- a/docs/devel/index.rst
146
+++ b/docs/devel/index.rst
147
@@ -XXX,XX +XXX,XX @@ Contents:
148
qom
149
block-coroutine-wrapper
150
multi-process
151
+ ebpf_rss
152
--
153
2.7.4
154
155
diff view generated by jsdifflib
1
From: Andrew Melnychenko <andrew@daynix.com>
1
From: Fan Yang <Fan_Yang@sjtu.edu.cn>
2
2
3
Signed-off-by: Yuri Benditovich <yuri.benditovich@daynix.com>
3
'colo_mark_tcp_pkt' should return 'true' when packets are the same, and
4
Signed-off-by: Andrew Melnychenko <andrew@daynix.com>
4
'false' otherwise. However, it returns 'true' when
5
'colo_compare_packet_payload' returns non-zero while
6
'colo_compare_packet_payload' is just a 'memcmp'. The result is that
7
COLO-compare reports inconsistent TCP packets when they are actually
8
the same.
9
10
Fixes: f449c9e549c ("colo: compare the packet based on the tcp sequence number")
11
Cc: qemu-stable@nongnu.org
12
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
13
Signed-off-by: Fan Yang <Fan_Yang@sjtu.edu.cn>
5
Signed-off-by: Jason Wang <jasowang@redhat.com>
14
Signed-off-by: Jason Wang <jasowang@redhat.com>
6
---
15
---
7
MAINTAINERS | 8 ++++++++
16
net/colo-compare.c | 6 +++---
8
1 file changed, 8 insertions(+)
17
1 file changed, 3 insertions(+), 3 deletions(-)
9
18
10
diff --git a/MAINTAINERS b/MAINTAINERS
19
diff --git a/net/colo-compare.c b/net/colo-compare.c
11
index XXXXXXX..XXXXXXX 100644
20
index XXXXXXX..XXXXXXX 100644
12
--- a/MAINTAINERS
21
--- a/net/colo-compare.c
13
+++ b/MAINTAINERS
22
+++ b/net/colo-compare.c
14
@@ -XXX,XX +XXX,XX @@ F: include/hw/remote/proxy-memory-listener.h
23
@@ -XXX,XX +XXX,XX @@ static bool colo_mark_tcp_pkt(Packet *ppkt, Packet *spkt,
15
F: hw/remote/iohub.c
24
*mark = 0;
16
F: include/hw/remote/iohub.h
25
17
26
if (ppkt->tcp_seq == spkt->tcp_seq && ppkt->seq_end == spkt->seq_end) {
18
+EBPF:
27
- if (colo_compare_packet_payload(ppkt, spkt,
19
+M: Jason Wang <jasowang@redhat.com>
28
+ if (!colo_compare_packet_payload(ppkt, spkt,
20
+R: Andrew Melnychenko <andrew@daynix.com>
29
ppkt->header_size, spkt->header_size,
21
+R: Yuri Benditovich <yuri.benditovich@daynix.com>
30
ppkt->payload_size)) {
22
+S: Maintained
31
*mark = COLO_COMPARE_FREE_SECONDARY | COLO_COMPARE_FREE_PRIMARY;
23
+F: ebpf/*
32
@@ -XXX,XX +XXX,XX @@ static bool colo_mark_tcp_pkt(Packet *ppkt, Packet *spkt,
24
+F: tools/ebpf/*
33
25
+
34
/* one part of secondary packet payload still need to be compared */
26
Build and test automation
35
if (!after(ppkt->seq_end, spkt->seq_end)) {
27
-------------------------
36
- if (colo_compare_packet_payload(ppkt, spkt,
28
Build and test automation, general continuous integration
37
+ if (!colo_compare_packet_payload(ppkt, spkt,
38
ppkt->header_size + ppkt->offset,
39
spkt->header_size + spkt->offset,
40
ppkt->payload_size - ppkt->offset)) {
41
@@ -XXX,XX +XXX,XX @@ static bool colo_mark_tcp_pkt(Packet *ppkt, Packet *spkt,
42
/* primary packet is longer than secondary packet, compare
43
* the same part and mark the primary packet offset
44
*/
45
- if (colo_compare_packet_payload(ppkt, spkt,
46
+ if (!colo_compare_packet_payload(ppkt, spkt,
47
ppkt->header_size + ppkt->offset,
48
spkt->header_size + spkt->offset,
49
spkt->payload_size - spkt->offset)) {
29
--
50
--
30
2.7.4
51
2.5.0
31
52
32
53
diff view generated by jsdifflib