]>
Commit | Line | Data |
---|---|---|
1 | =========================== | |
2 | eBPF RSS virtio-net support | |
3 | =========================== | |
4 | ||
5 | RSS(Receive Side Scaling) is used to distribute network packets to guest virtqueues | |
6 | by calculating packet hash. Usually every queue is processed then by a specific guest CPU core. | |
7 | ||
8 | For now there are 2 RSS implementations in qemu: | |
9 | - 'in-qemu' RSS (functions if qemu receives network packets, i.e. vhost=off) | |
10 | - eBPF RSS (can function with also with vhost=on) | |
11 | ||
12 | eBPF support (CONFIG_EBPF) is enabled by 'configure' script. | |
13 | To enable eBPF RSS support use './configure --enable-bpf'. | |
14 | ||
15 | If steering BPF is not set for kernel's TUN module, the TUN uses automatic selection | |
16 | of rx virtqueue based on lookup table built according to calculated symmetric hash | |
17 | of transmitted packets. | |
18 | If steering BPF is set for TUN the BPF code calculates the hash of packet header and | |
19 | returns the virtqueue number to place the packet to. | |
20 | ||
21 | Simplified decision formula: | |
22 | ||
23 | .. code:: C | |
24 | ||
25 | queue_index = indirection_table[hash(<packet data>)%<indirection_table size>] | |
26 | ||
27 | ||
28 | Not for all packets, the hash can/should be calculated. | |
29 | ||
30 | Note: currently, eBPF RSS does not support hash reporting. | |
31 | ||
32 | eBPF RSS turned on by different combinations of vhost-net, vitrio-net and tap configurations: | |
33 | ||
34 | - eBPF is used: | |
35 | ||
36 | tap,vhost=off & virtio-net-pci,rss=on,hash=off | |
37 | ||
38 | - eBPF is used: | |
39 | ||
40 | tap,vhost=on & virtio-net-pci,rss=on,hash=off | |
41 | ||
42 | - 'in-qemu' RSS is used: | |
43 | ||
44 | tap,vhost=off & virtio-net-pci,rss=on,hash=on | |
45 | ||
46 | - eBPF is used, hash population feature is not reported to the guest: | |
47 | ||
48 | tap,vhost=on & virtio-net-pci,rss=on,hash=on | |
49 | ||
50 | If CONFIG_EBPF is not set then only 'in-qemu' RSS is supported. | |
51 | Also 'in-qemu' RSS, as a fallback, is used if the eBPF program failed to load or set to TUN. | |
52 | ||
53 | RSS eBPF program | |
54 | ---------------- | |
55 | ||
56 | RSS program located in ebpf/rss.bpf.skeleton.h generated by bpftool. | |
57 | So the program is part of the qemu binary. | |
58 | Initially, the eBPF program was compiled by clang and source code located at tools/ebpf/rss.bpf.c. | |
59 | Prerequisites to recompile the eBPF program (regenerate ebpf/rss.bpf.skeleton.h): | |
60 | ||
61 | llvm, clang, kernel source tree, bpftool | |
62 | Adjust Makefile.ebpf to reflect the location of the kernel source tree | |
63 | ||
64 | $ cd tools/ebpf | |
65 | $ make -f Makefile.ebpf | |
66 | ||
67 | Current eBPF RSS implementation uses 'bounded loops' with 'backward jump instructions' which present in the last kernels. | |
68 | Overall eBPF RSS works on kernels 5.8+. | |
69 | ||
70 | eBPF RSS implementation | |
71 | ----------------------- | |
72 | ||
73 | eBPF RSS loading functionality located in ebpf/ebpf_rss.c and ebpf/ebpf_rss.h. | |
74 | ||
75 | The `struct EBPFRSSContext` structure that holds 4 file descriptors: | |
76 | ||
77 | - ctx - pointer of the libbpf context. | |
78 | - program_fd - file descriptor of the eBPF RSS program. | |
79 | - map_configuration - file descriptor of the 'configuration' map. This map contains one element of 'struct EBPFRSSConfig'. This configuration determines eBPF program behavior. | |
80 | - map_toeplitz_key - file descriptor of the 'Toeplitz key' map. One element of the 40byte key prepared for the hashing algorithm. | |
81 | - map_indirections_table - 128 elements of queue indexes. | |
82 | ||
83 | `struct EBPFRSSConfig` fields: | |
84 | ||
85 | - redirect - "boolean" value, should the hash be calculated, on false - `default_queue` would be used as the final decision. | |
86 | - populate_hash - for now, not used. eBPF RSS doesn't support hash reporting. | |
87 | - 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. | |
88 | - indirections_len - length of the indirections table, maximum 128. | |
89 | - 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). | |
90 | ||
91 | Functions: | |
92 | ||
93 | - `ebpf_rss_init()` - sets ctx to NULL, which indicates that EBPFRSSContext is not loaded. | |
94 | - `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. | |
95 | - `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. | |
96 | - `ebpf_rss_unload()` - close all file descriptors and set ctx to NULL. | |
97 | ||
98 | Simplified eBPF RSS workflow: | |
99 | ||
100 | .. code:: C | |
101 | ||
102 | struct EBPFRSSConfig config; | |
103 | config.redirect = 1; | |
104 | config.hash_types = VIRTIO_NET_RSS_HASH_TYPE_UDPv4 | VIRTIO_NET_RSS_HASH_TYPE_TCPv4; | |
105 | config.indirections_len = VIRTIO_NET_RSS_MAX_TABLE_LEN; | |
106 | config.default_queue = 0; | |
107 | ||
108 | uint16_t table[VIRTIO_NET_RSS_MAX_TABLE_LEN] = {...}; | |
109 | uint8_t key[VIRTIO_NET_RSS_MAX_KEY_SIZE] = {...}; | |
110 | ||
111 | struct EBPFRSSContext ctx; | |
112 | ebpf_rss_init(&ctx); | |
113 | ebpf_rss_load(&ctx); | |
114 | ebpf_rss_set_all(&ctx, &config, table, key); | |
115 | if (net_client->info->set_steering_ebpf != NULL) { | |
116 | net_client->info->set_steering_ebpf(net_client, ctx->program_fd); | |
117 | } | |
118 | ... | |
119 | ebpf_unload(&ctx); | |
120 | ||
121 | ||
122 | NetClientState SetSteeringEBPF() | |
123 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
124 | ||
125 | For now, `set_steering_ebpf()` method supported by Linux TAP NetClientState. The method requires an eBPF program file descriptor as an argument. |