1 /* 2 * eBPF RSS loader 3 * 4 * Developed by Daynix Computing LTD (http://www.daynix.com) 5 * 6 * Authors: 7 * Andrew Melnychenko <andrew@daynix.com> 8 * Yuri Benditovich <yuri.benditovich@daynix.com> 9 * 10 * This work is licensed under the terms of the GNU GPL, version 2. See 11 * the COPYING file in the top-level directory. 12 */ 13 14 #include "qemu/osdep.h" 15 #include "qemu/error-report.h" 16 17 #include <bpf/libbpf.h> 18 #include <bpf/bpf.h> 19 20 #include "hw/virtio/virtio-net.h" /* VIRTIO_NET_RSS_MAX_TABLE_LEN */ 21 22 #include "ebpf/ebpf_rss.h" 23 #include "ebpf/rss.bpf.skeleton.h" 24 #include "trace.h" 25 26 void ebpf_rss_init(struct EBPFRSSContext *ctx) 27 { 28 if (ctx != NULL) { 29 ctx->obj = NULL; 30 ctx->program_fd = -1; 31 ctx->map_configuration = -1; 32 ctx->map_toeplitz_key = -1; 33 ctx->map_indirections_table = -1; 34 35 ctx->mmap_configuration = NULL; 36 ctx->mmap_toeplitz_key = NULL; 37 ctx->mmap_indirections_table = NULL; 38 } 39 } 40 41 bool ebpf_rss_is_loaded(struct EBPFRSSContext *ctx) 42 { 43 return ctx != NULL && (ctx->obj != NULL || ctx->program_fd != -1); 44 } 45 46 static bool ebpf_rss_mmap(struct EBPFRSSContext *ctx) 47 { 48 if (!ebpf_rss_is_loaded(ctx)) { 49 return false; 50 } 51 52 ctx->mmap_configuration = mmap(NULL, qemu_real_host_page_size(), 53 PROT_READ | PROT_WRITE, MAP_SHARED, 54 ctx->map_configuration, 0); 55 if (ctx->mmap_configuration == MAP_FAILED) { 56 trace_ebpf_error("eBPF RSS", "can not mmap eBPF configuration array"); 57 return false; 58 } 59 ctx->mmap_toeplitz_key = mmap(NULL, qemu_real_host_page_size(), 60 PROT_READ | PROT_WRITE, MAP_SHARED, 61 ctx->map_toeplitz_key, 0); 62 if (ctx->mmap_toeplitz_key == MAP_FAILED) { 63 trace_ebpf_error("eBPF RSS", "can not mmap eBPF toeplitz key"); 64 goto toeplitz_fail; 65 } 66 ctx->mmap_indirections_table = mmap(NULL, qemu_real_host_page_size(), 67 PROT_READ | PROT_WRITE, MAP_SHARED, 68 ctx->map_indirections_table, 0); 69 if (ctx->mmap_indirections_table == MAP_FAILED) { 70 trace_ebpf_error("eBPF RSS", "can not mmap eBPF indirection table"); 71 goto indirection_fail; 72 } 73 74 return true; 75 76 indirection_fail: 77 munmap(ctx->mmap_toeplitz_key, qemu_real_host_page_size()); 78 ctx->mmap_toeplitz_key = NULL; 79 toeplitz_fail: 80 munmap(ctx->mmap_configuration, qemu_real_host_page_size()); 81 ctx->mmap_configuration = NULL; 82 83 ctx->mmap_indirections_table = NULL; 84 return false; 85 } 86 87 static void ebpf_rss_munmap(struct EBPFRSSContext *ctx) 88 { 89 if (!ebpf_rss_is_loaded(ctx)) { 90 return; 91 } 92 93 munmap(ctx->mmap_indirections_table, qemu_real_host_page_size()); 94 munmap(ctx->mmap_toeplitz_key, qemu_real_host_page_size()); 95 munmap(ctx->mmap_configuration, qemu_real_host_page_size()); 96 97 ctx->mmap_configuration = NULL; 98 ctx->mmap_toeplitz_key = NULL; 99 ctx->mmap_indirections_table = NULL; 100 } 101 102 bool ebpf_rss_load(struct EBPFRSSContext *ctx) 103 { 104 struct rss_bpf *rss_bpf_ctx; 105 106 if (ebpf_rss_is_loaded(ctx)) { 107 return false; 108 } 109 110 rss_bpf_ctx = rss_bpf__open(); 111 if (rss_bpf_ctx == NULL) { 112 trace_ebpf_error("eBPF RSS", "can not open eBPF RSS object"); 113 goto error; 114 } 115 116 bpf_program__set_type(rss_bpf_ctx->progs.tun_rss_steering_prog, BPF_PROG_TYPE_SOCKET_FILTER); 117 118 if (rss_bpf__load(rss_bpf_ctx)) { 119 trace_ebpf_error("eBPF RSS", "can not load RSS program"); 120 goto error; 121 } 122 123 ctx->obj = rss_bpf_ctx; 124 ctx->program_fd = bpf_program__fd( 125 rss_bpf_ctx->progs.tun_rss_steering_prog); 126 ctx->map_configuration = bpf_map__fd( 127 rss_bpf_ctx->maps.tap_rss_map_configurations); 128 ctx->map_indirections_table = bpf_map__fd( 129 rss_bpf_ctx->maps.tap_rss_map_indirection_table); 130 ctx->map_toeplitz_key = bpf_map__fd( 131 rss_bpf_ctx->maps.tap_rss_map_toeplitz_key); 132 133 if (!ebpf_rss_mmap(ctx)) { 134 goto error; 135 } 136 137 return true; 138 error: 139 rss_bpf__destroy(rss_bpf_ctx); 140 ctx->obj = NULL; 141 ctx->program_fd = -1; 142 ctx->map_configuration = -1; 143 ctx->map_toeplitz_key = -1; 144 ctx->map_indirections_table = -1; 145 146 return false; 147 } 148 149 bool ebpf_rss_load_fds(struct EBPFRSSContext *ctx, int program_fd, 150 int config_fd, int toeplitz_fd, int table_fd) 151 { 152 if (ebpf_rss_is_loaded(ctx)) { 153 return false; 154 } 155 156 if (program_fd < 0 || config_fd < 0 || toeplitz_fd < 0 || table_fd < 0) { 157 return false; 158 } 159 160 ctx->program_fd = program_fd; 161 ctx->map_configuration = config_fd; 162 ctx->map_toeplitz_key = toeplitz_fd; 163 ctx->map_indirections_table = table_fd; 164 165 if (!ebpf_rss_mmap(ctx)) { 166 ctx->program_fd = -1; 167 ctx->map_configuration = -1; 168 ctx->map_toeplitz_key = -1; 169 ctx->map_indirections_table = -1; 170 return false; 171 } 172 173 return true; 174 } 175 176 static bool ebpf_rss_set_config(struct EBPFRSSContext *ctx, 177 struct EBPFRSSConfig *config) 178 { 179 if (!ebpf_rss_is_loaded(ctx)) { 180 return false; 181 } 182 183 memcpy(ctx->mmap_configuration, config, sizeof(*config)); 184 return true; 185 } 186 187 static bool ebpf_rss_set_indirections_table(struct EBPFRSSContext *ctx, 188 uint16_t *indirections_table, 189 size_t len) 190 { 191 if (!ebpf_rss_is_loaded(ctx) || indirections_table == NULL || 192 len > VIRTIO_NET_RSS_MAX_TABLE_LEN) { 193 return false; 194 } 195 196 memcpy(ctx->mmap_indirections_table, indirections_table, 197 sizeof(*indirections_table) * len); 198 return true; 199 } 200 201 static bool ebpf_rss_set_toepliz_key(struct EBPFRSSContext *ctx, 202 uint8_t *toeplitz_key) 203 { 204 /* prepare toeplitz key */ 205 uint8_t toe[VIRTIO_NET_RSS_MAX_KEY_SIZE] = {}; 206 207 if (!ebpf_rss_is_loaded(ctx) || toeplitz_key == NULL) { 208 return false; 209 } 210 memcpy(toe, toeplitz_key, VIRTIO_NET_RSS_MAX_KEY_SIZE); 211 *(uint32_t *)toe = ntohl(*(uint32_t *)toe); 212 213 memcpy(ctx->mmap_toeplitz_key, toe, VIRTIO_NET_RSS_MAX_KEY_SIZE); 214 return true; 215 } 216 217 bool ebpf_rss_set_all(struct EBPFRSSContext *ctx, struct EBPFRSSConfig *config, 218 uint16_t *indirections_table, uint8_t *toeplitz_key) 219 { 220 if (!ebpf_rss_is_loaded(ctx) || config == NULL || 221 indirections_table == NULL || toeplitz_key == NULL) { 222 return false; 223 } 224 225 if (!ebpf_rss_set_config(ctx, config)) { 226 return false; 227 } 228 229 if (!ebpf_rss_set_indirections_table(ctx, indirections_table, 230 config->indirections_len)) { 231 return false; 232 } 233 234 if (!ebpf_rss_set_toepliz_key(ctx, toeplitz_key)) { 235 return false; 236 } 237 238 return true; 239 } 240 241 void ebpf_rss_unload(struct EBPFRSSContext *ctx) 242 { 243 if (!ebpf_rss_is_loaded(ctx)) { 244 return; 245 } 246 247 ebpf_rss_munmap(ctx); 248 249 if (ctx->obj) { 250 rss_bpf__destroy(ctx->obj); 251 } else { 252 close(ctx->program_fd); 253 close(ctx->map_configuration); 254 close(ctx->map_toeplitz_key); 255 close(ctx->map_indirections_table); 256 } 257 258 ctx->obj = NULL; 259 ctx->program_fd = -1; 260 ctx->map_configuration = -1; 261 ctx->map_toeplitz_key = -1; 262 ctx->map_indirections_table = -1; 263 } 264