]> asedeno.scripts.mit.edu Git - linux.git/blob - tools/lib/bpf/xsk.c
Merge tag 'v5.3-rc1' into regulator-5.3
[linux.git] / tools / lib / 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 <magnus.karlsson@intel.com>
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/sockios.h>
24 #include <net/if.h>
25 #include <sys/ioctl.h>
26 #include <sys/mman.h>
27 #include <sys/socket.h>
28 #include <sys/types.h>
29
30 #include "bpf.h"
31 #include "libbpf.h"
32 #include "libbpf_internal.h"
33 #include "xsk.h"
34
35 #ifndef SOL_XDP
36  #define SOL_XDP 283
37 #endif
38
39 #ifndef AF_XDP
40  #define AF_XDP 44
41 #endif
42
43 #ifndef PF_XDP
44  #define PF_XDP AF_XDP
45 #endif
46
47 struct xsk_umem {
48         struct xsk_ring_prod *fill;
49         struct xsk_ring_cons *comp;
50         char *umem_area;
51         struct xsk_umem_config config;
52         int fd;
53         int refcount;
54 };
55
56 struct xsk_socket {
57         struct xsk_ring_cons *rx;
58         struct xsk_ring_prod *tx;
59         __u64 outstanding_tx;
60         struct xsk_umem *umem;
61         struct xsk_socket_config config;
62         int fd;
63         int ifindex;
64         int prog_fd;
65         int xsks_map_fd;
66         __u32 queue_id;
67         char ifname[IFNAMSIZ];
68         bool zc;
69 };
70
71 struct xsk_nl_info {
72         bool xdp_prog_attached;
73         int ifindex;
74         int fd;
75 };
76
77 /* For 32-bit systems, we need to use mmap2 as the offsets are 64-bit.
78  * Unfortunately, it is not part of glibc.
79  */
80 static inline void *xsk_mmap(void *addr, size_t length, int prot, int flags,
81                              int fd, __u64 offset)
82 {
83 #ifdef __NR_mmap2
84         unsigned int page_shift = __builtin_ffs(getpagesize()) - 1;
85         long ret = syscall(__NR_mmap2, addr, length, prot, flags, fd,
86                            (off_t)(offset >> page_shift));
87
88         return (void *)ret;
89 #else
90         return mmap(addr, length, prot, flags, fd, offset);
91 #endif
92 }
93
94 int xsk_umem__fd(const struct xsk_umem *umem)
95 {
96         return umem ? umem->fd : -EINVAL;
97 }
98
99 int xsk_socket__fd(const struct xsk_socket *xsk)
100 {
101         return xsk ? xsk->fd : -EINVAL;
102 }
103
104 static bool xsk_page_aligned(void *buffer)
105 {
106         unsigned long addr = (unsigned long)buffer;
107
108         return !(addr & (getpagesize() - 1));
109 }
110
111 static void xsk_set_umem_config(struct xsk_umem_config *cfg,
112                                 const struct xsk_umem_config *usr_cfg)
113 {
114         if (!usr_cfg) {
115                 cfg->fill_size = XSK_RING_PROD__DEFAULT_NUM_DESCS;
116                 cfg->comp_size = XSK_RING_CONS__DEFAULT_NUM_DESCS;
117                 cfg->frame_size = XSK_UMEM__DEFAULT_FRAME_SIZE;
118                 cfg->frame_headroom = XSK_UMEM__DEFAULT_FRAME_HEADROOM;
119                 return;
120         }
121
122         cfg->fill_size = usr_cfg->fill_size;
123         cfg->comp_size = usr_cfg->comp_size;
124         cfg->frame_size = usr_cfg->frame_size;
125         cfg->frame_headroom = usr_cfg->frame_headroom;
126 }
127
128 static int xsk_set_xdp_socket_config(struct xsk_socket_config *cfg,
129                                      const struct xsk_socket_config *usr_cfg)
130 {
131         if (!usr_cfg) {
132                 cfg->rx_size = XSK_RING_CONS__DEFAULT_NUM_DESCS;
133                 cfg->tx_size = XSK_RING_PROD__DEFAULT_NUM_DESCS;
134                 cfg->libbpf_flags = 0;
135                 cfg->xdp_flags = 0;
136                 cfg->bind_flags = 0;
137                 return 0;
138         }
139
140         if (usr_cfg->libbpf_flags & ~XSK_LIBBPF_FLAGS__INHIBIT_PROG_LOAD)
141                 return -EINVAL;
142
143         cfg->rx_size = usr_cfg->rx_size;
144         cfg->tx_size = usr_cfg->tx_size;
145         cfg->libbpf_flags = usr_cfg->libbpf_flags;
146         cfg->xdp_flags = usr_cfg->xdp_flags;
147         cfg->bind_flags = usr_cfg->bind_flags;
148
149         return 0;
150 }
151
152 int xsk_umem__create(struct xsk_umem **umem_ptr, void *umem_area, __u64 size,
153                      struct xsk_ring_prod *fill, struct xsk_ring_cons *comp,
154                      const struct xsk_umem_config *usr_config)
155 {
156         struct xdp_mmap_offsets off;
157         struct xdp_umem_reg mr;
158         struct xsk_umem *umem;
159         socklen_t optlen;
160         void *map;
161         int err;
162
163         if (!umem_area || !umem_ptr || !fill || !comp)
164                 return -EFAULT;
165         if (!size && !xsk_page_aligned(umem_area))
166                 return -EINVAL;
167
168         umem = calloc(1, sizeof(*umem));
169         if (!umem)
170                 return -ENOMEM;
171
172         umem->fd = socket(AF_XDP, SOCK_RAW, 0);
173         if (umem->fd < 0) {
174                 err = -errno;
175                 goto out_umem_alloc;
176         }
177
178         umem->umem_area = umem_area;
179         xsk_set_umem_config(&umem->config, usr_config);
180
181         mr.addr = (uintptr_t)umem_area;
182         mr.len = size;
183         mr.chunk_size = umem->config.frame_size;
184         mr.headroom = umem->config.frame_headroom;
185
186         err = setsockopt(umem->fd, SOL_XDP, XDP_UMEM_REG, &mr, sizeof(mr));
187         if (err) {
188                 err = -errno;
189                 goto out_socket;
190         }
191         err = setsockopt(umem->fd, SOL_XDP, XDP_UMEM_FILL_RING,
192                          &umem->config.fill_size,
193                          sizeof(umem->config.fill_size));
194         if (err) {
195                 err = -errno;
196                 goto out_socket;
197         }
198         err = setsockopt(umem->fd, SOL_XDP, XDP_UMEM_COMPLETION_RING,
199                          &umem->config.comp_size,
200                          sizeof(umem->config.comp_size));
201         if (err) {
202                 err = -errno;
203                 goto out_socket;
204         }
205
206         optlen = sizeof(off);
207         err = getsockopt(umem->fd, SOL_XDP, XDP_MMAP_OFFSETS, &off, &optlen);
208         if (err) {
209                 err = -errno;
210                 goto out_socket;
211         }
212
213         map = xsk_mmap(NULL, off.fr.desc +
214                        umem->config.fill_size * sizeof(__u64),
215                        PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE,
216                        umem->fd, XDP_UMEM_PGOFF_FILL_RING);
217         if (map == MAP_FAILED) {
218                 err = -errno;
219                 goto out_socket;
220         }
221
222         umem->fill = fill;
223         fill->mask = umem->config.fill_size - 1;
224         fill->size = umem->config.fill_size;
225         fill->producer = map + off.fr.producer;
226         fill->consumer = map + off.fr.consumer;
227         fill->ring = map + off.fr.desc;
228         fill->cached_cons = umem->config.fill_size;
229
230         map = xsk_mmap(NULL,
231                        off.cr.desc + umem->config.comp_size * sizeof(__u64),
232                        PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE,
233                        umem->fd, XDP_UMEM_PGOFF_COMPLETION_RING);
234         if (map == MAP_FAILED) {
235                 err = -errno;
236                 goto out_mmap;
237         }
238
239         umem->comp = comp;
240         comp->mask = umem->config.comp_size - 1;
241         comp->size = umem->config.comp_size;
242         comp->producer = map + off.cr.producer;
243         comp->consumer = map + off.cr.consumer;
244         comp->ring = map + off.cr.desc;
245
246         *umem_ptr = umem;
247         return 0;
248
249 out_mmap:
250         munmap(map, off.fr.desc + umem->config.fill_size * sizeof(__u64));
251 out_socket:
252         close(umem->fd);
253 out_umem_alloc:
254         free(umem);
255         return err;
256 }
257
258 static int xsk_load_xdp_prog(struct xsk_socket *xsk)
259 {
260         static const int log_buf_size = 16 * 1024;
261         char log_buf[log_buf_size];
262         int err, prog_fd;
263
264         /* This is the C-program:
265          * SEC("xdp_sock") int xdp_sock_prog(struct xdp_md *ctx)
266          * {
267          *     int index = ctx->rx_queue_index;
268          *
269          *     // A set entry here means that the correspnding queue_id
270          *     // has an active AF_XDP socket bound to it.
271          *     if (bpf_map_lookup_elem(&xsks_map, &index))
272          *         return bpf_redirect_map(&xsks_map, index, 0);
273          *
274          *     return XDP_PASS;
275          * }
276          */
277         struct bpf_insn prog[] = {
278                 /* r1 = *(u32 *)(r1 + 16) */
279                 BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_1, 16),
280                 /* *(u32 *)(r10 - 4) = r1 */
281                 BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_1, -4),
282                 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
283                 BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),
284                 BPF_LD_MAP_FD(BPF_REG_1, xsk->xsks_map_fd),
285                 BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
286                 BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
287                 BPF_MOV32_IMM(BPF_REG_0, 2),
288                 /* if r1 == 0 goto +5 */
289                 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 5),
290                 /* r2 = *(u32 *)(r10 - 4) */
291                 BPF_LD_MAP_FD(BPF_REG_1, xsk->xsks_map_fd),
292                 BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_10, -4),
293                 BPF_MOV32_IMM(BPF_REG_3, 0),
294                 BPF_EMIT_CALL(BPF_FUNC_redirect_map),
295                 /* The jumps are to this instruction */
296                 BPF_EXIT_INSN(),
297         };
298         size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn);
299
300         prog_fd = bpf_load_program(BPF_PROG_TYPE_XDP, prog, insns_cnt,
301                                    "LGPL-2.1 or BSD-2-Clause", 0, log_buf,
302                                    log_buf_size);
303         if (prog_fd < 0) {
304                 pr_warning("BPF log buffer:\n%s", log_buf);
305                 return prog_fd;
306         }
307
308         err = bpf_set_link_xdp_fd(xsk->ifindex, prog_fd, xsk->config.xdp_flags);
309         if (err) {
310                 close(prog_fd);
311                 return err;
312         }
313
314         xsk->prog_fd = prog_fd;
315         return 0;
316 }
317
318 static int xsk_get_max_queues(struct xsk_socket *xsk)
319 {
320         struct ethtool_channels channels;
321         struct ifreq ifr;
322         int fd, err, ret;
323
324         fd = socket(AF_INET, SOCK_DGRAM, 0);
325         if (fd < 0)
326                 return -errno;
327
328         channels.cmd = ETHTOOL_GCHANNELS;
329         ifr.ifr_data = (void *)&channels;
330         strncpy(ifr.ifr_name, xsk->ifname, IFNAMSIZ - 1);
331         ifr.ifr_name[IFNAMSIZ - 1] = '\0';
332         err = ioctl(fd, SIOCETHTOOL, &ifr);
333         if (err && errno != EOPNOTSUPP) {
334                 ret = -errno;
335                 goto out;
336         }
337
338         if (channels.max_combined == 0 || errno == EOPNOTSUPP)
339                 /* If the device says it has no channels, then all traffic
340                  * is sent to a single stream, so max queues = 1.
341                  */
342                 ret = 1;
343         else
344                 ret = channels.max_combined;
345
346 out:
347         close(fd);
348         return ret;
349 }
350
351 static int xsk_create_bpf_maps(struct xsk_socket *xsk)
352 {
353         int max_queues;
354         int fd;
355
356         max_queues = xsk_get_max_queues(xsk);
357         if (max_queues < 0)
358                 return max_queues;
359
360         fd = bpf_create_map_name(BPF_MAP_TYPE_XSKMAP, "xsks_map",
361                                  sizeof(int), sizeof(int), max_queues, 0);
362         if (fd < 0)
363                 return fd;
364
365         xsk->xsks_map_fd = fd;
366
367         return 0;
368 }
369
370 static void xsk_delete_bpf_maps(struct xsk_socket *xsk)
371 {
372         bpf_map_delete_elem(xsk->xsks_map_fd, &xsk->queue_id);
373         close(xsk->xsks_map_fd);
374 }
375
376 static int xsk_lookup_bpf_maps(struct xsk_socket *xsk)
377 {
378         __u32 i, *map_ids, num_maps, prog_len = sizeof(struct bpf_prog_info);
379         __u32 map_len = sizeof(struct bpf_map_info);
380         struct bpf_prog_info prog_info = {};
381         struct bpf_map_info map_info;
382         int fd, err;
383
384         err = bpf_obj_get_info_by_fd(xsk->prog_fd, &prog_info, &prog_len);
385         if (err)
386                 return err;
387
388         num_maps = prog_info.nr_map_ids;
389
390         map_ids = calloc(prog_info.nr_map_ids, sizeof(*map_ids));
391         if (!map_ids)
392                 return -ENOMEM;
393
394         memset(&prog_info, 0, prog_len);
395         prog_info.nr_map_ids = num_maps;
396         prog_info.map_ids = (__u64)(unsigned long)map_ids;
397
398         err = bpf_obj_get_info_by_fd(xsk->prog_fd, &prog_info, &prog_len);
399         if (err)
400                 goto out_map_ids;
401
402         xsk->xsks_map_fd = -1;
403
404         for (i = 0; i < prog_info.nr_map_ids; i++) {
405                 fd = bpf_map_get_fd_by_id(map_ids[i]);
406                 if (fd < 0)
407                         continue;
408
409                 err = bpf_obj_get_info_by_fd(fd, &map_info, &map_len);
410                 if (err) {
411                         close(fd);
412                         continue;
413                 }
414
415                 if (!strcmp(map_info.name, "xsks_map")) {
416                         xsk->xsks_map_fd = fd;
417                         continue;
418                 }
419
420                 close(fd);
421         }
422
423         err = 0;
424         if (xsk->xsks_map_fd == -1)
425                 err = -ENOENT;
426
427 out_map_ids:
428         free(map_ids);
429         return err;
430 }
431
432 static int xsk_set_bpf_maps(struct xsk_socket *xsk)
433 {
434         return bpf_map_update_elem(xsk->xsks_map_fd, &xsk->queue_id,
435                                    &xsk->fd, 0);
436 }
437
438 static int xsk_setup_xdp_prog(struct xsk_socket *xsk)
439 {
440         __u32 prog_id = 0;
441         int err;
442
443         err = bpf_get_link_xdp_id(xsk->ifindex, &prog_id,
444                                   xsk->config.xdp_flags);
445         if (err)
446                 return err;
447
448         if (!prog_id) {
449                 err = xsk_create_bpf_maps(xsk);
450                 if (err)
451                         return err;
452
453                 err = xsk_load_xdp_prog(xsk);
454                 if (err) {
455                         xsk_delete_bpf_maps(xsk);
456                         return err;
457                 }
458         } else {
459                 xsk->prog_fd = bpf_prog_get_fd_by_id(prog_id);
460                 err = xsk_lookup_bpf_maps(xsk);
461                 if (err) {
462                         close(xsk->prog_fd);
463                         return err;
464                 }
465         }
466
467         err = xsk_set_bpf_maps(xsk);
468         if (err) {
469                 xsk_delete_bpf_maps(xsk);
470                 close(xsk->prog_fd);
471                 return err;
472         }
473
474         return 0;
475 }
476
477 int xsk_socket__create(struct xsk_socket **xsk_ptr, const char *ifname,
478                        __u32 queue_id, struct xsk_umem *umem,
479                        struct xsk_ring_cons *rx, struct xsk_ring_prod *tx,
480                        const struct xsk_socket_config *usr_config)
481 {
482         void *rx_map = NULL, *tx_map = NULL;
483         struct sockaddr_xdp sxdp = {};
484         struct xdp_mmap_offsets off;
485         struct xdp_options opts;
486         struct xsk_socket *xsk;
487         socklen_t optlen;
488         int err;
489
490         if (!umem || !xsk_ptr || !rx || !tx)
491                 return -EFAULT;
492
493         if (umem->refcount) {
494                 pr_warning("Error: shared umems not supported by libbpf.\n");
495                 return -EBUSY;
496         }
497
498         xsk = calloc(1, sizeof(*xsk));
499         if (!xsk)
500                 return -ENOMEM;
501
502         if (umem->refcount++ > 0) {
503                 xsk->fd = socket(AF_XDP, SOCK_RAW, 0);
504                 if (xsk->fd < 0) {
505                         err = -errno;
506                         goto out_xsk_alloc;
507                 }
508         } else {
509                 xsk->fd = umem->fd;
510         }
511
512         xsk->outstanding_tx = 0;
513         xsk->queue_id = queue_id;
514         xsk->umem = umem;
515         xsk->ifindex = if_nametoindex(ifname);
516         if (!xsk->ifindex) {
517                 err = -errno;
518                 goto out_socket;
519         }
520         strncpy(xsk->ifname, ifname, IFNAMSIZ - 1);
521         xsk->ifname[IFNAMSIZ - 1] = '\0';
522
523         err = xsk_set_xdp_socket_config(&xsk->config, usr_config);
524         if (err)
525                 goto out_socket;
526
527         if (rx) {
528                 err = setsockopt(xsk->fd, SOL_XDP, XDP_RX_RING,
529                                  &xsk->config.rx_size,
530                                  sizeof(xsk->config.rx_size));
531                 if (err) {
532                         err = -errno;
533                         goto out_socket;
534                 }
535         }
536         if (tx) {
537                 err = setsockopt(xsk->fd, SOL_XDP, XDP_TX_RING,
538                                  &xsk->config.tx_size,
539                                  sizeof(xsk->config.tx_size));
540                 if (err) {
541                         err = -errno;
542                         goto out_socket;
543                 }
544         }
545
546         optlen = sizeof(off);
547         err = getsockopt(xsk->fd, SOL_XDP, XDP_MMAP_OFFSETS, &off, &optlen);
548         if (err) {
549                 err = -errno;
550                 goto out_socket;
551         }
552
553         if (rx) {
554                 rx_map = xsk_mmap(NULL, off.rx.desc +
555                                   xsk->config.rx_size * sizeof(struct xdp_desc),
556                                   PROT_READ | PROT_WRITE,
557                                   MAP_SHARED | MAP_POPULATE,
558                                   xsk->fd, XDP_PGOFF_RX_RING);
559                 if (rx_map == MAP_FAILED) {
560                         err = -errno;
561                         goto out_socket;
562                 }
563
564                 rx->mask = xsk->config.rx_size - 1;
565                 rx->size = xsk->config.rx_size;
566                 rx->producer = rx_map + off.rx.producer;
567                 rx->consumer = rx_map + off.rx.consumer;
568                 rx->ring = rx_map + off.rx.desc;
569         }
570         xsk->rx = rx;
571
572         if (tx) {
573                 tx_map = xsk_mmap(NULL, off.tx.desc +
574                                   xsk->config.tx_size * sizeof(struct xdp_desc),
575                                   PROT_READ | PROT_WRITE,
576                                   MAP_SHARED | MAP_POPULATE,
577                                   xsk->fd, XDP_PGOFF_TX_RING);
578                 if (tx_map == MAP_FAILED) {
579                         err = -errno;
580                         goto out_mmap_rx;
581                 }
582
583                 tx->mask = xsk->config.tx_size - 1;
584                 tx->size = xsk->config.tx_size;
585                 tx->producer = tx_map + off.tx.producer;
586                 tx->consumer = tx_map + off.tx.consumer;
587                 tx->ring = tx_map + off.tx.desc;
588                 tx->cached_cons = xsk->config.tx_size;
589         }
590         xsk->tx = tx;
591
592         sxdp.sxdp_family = PF_XDP;
593         sxdp.sxdp_ifindex = xsk->ifindex;
594         sxdp.sxdp_queue_id = xsk->queue_id;
595         sxdp.sxdp_flags = xsk->config.bind_flags;
596
597         err = bind(xsk->fd, (struct sockaddr *)&sxdp, sizeof(sxdp));
598         if (err) {
599                 err = -errno;
600                 goto out_mmap_tx;
601         }
602
603         xsk->prog_fd = -1;
604
605         optlen = sizeof(opts);
606         err = getsockopt(xsk->fd, SOL_XDP, XDP_OPTIONS, &opts, &optlen);
607         if (err) {
608                 err = -errno;
609                 goto out_mmap_tx;
610         }
611
612         xsk->zc = opts.flags & XDP_OPTIONS_ZEROCOPY;
613
614         if (!(xsk->config.libbpf_flags & XSK_LIBBPF_FLAGS__INHIBIT_PROG_LOAD)) {
615                 err = xsk_setup_xdp_prog(xsk);
616                 if (err)
617                         goto out_mmap_tx;
618         }
619
620         *xsk_ptr = xsk;
621         return 0;
622
623 out_mmap_tx:
624         if (tx)
625                 munmap(tx_map, off.tx.desc +
626                        xsk->config.tx_size * sizeof(struct xdp_desc));
627 out_mmap_rx:
628         if (rx)
629                 munmap(rx_map, off.rx.desc +
630                        xsk->config.rx_size * sizeof(struct xdp_desc));
631 out_socket:
632         if (--umem->refcount)
633                 close(xsk->fd);
634 out_xsk_alloc:
635         free(xsk);
636         return err;
637 }
638
639 int xsk_umem__delete(struct xsk_umem *umem)
640 {
641         struct xdp_mmap_offsets off;
642         socklen_t optlen;
643         int err;
644
645         if (!umem)
646                 return 0;
647
648         if (umem->refcount)
649                 return -EBUSY;
650
651         optlen = sizeof(off);
652         err = getsockopt(umem->fd, SOL_XDP, XDP_MMAP_OFFSETS, &off, &optlen);
653         if (!err) {
654                 munmap(umem->fill->ring - off.fr.desc,
655                        off.fr.desc + umem->config.fill_size * sizeof(__u64));
656                 munmap(umem->comp->ring - off.cr.desc,
657                        off.cr.desc + umem->config.comp_size * sizeof(__u64));
658         }
659
660         close(umem->fd);
661         free(umem);
662
663         return 0;
664 }
665
666 void xsk_socket__delete(struct xsk_socket *xsk)
667 {
668         size_t desc_sz = sizeof(struct xdp_desc);
669         struct xdp_mmap_offsets off;
670         socklen_t optlen;
671         int err;
672
673         if (!xsk)
674                 return;
675
676         if (xsk->prog_fd != -1) {
677                 xsk_delete_bpf_maps(xsk);
678                 close(xsk->prog_fd);
679         }
680
681         optlen = sizeof(off);
682         err = getsockopt(xsk->fd, SOL_XDP, XDP_MMAP_OFFSETS, &off, &optlen);
683         if (!err) {
684                 if (xsk->rx) {
685                         munmap(xsk->rx->ring - off.rx.desc,
686                                off.rx.desc + xsk->config.rx_size * desc_sz);
687                 }
688                 if (xsk->tx) {
689                         munmap(xsk->tx->ring - off.tx.desc,
690                                off.tx.desc + xsk->config.tx_size * desc_sz);
691                 }
692
693         }
694
695         xsk->umem->refcount--;
696         /* Do not close an fd that also has an associated umem connected
697          * to it.
698          */
699         if (xsk->fd != xsk->umem->fd)
700                 close(xsk->fd);
701         free(xsk);
702 }