]> Git Repo - J-linux.git/blob - tools/testing/selftests/bpf/xsk.c
Merge tag 'amd-drm-next-6.5-2023-06-09' of https://gitlab.freedesktop.org/agd5f/linux...
[J-linux.git] / tools / testing / selftests / bpf / xsk.c
1 // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2
3 /*
4  * AF_XDP user-space access library.
5  *
6  * Copyright(c) 2018 - 2019 Intel Corporation.
7  *
8  * Author(s): Magnus Karlsson <[email protected]>
9  */
10
11 #include <errno.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <unistd.h>
15 #include <arpa/inet.h>
16 #include <asm/barrier.h>
17 #include <linux/compiler.h>
18 #include <linux/ethtool.h>
19 #include <linux/filter.h>
20 #include <linux/if_ether.h>
21 #include <linux/if_packet.h>
22 #include <linux/if_xdp.h>
23 #include <linux/kernel.h>
24 #include <linux/list.h>
25 #include <linux/sockios.h>
26 #include <net/if.h>
27 #include <sys/ioctl.h>
28 #include <sys/mman.h>
29 #include <sys/socket.h>
30 #include <sys/types.h>
31 #include <linux/if_link.h>
32
33 #include <bpf/bpf.h>
34 #include <bpf/libbpf.h>
35 #include "xsk.h"
36 #include "bpf_util.h"
37
38 #ifndef SOL_XDP
39  #define SOL_XDP 283
40 #endif
41
42 #ifndef AF_XDP
43  #define AF_XDP 44
44 #endif
45
46 #ifndef PF_XDP
47  #define PF_XDP AF_XDP
48 #endif
49
50 #define pr_warn(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__)
51
52 #define XSKMAP_SIZE 1
53
54 struct xsk_umem {
55         struct xsk_ring_prod *fill_save;
56         struct xsk_ring_cons *comp_save;
57         char *umem_area;
58         struct xsk_umem_config config;
59         int fd;
60         int refcount;
61         struct list_head ctx_list;
62         bool rx_ring_setup_done;
63         bool tx_ring_setup_done;
64 };
65
66 struct xsk_ctx {
67         struct xsk_ring_prod *fill;
68         struct xsk_ring_cons *comp;
69         __u32 queue_id;
70         struct xsk_umem *umem;
71         int refcount;
72         int ifindex;
73         struct list_head list;
74 };
75
76 struct xsk_socket {
77         struct xsk_ring_cons *rx;
78         struct xsk_ring_prod *tx;
79         struct xsk_ctx *ctx;
80         struct xsk_socket_config config;
81         int fd;
82 };
83
84 int xsk_umem__fd(const struct xsk_umem *umem)
85 {
86         return umem ? umem->fd : -EINVAL;
87 }
88
89 int xsk_socket__fd(const struct xsk_socket *xsk)
90 {
91         return xsk ? xsk->fd : -EINVAL;
92 }
93
94 static bool xsk_page_aligned(void *buffer)
95 {
96         unsigned long addr = (unsigned long)buffer;
97
98         return !(addr & (getpagesize() - 1));
99 }
100
101 static void xsk_set_umem_config(struct xsk_umem_config *cfg,
102                                 const struct xsk_umem_config *usr_cfg)
103 {
104         if (!usr_cfg) {
105                 cfg->fill_size = XSK_RING_PROD__DEFAULT_NUM_DESCS;
106                 cfg->comp_size = XSK_RING_CONS__DEFAULT_NUM_DESCS;
107                 cfg->frame_size = XSK_UMEM__DEFAULT_FRAME_SIZE;
108                 cfg->frame_headroom = XSK_UMEM__DEFAULT_FRAME_HEADROOM;
109                 cfg->flags = XSK_UMEM__DEFAULT_FLAGS;
110                 return;
111         }
112
113         cfg->fill_size = usr_cfg->fill_size;
114         cfg->comp_size = usr_cfg->comp_size;
115         cfg->frame_size = usr_cfg->frame_size;
116         cfg->frame_headroom = usr_cfg->frame_headroom;
117         cfg->flags = usr_cfg->flags;
118 }
119
120 static int xsk_set_xdp_socket_config(struct xsk_socket_config *cfg,
121                                      const struct xsk_socket_config *usr_cfg)
122 {
123         if (!usr_cfg) {
124                 cfg->rx_size = XSK_RING_CONS__DEFAULT_NUM_DESCS;
125                 cfg->tx_size = XSK_RING_PROD__DEFAULT_NUM_DESCS;
126                 cfg->bind_flags = 0;
127                 return 0;
128         }
129
130         cfg->rx_size = usr_cfg->rx_size;
131         cfg->tx_size = usr_cfg->tx_size;
132         cfg->bind_flags = usr_cfg->bind_flags;
133
134         return 0;
135 }
136
137 static int xsk_get_mmap_offsets(int fd, struct xdp_mmap_offsets *off)
138 {
139         socklen_t optlen;
140         int err;
141
142         optlen = sizeof(*off);
143         err = getsockopt(fd, SOL_XDP, XDP_MMAP_OFFSETS, off, &optlen);
144         if (err)
145                 return err;
146
147         if (optlen == sizeof(*off))
148                 return 0;
149
150         return -EINVAL;
151 }
152
153 static int xsk_create_umem_rings(struct xsk_umem *umem, int fd,
154                                  struct xsk_ring_prod *fill,
155                                  struct xsk_ring_cons *comp)
156 {
157         struct xdp_mmap_offsets off;
158         void *map;
159         int err;
160
161         err = setsockopt(fd, SOL_XDP, XDP_UMEM_FILL_RING,
162                          &umem->config.fill_size,
163                          sizeof(umem->config.fill_size));
164         if (err)
165                 return -errno;
166
167         err = setsockopt(fd, SOL_XDP, XDP_UMEM_COMPLETION_RING,
168                          &umem->config.comp_size,
169                          sizeof(umem->config.comp_size));
170         if (err)
171                 return -errno;
172
173         err = xsk_get_mmap_offsets(fd, &off);
174         if (err)
175                 return -errno;
176
177         map = mmap(NULL, off.fr.desc + umem->config.fill_size * sizeof(__u64),
178                    PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, fd,
179                    XDP_UMEM_PGOFF_FILL_RING);
180         if (map == MAP_FAILED)
181                 return -errno;
182
183         fill->mask = umem->config.fill_size - 1;
184         fill->size = umem->config.fill_size;
185         fill->producer = map + off.fr.producer;
186         fill->consumer = map + off.fr.consumer;
187         fill->flags = map + off.fr.flags;
188         fill->ring = map + off.fr.desc;
189         fill->cached_cons = umem->config.fill_size;
190
191         map = mmap(NULL, off.cr.desc + umem->config.comp_size * sizeof(__u64),
192                    PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, fd,
193                    XDP_UMEM_PGOFF_COMPLETION_RING);
194         if (map == MAP_FAILED) {
195                 err = -errno;
196                 goto out_mmap;
197         }
198
199         comp->mask = umem->config.comp_size - 1;
200         comp->size = umem->config.comp_size;
201         comp->producer = map + off.cr.producer;
202         comp->consumer = map + off.cr.consumer;
203         comp->flags = map + off.cr.flags;
204         comp->ring = map + off.cr.desc;
205
206         return 0;
207
208 out_mmap:
209         munmap(map, off.fr.desc + umem->config.fill_size * sizeof(__u64));
210         return err;
211 }
212
213 int xsk_umem__create(struct xsk_umem **umem_ptr, void *umem_area,
214                      __u64 size, struct xsk_ring_prod *fill,
215                      struct xsk_ring_cons *comp,
216                      const struct xsk_umem_config *usr_config)
217 {
218         struct xdp_umem_reg mr;
219         struct xsk_umem *umem;
220         int err;
221
222         if (!umem_area || !umem_ptr || !fill || !comp)
223                 return -EFAULT;
224         if (!size && !xsk_page_aligned(umem_area))
225                 return -EINVAL;
226
227         umem = calloc(1, sizeof(*umem));
228         if (!umem)
229                 return -ENOMEM;
230
231         umem->fd = socket(AF_XDP, SOCK_RAW | SOCK_CLOEXEC, 0);
232         if (umem->fd < 0) {
233                 err = -errno;
234                 goto out_umem_alloc;
235         }
236
237         umem->umem_area = umem_area;
238         INIT_LIST_HEAD(&umem->ctx_list);
239         xsk_set_umem_config(&umem->config, usr_config);
240
241         memset(&mr, 0, sizeof(mr));
242         mr.addr = (uintptr_t)umem_area;
243         mr.len = size;
244         mr.chunk_size = umem->config.frame_size;
245         mr.headroom = umem->config.frame_headroom;
246         mr.flags = umem->config.flags;
247
248         err = setsockopt(umem->fd, SOL_XDP, XDP_UMEM_REG, &mr, sizeof(mr));
249         if (err) {
250                 err = -errno;
251                 goto out_socket;
252         }
253
254         err = xsk_create_umem_rings(umem, umem->fd, fill, comp);
255         if (err)
256                 goto out_socket;
257
258         umem->fill_save = fill;
259         umem->comp_save = comp;
260         *umem_ptr = umem;
261         return 0;
262
263 out_socket:
264         close(umem->fd);
265 out_umem_alloc:
266         free(umem);
267         return err;
268 }
269
270 bool xsk_is_in_mode(u32 ifindex, int mode)
271 {
272         LIBBPF_OPTS(bpf_xdp_query_opts, opts);
273         int ret;
274
275         ret = bpf_xdp_query(ifindex, mode, &opts);
276         if (ret) {
277                 printf("XDP mode query returned error %s\n", strerror(errno));
278                 return false;
279         }
280
281         if (mode == XDP_FLAGS_DRV_MODE)
282                 return opts.attach_mode == XDP_ATTACHED_DRV;
283         else if (mode == XDP_FLAGS_SKB_MODE)
284                 return opts.attach_mode == XDP_ATTACHED_SKB;
285
286         return false;
287 }
288
289 int xsk_attach_xdp_program(struct bpf_program *prog, int ifindex, u32 xdp_flags)
290 {
291         int prog_fd;
292
293         prog_fd = bpf_program__fd(prog);
294         return bpf_xdp_attach(ifindex, prog_fd, xdp_flags, NULL);
295 }
296
297 void xsk_detach_xdp_program(int ifindex, u32 xdp_flags)
298 {
299         bpf_xdp_detach(ifindex, xdp_flags, NULL);
300 }
301
302 void xsk_clear_xskmap(struct bpf_map *map)
303 {
304         u32 index = 0;
305         int map_fd;
306
307         map_fd = bpf_map__fd(map);
308         bpf_map_delete_elem(map_fd, &index);
309 }
310
311 int xsk_update_xskmap(struct bpf_map *map, struct xsk_socket *xsk)
312 {
313         int map_fd, sock_fd;
314         u32 index = 0;
315
316         map_fd = bpf_map__fd(map);
317         sock_fd = xsk_socket__fd(xsk);
318
319         return bpf_map_update_elem(map_fd, &index, &sock_fd, 0);
320 }
321
322 static struct xsk_ctx *xsk_get_ctx(struct xsk_umem *umem, int ifindex,
323                                    __u32 queue_id)
324 {
325         struct xsk_ctx *ctx;
326
327         if (list_empty(&umem->ctx_list))
328                 return NULL;
329
330         list_for_each_entry(ctx, &umem->ctx_list, list) {
331                 if (ctx->ifindex == ifindex && ctx->queue_id == queue_id) {
332                         ctx->refcount++;
333                         return ctx;
334                 }
335         }
336
337         return NULL;
338 }
339
340 static void xsk_put_ctx(struct xsk_ctx *ctx, bool unmap)
341 {
342         struct xsk_umem *umem = ctx->umem;
343         struct xdp_mmap_offsets off;
344         int err;
345
346         if (--ctx->refcount)
347                 return;
348
349         if (!unmap)
350                 goto out_free;
351
352         err = xsk_get_mmap_offsets(umem->fd, &off);
353         if (err)
354                 goto out_free;
355
356         munmap(ctx->fill->ring - off.fr.desc, off.fr.desc + umem->config.fill_size *
357                sizeof(__u64));
358         munmap(ctx->comp->ring - off.cr.desc, off.cr.desc + umem->config.comp_size *
359                sizeof(__u64));
360
361 out_free:
362         list_del(&ctx->list);
363         free(ctx);
364 }
365
366 static struct xsk_ctx *xsk_create_ctx(struct xsk_socket *xsk,
367                                       struct xsk_umem *umem, int ifindex,
368                                       __u32 queue_id,
369                                       struct xsk_ring_prod *fill,
370                                       struct xsk_ring_cons *comp)
371 {
372         struct xsk_ctx *ctx;
373         int err;
374
375         ctx = calloc(1, sizeof(*ctx));
376         if (!ctx)
377                 return NULL;
378
379         if (!umem->fill_save) {
380                 err = xsk_create_umem_rings(umem, xsk->fd, fill, comp);
381                 if (err) {
382                         free(ctx);
383                         return NULL;
384                 }
385         } else if (umem->fill_save != fill || umem->comp_save != comp) {
386                 /* Copy over rings to new structs. */
387                 memcpy(fill, umem->fill_save, sizeof(*fill));
388                 memcpy(comp, umem->comp_save, sizeof(*comp));
389         }
390
391         ctx->ifindex = ifindex;
392         ctx->refcount = 1;
393         ctx->umem = umem;
394         ctx->queue_id = queue_id;
395
396         ctx->fill = fill;
397         ctx->comp = comp;
398         list_add(&ctx->list, &umem->ctx_list);
399         return ctx;
400 }
401
402 int xsk_socket__create_shared(struct xsk_socket **xsk_ptr,
403                               int ifindex,
404                               __u32 queue_id, struct xsk_umem *umem,
405                               struct xsk_ring_cons *rx,
406                               struct xsk_ring_prod *tx,
407                               struct xsk_ring_prod *fill,
408                               struct xsk_ring_cons *comp,
409                               const struct xsk_socket_config *usr_config)
410 {
411         bool unmap, rx_setup_done = false, tx_setup_done = false;
412         void *rx_map = NULL, *tx_map = NULL;
413         struct sockaddr_xdp sxdp = {};
414         struct xdp_mmap_offsets off;
415         struct xsk_socket *xsk;
416         struct xsk_ctx *ctx;
417         int err;
418
419         if (!umem || !xsk_ptr || !(rx || tx))
420                 return -EFAULT;
421
422         unmap = umem->fill_save != fill;
423
424         xsk = calloc(1, sizeof(*xsk));
425         if (!xsk)
426                 return -ENOMEM;
427
428         err = xsk_set_xdp_socket_config(&xsk->config, usr_config);
429         if (err)
430                 goto out_xsk_alloc;
431
432         if (umem->refcount++ > 0) {
433                 xsk->fd = socket(AF_XDP, SOCK_RAW | SOCK_CLOEXEC, 0);
434                 if (xsk->fd < 0) {
435                         err = -errno;
436                         goto out_xsk_alloc;
437                 }
438         } else {
439                 xsk->fd = umem->fd;
440                 rx_setup_done = umem->rx_ring_setup_done;
441                 tx_setup_done = umem->tx_ring_setup_done;
442         }
443
444         ctx = xsk_get_ctx(umem, ifindex, queue_id);
445         if (!ctx) {
446                 if (!fill || !comp) {
447                         err = -EFAULT;
448                         goto out_socket;
449                 }
450
451                 ctx = xsk_create_ctx(xsk, umem, ifindex, queue_id, fill, comp);
452                 if (!ctx) {
453                         err = -ENOMEM;
454                         goto out_socket;
455                 }
456         }
457         xsk->ctx = ctx;
458
459         if (rx && !rx_setup_done) {
460                 err = setsockopt(xsk->fd, SOL_XDP, XDP_RX_RING,
461                                  &xsk->config.rx_size,
462                                  sizeof(xsk->config.rx_size));
463                 if (err) {
464                         err = -errno;
465                         goto out_put_ctx;
466                 }
467                 if (xsk->fd == umem->fd)
468                         umem->rx_ring_setup_done = true;
469         }
470         if (tx && !tx_setup_done) {
471                 err = setsockopt(xsk->fd, SOL_XDP, XDP_TX_RING,
472                                  &xsk->config.tx_size,
473                                  sizeof(xsk->config.tx_size));
474                 if (err) {
475                         err = -errno;
476                         goto out_put_ctx;
477                 }
478                 if (xsk->fd == umem->fd)
479                         umem->tx_ring_setup_done = true;
480         }
481
482         err = xsk_get_mmap_offsets(xsk->fd, &off);
483         if (err) {
484                 err = -errno;
485                 goto out_put_ctx;
486         }
487
488         if (rx) {
489                 rx_map = mmap(NULL, off.rx.desc +
490                               xsk->config.rx_size * sizeof(struct xdp_desc),
491                               PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE,
492                               xsk->fd, XDP_PGOFF_RX_RING);
493                 if (rx_map == MAP_FAILED) {
494                         err = -errno;
495                         goto out_put_ctx;
496                 }
497
498                 rx->mask = xsk->config.rx_size - 1;
499                 rx->size = xsk->config.rx_size;
500                 rx->producer = rx_map + off.rx.producer;
501                 rx->consumer = rx_map + off.rx.consumer;
502                 rx->flags = rx_map + off.rx.flags;
503                 rx->ring = rx_map + off.rx.desc;
504                 rx->cached_prod = *rx->producer;
505                 rx->cached_cons = *rx->consumer;
506         }
507         xsk->rx = rx;
508
509         if (tx) {
510                 tx_map = mmap(NULL, off.tx.desc +
511                               xsk->config.tx_size * sizeof(struct xdp_desc),
512                               PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE,
513                               xsk->fd, XDP_PGOFF_TX_RING);
514                 if (tx_map == MAP_FAILED) {
515                         err = -errno;
516                         goto out_mmap_rx;
517                 }
518
519                 tx->mask = xsk->config.tx_size - 1;
520                 tx->size = xsk->config.tx_size;
521                 tx->producer = tx_map + off.tx.producer;
522                 tx->consumer = tx_map + off.tx.consumer;
523                 tx->flags = tx_map + off.tx.flags;
524                 tx->ring = tx_map + off.tx.desc;
525                 tx->cached_prod = *tx->producer;
526                 /* cached_cons is r->size bigger than the real consumer pointer
527                  * See xsk_prod_nb_free
528                  */
529                 tx->cached_cons = *tx->consumer + xsk->config.tx_size;
530         }
531         xsk->tx = tx;
532
533         sxdp.sxdp_family = PF_XDP;
534         sxdp.sxdp_ifindex = ctx->ifindex;
535         sxdp.sxdp_queue_id = ctx->queue_id;
536         if (umem->refcount > 1) {
537                 sxdp.sxdp_flags |= XDP_SHARED_UMEM;
538                 sxdp.sxdp_shared_umem_fd = umem->fd;
539         } else {
540                 sxdp.sxdp_flags = xsk->config.bind_flags;
541         }
542
543         err = bind(xsk->fd, (struct sockaddr *)&sxdp, sizeof(sxdp));
544         if (err) {
545                 err = -errno;
546                 goto out_mmap_tx;
547         }
548
549         *xsk_ptr = xsk;
550         umem->fill_save = NULL;
551         umem->comp_save = NULL;
552         return 0;
553
554 out_mmap_tx:
555         if (tx)
556                 munmap(tx_map, off.tx.desc +
557                        xsk->config.tx_size * sizeof(struct xdp_desc));
558 out_mmap_rx:
559         if (rx)
560                 munmap(rx_map, off.rx.desc +
561                        xsk->config.rx_size * sizeof(struct xdp_desc));
562 out_put_ctx:
563         xsk_put_ctx(ctx, unmap);
564 out_socket:
565         if (--umem->refcount)
566                 close(xsk->fd);
567 out_xsk_alloc:
568         free(xsk);
569         return err;
570 }
571
572 int xsk_socket__create(struct xsk_socket **xsk_ptr, int ifindex,
573                        __u32 queue_id, struct xsk_umem *umem,
574                        struct xsk_ring_cons *rx, struct xsk_ring_prod *tx,
575                        const struct xsk_socket_config *usr_config)
576 {
577         if (!umem)
578                 return -EFAULT;
579
580         return xsk_socket__create_shared(xsk_ptr, ifindex, queue_id, umem,
581                                          rx, tx, umem->fill_save,
582                                          umem->comp_save, usr_config);
583 }
584
585 int xsk_umem__delete(struct xsk_umem *umem)
586 {
587         struct xdp_mmap_offsets off;
588         int err;
589
590         if (!umem)
591                 return 0;
592
593         if (umem->refcount)
594                 return -EBUSY;
595
596         err = xsk_get_mmap_offsets(umem->fd, &off);
597         if (!err && umem->fill_save && umem->comp_save) {
598                 munmap(umem->fill_save->ring - off.fr.desc,
599                        off.fr.desc + umem->config.fill_size * sizeof(__u64));
600                 munmap(umem->comp_save->ring - off.cr.desc,
601                        off.cr.desc + umem->config.comp_size * sizeof(__u64));
602         }
603
604         close(umem->fd);
605         free(umem);
606
607         return 0;
608 }
609
610 void xsk_socket__delete(struct xsk_socket *xsk)
611 {
612         size_t desc_sz = sizeof(struct xdp_desc);
613         struct xdp_mmap_offsets off;
614         struct xsk_umem *umem;
615         struct xsk_ctx *ctx;
616         int err;
617
618         if (!xsk)
619                 return;
620
621         ctx = xsk->ctx;
622         umem = ctx->umem;
623
624         xsk_put_ctx(ctx, true);
625
626         err = xsk_get_mmap_offsets(xsk->fd, &off);
627         if (!err) {
628                 if (xsk->rx) {
629                         munmap(xsk->rx->ring - off.rx.desc,
630                                off.rx.desc + xsk->config.rx_size * desc_sz);
631                 }
632                 if (xsk->tx) {
633                         munmap(xsk->tx->ring - off.tx.desc,
634                                off.tx.desc + xsk->config.tx_size * desc_sz);
635                 }
636         }
637
638         umem->refcount--;
639         /* Do not close an fd that also has an associated umem connected
640          * to it.
641          */
642         if (xsk->fd != umem->fd)
643                 close(xsk->fd);
644         free(xsk);
645 }
This page took 0.101907 seconds and 4 git commands to generate.