162306a36Sopenharmony_ci/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci/*
462306a36Sopenharmony_ci * AF_XDP user-space access library.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (c) 2018 - 2019 Intel Corporation.
762306a36Sopenharmony_ci * Copyright (c) 2019 Facebook
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Author(s): Magnus Karlsson <magnus.karlsson@intel.com>
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#ifndef __XSK_H
1362306a36Sopenharmony_ci#define __XSK_H
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <stdio.h>
1662306a36Sopenharmony_ci#include <stdint.h>
1762306a36Sopenharmony_ci#include <stdbool.h>
1862306a36Sopenharmony_ci#include <linux/if_xdp.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include <bpf/libbpf.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#ifdef __cplusplus
2362306a36Sopenharmony_ciextern "C" {
2462306a36Sopenharmony_ci#endif
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/* Do not access these members directly. Use the functions below. */
2762306a36Sopenharmony_ci#define DEFINE_XSK_RING(name) \
2862306a36Sopenharmony_cistruct name { \
2962306a36Sopenharmony_ci	__u32 cached_prod; \
3062306a36Sopenharmony_ci	__u32 cached_cons; \
3162306a36Sopenharmony_ci	__u32 mask; \
3262306a36Sopenharmony_ci	__u32 size; \
3362306a36Sopenharmony_ci	__u32 *producer; \
3462306a36Sopenharmony_ci	__u32 *consumer; \
3562306a36Sopenharmony_ci	void *ring; \
3662306a36Sopenharmony_ci	__u32 *flags; \
3762306a36Sopenharmony_ci}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ciDEFINE_XSK_RING(xsk_ring_prod);
4062306a36Sopenharmony_ciDEFINE_XSK_RING(xsk_ring_cons);
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/* For a detailed explanation on the memory barriers associated with the
4362306a36Sopenharmony_ci * ring, please take a look at net/xdp/xsk_queue.h.
4462306a36Sopenharmony_ci */
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistruct xsk_umem;
4762306a36Sopenharmony_cistruct xsk_socket;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic inline __u64 *xsk_ring_prod__fill_addr(struct xsk_ring_prod *fill,
5062306a36Sopenharmony_ci					      __u32 idx)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	__u64 *addrs = (__u64 *)fill->ring;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	return &addrs[idx & fill->mask];
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic inline const __u64 *
5862306a36Sopenharmony_cixsk_ring_cons__comp_addr(const struct xsk_ring_cons *comp, __u32 idx)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	const __u64 *addrs = (const __u64 *)comp->ring;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	return &addrs[idx & comp->mask];
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic inline struct xdp_desc *xsk_ring_prod__tx_desc(struct xsk_ring_prod *tx,
6662306a36Sopenharmony_ci						      __u32 idx)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	struct xdp_desc *descs = (struct xdp_desc *)tx->ring;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	return &descs[idx & tx->mask];
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic inline const struct xdp_desc *
7462306a36Sopenharmony_cixsk_ring_cons__rx_desc(const struct xsk_ring_cons *rx, __u32 idx)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	const struct xdp_desc *descs = (const struct xdp_desc *)rx->ring;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	return &descs[idx & rx->mask];
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic inline int xsk_ring_prod__needs_wakeup(const struct xsk_ring_prod *r)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	return *r->flags & XDP_RING_NEED_WAKEUP;
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic inline __u32 xsk_prod_nb_free(struct xsk_ring_prod *r, __u32 nb)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	__u32 free_entries = r->cached_cons - r->cached_prod;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	if (free_entries >= nb)
9162306a36Sopenharmony_ci		return free_entries;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	/* Refresh the local tail pointer.
9462306a36Sopenharmony_ci	 * cached_cons is r->size bigger than the real consumer pointer so
9562306a36Sopenharmony_ci	 * that this addition can be avoided in the more frequently
9662306a36Sopenharmony_ci	 * executed code that computs free_entries in the beginning of
9762306a36Sopenharmony_ci	 * this function. Without this optimization it whould have been
9862306a36Sopenharmony_ci	 * free_entries = r->cached_prod - r->cached_cons + r->size.
9962306a36Sopenharmony_ci	 */
10062306a36Sopenharmony_ci	r->cached_cons = __atomic_load_n(r->consumer, __ATOMIC_ACQUIRE);
10162306a36Sopenharmony_ci	r->cached_cons += r->size;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	return r->cached_cons - r->cached_prod;
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic inline __u32 xsk_cons_nb_avail(struct xsk_ring_cons *r, __u32 nb)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	__u32 entries = r->cached_prod - r->cached_cons;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	if (entries == 0) {
11162306a36Sopenharmony_ci		r->cached_prod = __atomic_load_n(r->producer, __ATOMIC_ACQUIRE);
11262306a36Sopenharmony_ci		entries = r->cached_prod - r->cached_cons;
11362306a36Sopenharmony_ci	}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	return (entries > nb) ? nb : entries;
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic inline __u32 xsk_ring_prod__reserve(struct xsk_ring_prod *prod, __u32 nb, __u32 *idx)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	if (xsk_prod_nb_free(prod, nb) < nb)
12162306a36Sopenharmony_ci		return 0;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	*idx = prod->cached_prod;
12462306a36Sopenharmony_ci	prod->cached_prod += nb;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	return nb;
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic inline void xsk_ring_prod__submit(struct xsk_ring_prod *prod, __u32 nb)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	/* Make sure everything has been written to the ring before indicating
13262306a36Sopenharmony_ci	 * this to the kernel by writing the producer pointer.
13362306a36Sopenharmony_ci	 */
13462306a36Sopenharmony_ci	__atomic_store_n(prod->producer, *prod->producer + nb, __ATOMIC_RELEASE);
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic inline void xsk_ring_prod__cancel(struct xsk_ring_prod *prod, __u32 nb)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	prod->cached_prod -= nb;
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic inline __u32 xsk_ring_cons__peek(struct xsk_ring_cons *cons, __u32 nb, __u32 *idx)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	__u32 entries = xsk_cons_nb_avail(cons, nb);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	if (entries > 0) {
14762306a36Sopenharmony_ci		*idx = cons->cached_cons;
14862306a36Sopenharmony_ci		cons->cached_cons += entries;
14962306a36Sopenharmony_ci	}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	return entries;
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cistatic inline void xsk_ring_cons__cancel(struct xsk_ring_cons *cons, __u32 nb)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	cons->cached_cons -= nb;
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cistatic inline void xsk_ring_cons__release(struct xsk_ring_cons *cons, __u32 nb)
16062306a36Sopenharmony_ci{
16162306a36Sopenharmony_ci	/* Make sure data has been read before indicating we are done
16262306a36Sopenharmony_ci	 * with the entries by updating the consumer pointer.
16362306a36Sopenharmony_ci	 */
16462306a36Sopenharmony_ci	__atomic_store_n(cons->consumer, *cons->consumer + nb, __ATOMIC_RELEASE);
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_cistatic inline void *xsk_umem__get_data(void *umem_area, __u64 addr)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	return &((char *)umem_area)[addr];
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic inline __u64 xsk_umem__extract_addr(__u64 addr)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	return addr & XSK_UNALIGNED_BUF_ADDR_MASK;
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic inline __u64 xsk_umem__extract_offset(__u64 addr)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	return addr >> XSK_UNALIGNED_BUF_OFFSET_SHIFT;
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cistatic inline __u64 xsk_umem__add_offset_to_addr(__u64 addr)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	return xsk_umem__extract_addr(addr) + xsk_umem__extract_offset(addr);
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ciint xsk_umem__fd(const struct xsk_umem *umem);
18862306a36Sopenharmony_ciint xsk_socket__fd(const struct xsk_socket *xsk);
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci#define XSK_RING_CONS__DEFAULT_NUM_DESCS      2048
19162306a36Sopenharmony_ci#define XSK_RING_PROD__DEFAULT_NUM_DESCS      2048
19262306a36Sopenharmony_ci#define XSK_UMEM__DEFAULT_FRAME_SHIFT    12 /* 4096 bytes */
19362306a36Sopenharmony_ci#define XSK_UMEM__DEFAULT_FRAME_SIZE     (1 << XSK_UMEM__DEFAULT_FRAME_SHIFT)
19462306a36Sopenharmony_ci#define XSK_UMEM__DEFAULT_FRAME_HEADROOM 0
19562306a36Sopenharmony_ci#define XSK_UMEM__DEFAULT_FLAGS 0
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistruct xsk_umem_config {
19862306a36Sopenharmony_ci	__u32 fill_size;
19962306a36Sopenharmony_ci	__u32 comp_size;
20062306a36Sopenharmony_ci	__u32 frame_size;
20162306a36Sopenharmony_ci	__u32 frame_headroom;
20262306a36Sopenharmony_ci	__u32 flags;
20362306a36Sopenharmony_ci};
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ciint xsk_attach_xdp_program(struct bpf_program *prog, int ifindex, u32 xdp_flags);
20662306a36Sopenharmony_civoid xsk_detach_xdp_program(int ifindex, u32 xdp_flags);
20762306a36Sopenharmony_ciint xsk_update_xskmap(struct bpf_map *map, struct xsk_socket *xsk);
20862306a36Sopenharmony_civoid xsk_clear_xskmap(struct bpf_map *map);
20962306a36Sopenharmony_cibool xsk_is_in_mode(u32 ifindex, int mode);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_cistruct xsk_socket_config {
21262306a36Sopenharmony_ci	__u32 rx_size;
21362306a36Sopenharmony_ci	__u32 tx_size;
21462306a36Sopenharmony_ci	__u16 bind_flags;
21562306a36Sopenharmony_ci};
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci/* Set config to NULL to get the default configuration. */
21862306a36Sopenharmony_ciint xsk_umem__create(struct xsk_umem **umem,
21962306a36Sopenharmony_ci		     void *umem_area, __u64 size,
22062306a36Sopenharmony_ci		     struct xsk_ring_prod *fill,
22162306a36Sopenharmony_ci		     struct xsk_ring_cons *comp,
22262306a36Sopenharmony_ci		     const struct xsk_umem_config *config);
22362306a36Sopenharmony_ciint xsk_socket__create(struct xsk_socket **xsk,
22462306a36Sopenharmony_ci		       int ifindex, __u32 queue_id,
22562306a36Sopenharmony_ci		       struct xsk_umem *umem,
22662306a36Sopenharmony_ci		       struct xsk_ring_cons *rx,
22762306a36Sopenharmony_ci		       struct xsk_ring_prod *tx,
22862306a36Sopenharmony_ci		       const struct xsk_socket_config *config);
22962306a36Sopenharmony_ciint xsk_socket__create_shared(struct xsk_socket **xsk_ptr,
23062306a36Sopenharmony_ci			      int ifindex,
23162306a36Sopenharmony_ci			      __u32 queue_id, struct xsk_umem *umem,
23262306a36Sopenharmony_ci			      struct xsk_ring_cons *rx,
23362306a36Sopenharmony_ci			      struct xsk_ring_prod *tx,
23462306a36Sopenharmony_ci			      struct xsk_ring_prod *fill,
23562306a36Sopenharmony_ci			      struct xsk_ring_cons *comp,
23662306a36Sopenharmony_ci			      const struct xsk_socket_config *config);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci/* Returns 0 for success and -EBUSY if the umem is still in use. */
23962306a36Sopenharmony_ciint xsk_umem__delete(struct xsk_umem *umem);
24062306a36Sopenharmony_civoid xsk_socket__delete(struct xsk_socket *xsk);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ciint xsk_set_mtu(int ifindex, int mtu);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci#ifdef __cplusplus
24562306a36Sopenharmony_ci} /* extern "C" */
24662306a36Sopenharmony_ci#endif
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci#endif /* __XSK_H */
249