root/tools/testing/selftests/bpf/prog_tests/test_xsk.h
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef TEST_XSK_H_
#define TEST_XSK_H_

#include <linux/ethtool.h>
#include <linux/if_xdp.h>

#include "../kselftest.h"
#include "xsk.h"

#ifndef SO_PREFER_BUSY_POLL
#define SO_PREFER_BUSY_POLL 69
#endif

#ifndef SO_BUSY_POLL_BUDGET
#define SO_BUSY_POLL_BUDGET 70
#endif

#define TEST_PASS 0
#define TEST_FAILURE -1
#define TEST_CONTINUE 1
#define TEST_SKIP 2

#define DEFAULT_PKT_CNT                 (4 * 1024)
#define DEFAULT_UMEM_BUFFERS            (DEFAULT_PKT_CNT / 4)
#define HUGEPAGE_SIZE                   (2 * 1024 * 1024)
#define MIN_PKT_SIZE                    64
#define MAX_ETH_PKT_SIZE                1518
#define MAX_INTERFACE_NAME_CHARS        16
#define MAX_TEST_NAME_SIZE              48
#define SOCK_RECONF_CTR                 10
#define USLEEP_MAX                      10000

extern bool opt_verbose;
#define print_verbose(x...) do { if (opt_verbose) ksft_print_msg(x); } while (0)


static inline u32 ceil_u32(u32 a, u32 b)
{
        return (a + b - 1) / b;
}

static inline u64 ceil_u64(u64 a, u64 b)
{
        return (a + b - 1) / b;
}

/* Simple test */
enum test_mode {
        TEST_MODE_SKB,
        TEST_MODE_DRV,
        TEST_MODE_ZC,
        TEST_MODE_ALL
};

struct ifobject;
struct test_spec;
typedef int (*validation_func_t)(struct ifobject *ifobj);
typedef void *(*thread_func_t)(void *arg);
typedef int (*test_func_t)(struct test_spec *test);

struct xsk_socket_info {
        struct xsk_ring_cons rx;
        struct xsk_ring_prod tx;
        struct xsk_umem_info *umem;
        struct xsk_socket *xsk;
        struct pkt_stream *pkt_stream;
        u32 outstanding_tx;
        u32 rxqsize;
        u32 batch_size;
        u8 dst_mac[ETH_ALEN];
        u8 src_mac[ETH_ALEN];
        bool check_consumer;
};

int kick_rx(struct xsk_socket_info *xsk);
int kick_tx(struct xsk_socket_info *xsk);

struct xsk_umem_info {
        struct xsk_ring_prod fq;
        struct xsk_ring_cons cq;
        struct xsk_umem *umem;
        u64 next_buffer;
        u32 num_frames;
        u32 frame_headroom;
        void *buffer;
        u32 frame_size;
        u32 base_addr;
        u32 fill_size;
        u32 comp_size;
        bool unaligned_mode;
};

struct set_hw_ring {
        u32 default_tx;
        u32 default_rx;
};

int hw_ring_size_reset(struct ifobject *ifobj);

struct ifobject {
        char ifname[MAX_INTERFACE_NAME_CHARS];
        struct xsk_socket_info *xsk;
        struct xsk_socket_info *xsk_arr;
        struct xsk_umem_info *umem;
        thread_func_t func_ptr;
        validation_func_t validation_func;
        struct xsk_xdp_progs *xdp_progs;
        struct bpf_map *xskmap;
        struct bpf_program *xdp_prog;
        struct ethtool_ringparam ring;
        struct set_hw_ring set_ring;
        enum test_mode mode;
        int ifindex;
        int mtu;
        u32 bind_flags;
        u32 xdp_zc_max_segs;
        bool tx_on;
        bool rx_on;
        bool use_poll;
        bool busy_poll;
        bool use_fill_ring;
        bool release_rx;
        bool shared_umem;
        bool use_metadata;
        bool unaligned_supp;
        bool multi_buff_supp;
        bool multi_buff_zc_supp;
        bool hw_ring_size_supp;
};
struct ifobject *ifobject_create(void);
void ifobject_delete(struct ifobject *ifobj);
int init_iface(struct ifobject *ifobj, thread_func_t func_ptr);

int xsk_configure_umem(struct ifobject *ifobj, struct xsk_umem_info *umem, void *buffer, u64 size);
int xsk_configure_socket(struct xsk_socket_info *xsk, struct xsk_umem_info *umem,
                         struct ifobject *ifobject, bool shared);


struct pkt {
        int offset;
        u32 len;
        u32 pkt_nb;
        bool valid;
        u16 options;
};

struct pkt_stream {
        u32 nb_pkts;
        u32 current_pkt_nb;
        struct pkt *pkts;
        u32 max_pkt_len;
        u32 nb_rx_pkts;
        u32 nb_valid_entries;
        bool verbatim;
};

static inline bool pkt_continues(u32 options)
{
        return options & XDP_PKT_CONTD;
}

struct pkt_stream *pkt_stream_generate(u32 nb_pkts, u32 pkt_len);
void pkt_stream_delete(struct pkt_stream *pkt_stream);
void pkt_stream_reset(struct pkt_stream *pkt_stream);
void pkt_stream_restore_default(struct test_spec *test);

struct test_spec {
        struct ifobject *ifobj_tx;
        struct ifobject *ifobj_rx;
        struct pkt_stream *tx_pkt_stream_default;
        struct pkt_stream *rx_pkt_stream_default;
        struct bpf_program *xdp_prog_rx;
        struct bpf_program *xdp_prog_tx;
        struct bpf_map *xskmap_rx;
        struct bpf_map *xskmap_tx;
        test_func_t test_func;
        int mtu;
        u16 total_steps;
        u16 current_step;
        u16 nb_sockets;
        bool fail;
        bool set_ring;
        bool adjust_tail;
        bool adjust_tail_support;
        enum test_mode mode;
        char name[MAX_TEST_NAME_SIZE];
};

#define busy_poll_string(test) (test)->ifobj_tx->busy_poll ? "BUSY-POLL " : ""
static inline char *mode_string(struct test_spec *test)
{
        switch (test->mode) {
        case TEST_MODE_SKB:
                return "SKB";
        case TEST_MODE_DRV:
                return "DRV";
        case TEST_MODE_ZC:
                return "ZC";
        default:
                return "BOGUS";
        }
}

void test_init(struct test_spec *test, struct ifobject *ifobj_tx,
               struct ifobject *ifobj_rx, enum test_mode mode,
               const struct test_spec *test_to_run);

int testapp_adjust_tail_grow(struct test_spec *test);
int testapp_adjust_tail_grow_mb(struct test_spec *test);
int testapp_adjust_tail_shrink(struct test_spec *test);
int testapp_adjust_tail_shrink_mb(struct test_spec *test);
int testapp_aligned_inv_desc(struct test_spec *test);
int testapp_aligned_inv_desc_2k_frame(struct test_spec *test);
int testapp_aligned_inv_desc_mb(struct test_spec *test);
int testapp_bidirectional(struct test_spec *test);
int testapp_headroom(struct test_spec *test);
int testapp_hw_sw_max_ring_size(struct test_spec *test);
int testapp_hw_sw_min_ring_size(struct test_spec *test);
int testapp_poll_rx(struct test_spec *test);
int testapp_poll_rxq_tmout(struct test_spec *test);
int testapp_poll_tx(struct test_spec *test);
int testapp_poll_txq_tmout(struct test_spec *test);
int testapp_send_receive(struct test_spec *test);
int testapp_send_receive_2k_frame(struct test_spec *test);
int testapp_send_receive_mb(struct test_spec *test);
int testapp_send_receive_unaligned(struct test_spec *test);
int testapp_send_receive_unaligned_mb(struct test_spec *test);
int testapp_single_pkt(struct test_spec *test);
int testapp_stats_fill_empty(struct test_spec *test);
int testapp_stats_rx_dropped(struct test_spec *test);
int testapp_stats_tx_invalid_descs(struct test_spec *test);
int testapp_stats_rx_full(struct test_spec *test);
int testapp_teardown(struct test_spec *test);
int testapp_too_many_frags(struct test_spec *test);
int testapp_tx_queue_consumer(struct test_spec *test);
int testapp_unaligned_inv_desc(struct test_spec *test);
int testapp_unaligned_inv_desc_4001_frame(struct test_spec *test);
int testapp_unaligned_inv_desc_mb(struct test_spec *test);
int testapp_xdp_drop(struct test_spec *test);
int testapp_xdp_metadata(struct test_spec *test);
int testapp_xdp_metadata_mb(struct test_spec *test);
int testapp_xdp_prog_cleanup(struct test_spec *test);
int testapp_xdp_shared_umem(struct test_spec *test);

void *worker_testapp_validate_rx(void *arg);
void *worker_testapp_validate_tx(void *arg);

static const struct test_spec tests[] = {
        {.name = "SEND_RECEIVE", .test_func = testapp_send_receive},
        {.name = "SEND_RECEIVE_2K_FRAME", .test_func = testapp_send_receive_2k_frame},
        {.name = "SEND_RECEIVE_SINGLE_PKT", .test_func = testapp_single_pkt},
        {.name = "POLL_RX", .test_func = testapp_poll_rx},
        {.name = "POLL_TX", .test_func = testapp_poll_tx},
        {.name = "POLL_RXQ_FULL", .test_func = testapp_poll_rxq_tmout},
        {.name = "POLL_TXQ_FULL", .test_func = testapp_poll_txq_tmout},
        {.name = "ALIGNED_INV_DESC", .test_func = testapp_aligned_inv_desc},
        {.name = "ALIGNED_INV_DESC_2K_FRAME_SIZE", .test_func = testapp_aligned_inv_desc_2k_frame},
        {.name = "UMEM_HEADROOM", .test_func = testapp_headroom},
        {.name = "BIDIRECTIONAL", .test_func = testapp_bidirectional},
        {.name = "STAT_RX_DROPPED", .test_func = testapp_stats_rx_dropped},
        {.name = "STAT_TX_INVALID", .test_func = testapp_stats_tx_invalid_descs},
        {.name = "STAT_RX_FULL", .test_func = testapp_stats_rx_full},
        {.name = "STAT_FILL_EMPTY", .test_func = testapp_stats_fill_empty},
        {.name = "XDP_PROG_CLEANUP", .test_func = testapp_xdp_prog_cleanup},
        {.name = "XDP_DROP_HALF", .test_func = testapp_xdp_drop},
        {.name = "XDP_SHARED_UMEM", .test_func = testapp_xdp_shared_umem},
        {.name = "XDP_METADATA_COPY", .test_func = testapp_xdp_metadata},
        {.name = "XDP_METADATA_COPY_MULTI_BUFF", .test_func = testapp_xdp_metadata_mb},
        {.name = "ALIGNED_INV_DESC_MULTI_BUFF", .test_func = testapp_aligned_inv_desc_mb},
        {.name = "TOO_MANY_FRAGS", .test_func = testapp_too_many_frags},
        {.name = "XDP_ADJUST_TAIL_SHRINK", .test_func = testapp_adjust_tail_shrink},
        {.name = "TX_QUEUE_CONSUMER", .test_func = testapp_tx_queue_consumer},
        };

static const struct test_spec ci_skip_tests[] = {
        /* Flaky tests */
        {.name = "XDP_ADJUST_TAIL_SHRINK_MULTI_BUFF", .test_func = testapp_adjust_tail_shrink_mb},
        {.name = "XDP_ADJUST_TAIL_GROW", .test_func = testapp_adjust_tail_grow},
        {.name = "XDP_ADJUST_TAIL_GROW_MULTI_BUFF", .test_func = testapp_adjust_tail_grow_mb},
        {.name = "SEND_RECEIVE_9K_PACKETS", .test_func = testapp_send_receive_mb},
        /* Tests with huge page dependency */
        {.name = "SEND_RECEIVE_UNALIGNED", .test_func = testapp_send_receive_unaligned},
        {.name = "UNALIGNED_INV_DESC", .test_func = testapp_unaligned_inv_desc},
        {.name = "UNALIGNED_INV_DESC_4001_FRAME_SIZE",
         .test_func = testapp_unaligned_inv_desc_4001_frame},
        {.name = "SEND_RECEIVE_UNALIGNED_9K_PACKETS",
         .test_func = testapp_send_receive_unaligned_mb},
        {.name = "UNALIGNED_INV_DESC_MULTI_BUFF", .test_func = testapp_unaligned_inv_desc_mb},
        /* Test with HW ring size dependency */
        {.name = "HW_SW_MIN_RING_SIZE", .test_func = testapp_hw_sw_min_ring_size},
        {.name = "HW_SW_MAX_RING_SIZE", .test_func = testapp_hw_sw_max_ring_size},
        /* Too long test */
        {.name = "TEARDOWN", .test_func = testapp_teardown},
};


#endif                          /* TEST_XSK_H_ */